civicrm.php 51.1 KB
Newer Older
totten's avatar
totten committed
1
<?php
2 3 4
/*
Plugin Name: CiviCRM
Description: CiviCRM - Growing and Sustaining Relationships
Kurund Jalmi's avatar
Kurund Jalmi committed
5
Version: 4.7
6
Author: CiviCRM LLC
7 8
Author URI: https://civicrm.org/
Plugin URI: https://wiki.civicrm.org/confluence/display/CRMDOC/Installing+CiviCRM+for+WordPress
9 10 11 12 13 14
License: AGPL3
Text Domain: civicrm
Domain Path: /languages
*/


totten's avatar
totten committed
15 16
/*
 +--------------------------------------------------------------------+
17
 | CiviCRM version 5                                                  |
totten's avatar
totten committed
18
 +--------------------------------------------------------------------+
19
 | Copyright CiviCRM LLC (c) 2004-2019                                |
totten's avatar
totten committed
20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43
 +--------------------------------------------------------------------+
 | This file is a part of CiviCRM.                                    |
 |                                                                    |
 | CiviCRM is free software; you can copy, modify, and distribute it  |
 | under the terms of the GNU Affero General Public License           |
 | Version 3, 19 November 2007 and the CiviCRM Licensing Exception.   |
 |                                                                    |
 | CiviCRM is distributed in the hope that it will be useful, but     |
 | WITHOUT ANY WARRANTY; without even the implied warranty of         |
 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.               |
 | See the GNU Affero General Public License for more details.        |
 |                                                                    |
 | You should have received a copy of the GNU Affero General Public   |
 | License and the CiviCRM Licensing Exception along                  |
 | with this program; if not, contact CiviCRM LLC                     |
 | at info[AT]civicrm[DOT]org. If you have questions about the        |
 | GNU Affero General Public License or the licensing of CiviCRM,     |
 | see the CiviCRM license FAQ at http://civicrm.org/licensing        |
 +--------------------------------------------------------------------+
*/

/**
 *
 * @package CRM
44
 * @copyright CiviCRM LLC (c) 2004-2019
totten's avatar
totten committed
45 46 47
 *
 */

haystack's avatar
haystack committed
48

haystack's avatar
haystack committed
49 50 51 52
/*
--------------------------------------------------------------------------------
WordPress resources for developers
--------------------------------------------------------------------------------
colemanw's avatar
colemanw committed
53
Not that they're ever adhered to anywhere other than core, but people do their
haystack's avatar
haystack committed
54 55 56 57 58 59 60 61 62 63 64 65 66 67
best to comply...

WordPress core coding standards:
http://make.wordpress.org/core/handbook/coding-standards/php/

WordPress HTML standards:
http://make.wordpress.org/core/handbook/coding-standards/html/

WordPress JavaScript standards:
http://make.wordpress.org/core/handbook/coding-standards/javascript/
--------------------------------------------------------------------------------
*/


68
// This file must not accessed directly
69 70 71
if ( ! defined( 'ABSPATH' ) ) exit;


72
// Set version here: when it changes, will force JS to reload
Kurund Jalmi's avatar
Kurund Jalmi committed
73
define( 'CIVICRM_PLUGIN_VERSION', '4.7' );
haystack's avatar
haystack committed
74

75
// Store reference to this file
76 77 78 79
if (!defined('CIVICRM_PLUGIN_FILE')) {
  define( 'CIVICRM_PLUGIN_FILE', __FILE__ );
}

80
// Store URL to this plugin's directory
81 82 83 84
if (!defined( 'CIVICRM_PLUGIN_URL')) {
  define( 'CIVICRM_PLUGIN_URL', plugin_dir_url(CIVICRM_PLUGIN_FILE) );
}

85
// Store PATH to this plugin's directory
86 87 88 89
if (!defined( 'CIVICRM_PLUGIN_DIR')) {
  define( 'CIVICRM_PLUGIN_DIR', plugin_dir_path(CIVICRM_PLUGIN_FILE) );
}

90
/*
91 92 93 94 95
 * The constant CIVICRM_SETTINGS_PATH is also defined in civicrm.config.php and
 * may already have been defined there - e.g. by cron or external scripts.
 */
if ( !defined( 'CIVICRM_SETTINGS_PATH' ) ) {

96
  /*
97 98 99 100 101
   * Test where the settings file exists.
   *
   * If the settings file is found in the 4.6 and prior location, use that as
   * CIVICRM_SETTINGS_PATH, otherwise use the new location.
   */
102 103 104 105
  $upload_dir    = wp_upload_dir();
  $wp_civi_settings = $upload_dir['basedir'] . DIRECTORY_SEPARATOR . 'civicrm' . DIRECTORY_SEPARATOR . 'civicrm.settings.php' ;
  $wp_civi_settings_deprectated = CIVICRM_PLUGIN_DIR . 'civicrm.settings.php';

106 107 108 109 110 111 112
  if (file_exists($wp_civi_settings_deprectated)) {
    define( 'CIVICRM_SETTINGS_PATH', $wp_civi_settings_deprectated );
  }
  else  {
    define( 'CIVICRM_SETTINGS_PATH', $wp_civi_settings );
  }

113
}
114

115
// Test if CiviCRM is installed
116 117 118 119
if ( file_exists( CIVICRM_SETTINGS_PATH )  ) {
    define( 'CIVICRM_INSTALLED', TRUE );
  } else {
    define( 'CIVICRM_INSTALLED', FALSE );
120 121
}

122
// Prevent CiviCRM from rendering its own header
yashodha's avatar
yashodha committed
123
define( 'CIVICRM_UF_HEAD', TRUE );
totten's avatar
totten committed
124 125


haystack's avatar
haystack committed
126
/**
127 128 129
 * Define CiviCRM_For_WordPress Class.
 *
 * @since 4.4
haystack's avatar
haystack committed
130
 */
haystack's avatar
haystack committed
131 132
class CiviCRM_For_WordPress {

colemanw's avatar
colemanw committed
133
  /**
134 135 136 137 138
   * Plugin instance.
   *
   * @since 4.4
   * @access private
   * @var object $instance The plugin instance.
colemanw's avatar
colemanw committed
139
   */
140
  private static $instance;
haystack's avatar
haystack committed
141

142
  /**
143 144 145 146 147
   * Plugin context (broad).
   *
   * @since 4.4
   * @access public
   * @var bool $in_wordpress The broad plugin context.
148
   */
colemanw's avatar
colemanw committed
149
  static $in_wordpress;
haystack's avatar
haystack committed
150

151 152 153 154 155 156 157
  /**
   * Plugin context (specific).
   *
   * @since 4.4
   * @access public
   * @var str $context The specific plugin context.
   */
158 159
  static $context;

160
  /**
161 162 163 164 165
   * Shortcodes management object.
   *
   * @since 4.4
   * @access public
   * @var object CiviCRM_For_WordPress_Shortcodes The shortcodes management object.
166
   */
167 168
  public $shortcodes;

169
  /**
170 171 172 173 174
   * Modal dialog management object.
   *
   * @since 4.4
   * @access public
   * @var object CiviCRM_For_WordPress_Shortcodes_Modal The modal dialog management object.
175
   */
176 177
  public $modal;

178
  /**
179 180 181 182 183
   * Basepage management object.
   *
   * @since 4.4
   * @access public
   * @var object CiviCRM_For_WordPress_Basepage The basepage management object.
184
   */
185 186
  public $basepage;

187
  /**
188 189 190 191 192
   * User management object.
   *
   * @since 4.4
   * @access public
   * @var object CiviCRM_For_WordPress_Users The user management object.
193
   */
194 195
  public $users;

haystack's avatar
haystack committed
196

197 198 199 200 201
  // ---------------------------------------------------------------------------
  // Setup
  // ---------------------------------------------------------------------------


colemanw's avatar
colemanw committed
202
  /**
203
   * Getter method which returns the CiviCRM instance and optionally creates one
haystack's avatar
haystack committed
204 205
   * if it does not already exist. Standard CiviCRM singleton pattern.
   *
206 207 208
   * @since 4.4
   *
   * @return object CiviCRM_For_WordPress The CiviCRM plugin instance.
colemanw's avatar
colemanw committed
209
   */
210
  public static function singleton() {
colemanw's avatar
colemanw committed
211

212
    // If instance doesn't already exist
colemanw's avatar
colemanw committed
213
    if ( ! isset( self::$instance ) ) {
214 215

      // Create instance
colemanw's avatar
colemanw committed
216
      self::$instance = new CiviCRM_For_WordPress;
217 218 219 220

      // Delay setup until 'plugins_loaded' to allow other plugins to load as well
      add_action( 'plugins_loaded', array( self::$instance, 'setup_instance' ) );

colemanw's avatar
colemanw committed
221 222
    }

223
    // Return instance
colemanw's avatar
colemanw committed
224 225 226 227 228 229
    return self::$instance;

  }


  /**
230 231 232
   * Dummy instance constructor.
   *
   * @since 4.4
colemanw's avatar
colemanw committed
233 234 235 236
   */
  function __construct() {}

  /**
237 238 239
   * Dummy magic method to prevent CiviCRM_For_WordPress from being cloned.
   *
   * @since 4.4
colemanw's avatar
colemanw committed
240 241
   */
  public function __clone() {
242
    _doing_it_wrong( __FUNCTION__, __( 'Only one instance of CiviCRM_For_WordPress please', 'civicrm' ), '4.4' );
colemanw's avatar
colemanw committed
243 244 245
  }

  /**
246 247 248
   * Dummy magic method to prevent CiviCRM_For_WordPress from being unserialized.
   *
   * @since 4.4
colemanw's avatar
colemanw committed
249 250
   */
  public function __wakeup() {
251
    _doing_it_wrong( __FUNCTION__, __( 'Please do not serialize CiviCRM_For_WordPress', 'civicrm' ), '4.4' );
colemanw's avatar
colemanw committed
252 253 254 255
  }


  /**
256 257 258 259 260
   * Plugin activation.
   *
   * This method is called only when CiviCRM plugin is activated. In order for
   * other plugins to be able to interact with Civi's activation, we wait until
   * after the activation redirect to perform activation actions.
haystack's avatar
haystack committed
261
   *
262
   * @since 4.4
colemanw's avatar
colemanw committed
263 264
   */
  public function activate() {
265

266
    // Set a one-time-only option
267 268 269
    add_option( 'civicrm_activation_in_progress', 'true' );

  }
colemanw's avatar
colemanw committed
270

271 272

  /**
273
   * Run CiviCRM's plugin activation procedure.
274
   *
275
   * @since 4.4
276 277
   */
  public function activation() {
278

279 280 281
    // Bail if not activating
    if ( get_option( 'civicrm_activation_in_progress' ) !== 'true' ) {
      return;
282
    }
colemanw's avatar
colemanw committed
283

284 285 286 287
    // Bail if not in WordPress admin
    if ( !is_admin() ) {
      return;
    }
288

289 290 291 292 293 294
    /**
     * Broadcast that activation actions need to happen now.
     *
     * @since 5.6
     */
    do_action( 'civicrm_activation' );
295

296 297
    // Change option so this action never fires again
    update_option( 'civicrm_activation_in_progress', 'false' );
298

colemanw's avatar
colemanw committed
299 300 301
  }


302
  /**
303 304 305 306 307
   * Plugin deactivation.
   *
   * This method is called only when CiviCRM plugin is deactivated. In order for
   * other plugins to be able to interact with Civi's activation, we need to
   * remove any options that are set in activate() above.
308
   *
309
   * @since 4.4
310 311
   */
  public function deactivate() {
312

313
    // Delete any options we hay have set
314
    delete_option( 'civicrm_activation_in_progress' );
315 316 317 318 319 320 321

    /**
     * Broadcast that deactivation actions need to happen now.
     *
     * @since 5.6
     */
    do_action( 'civicrm_deactivation' );
322 323 324 325

  }


colemanw's avatar
colemanw committed
326
  /**
327
   * Set up the CiviCRM plugin instance.
haystack's avatar
haystack committed
328
   *
329
   * @since 4.4
colemanw's avatar
colemanw committed
330 331 332
   */
  public function setup_instance() {

333
    // Kick out if another instance is being inited
334
    if ( isset( self::$in_wordpress ) ) {
335
      wp_die( __( 'Only one instance of CiviCRM_For_WordPress please', 'civicrm' ) );
colemanw's avatar
colemanw committed
336
    }
337

338 339 340
    // Get existing session ID
    $session_id = session_id();

341 342 343 344
    /*
     * There is no session handling in WP - hence we start it for CiviCRM pages
     * except when running via WP-CLI which does not require sessions.
     */
345
    if ( empty( $session_id ) && ! ( defined( 'WP_CLI' ) && WP_CLI ) ) {
colemanw's avatar
colemanw committed
346 347 348
      session_start();
    }

349
    // Get classes and instantiate
350
    $this->include_files();
351

352
    // Do plugin activation
353
    $this->activation();
354

355 356 357 358 359
    // Use translation files
    $this->enable_translation();

    // Register all hooks on init
    add_action( 'init', array( $this, 'register_hooks' ) );
colemanw's avatar
colemanw committed
360

361 362 363 364 365
    /**
     * Broadcast that this plugin is now loaded.
     *
     * @since 4.4
     */
colemanw's avatar
colemanw committed
366 367 368 369 370 371
    do_action( 'civicrm_instance_loaded' );

  }


  /**
372 373
   * Set broad CiviCRM context.
   *
374 375
   * Setter for determining if CiviCRM is currently being displayed in WordPress.
   * This becomes true whe CiviCRM is called in the following contexts:
376 377 378 379 380
   *
   * (a) in the WordPress back-end
   * (b) when CiviCRM content is being displayed on the front-end via wpBasePage
   * (c) when an AJAX request is made to CiviCRM
   *
381
   * It is NOT true when CiviCRM is called via a shortcode.
haystack's avatar
haystack committed
382
   *
383
   * @since 4.4
384 385 386
   */
  public function civicrm_in_wordpress_set() {

haystack's avatar
haystack committed
387 388 389
    // Get identifying query var
    $page = get_query_var( 'page' );

390
    // Store
haystack's avatar
haystack committed
391
    self::$in_wordpress = ( $page == 'CiviCRM' ) ? TRUE : FALSE;
392 393 394 395 396 397 398 399 400

  }


  /**
   * Getter for testing if CiviCRM is currently being displayed in WordPress.
   *
   * @see $this->civicrm_in_wordpress_set()
   *
401 402 403
   * @since 4.4
   *
   * @return bool $in_wordpress True if CiviCRM is displayed in WordPress, false otherwise.
colemanw's avatar
colemanw committed
404 405 406
   */
  public function civicrm_in_wordpress() {

407 408 409 410 411 412 413 414
    /**
     * Allow broad context to be filtered.
     *
     * @since 4.4
     *
     * @param bool $in_wordpress True if CiviCRM is displayed in WordPress, false otherwise.
     * @return bool $in_wordpress True if CiviCRM is displayed in WordPress, false otherwise.
     */
415
    return apply_filters( 'civicrm_in_wordpress', self::$in_wordpress );
colemanw's avatar
colemanw committed
416 417 418 419

  }


420
  /**
421 422
   * Set specific CiviCRM context.
   *
423 424 425 426 427 428 429 430
   * Setter for determining how CiviCRM is currently being displayed in WordPress.
   * This can be one of the following contexts:
   *
   * (a) in the WordPress back-end
   * (b) when CiviCRM content is being displayed on the front-end via wpBasePage
   * (c) when a "non-page" request is made to CiviCRM
   * (d) when CiviCRM is called via a shortcode
   *
431
   * The following codes correspond to the different contexts:
432 433 434 435 436 437
   *
   * (a) 'admin'
   * (b) 'basepage'
   * (c) 'nonpage'
   * (d) 'shortcode'
   *
438 439 440
   * @since 4.4
   *
   * @param string $context One of the four context codes above.
441 442 443
   */
  public function civicrm_context_set( $context ) {

444
    // Store
445 446 447 448 449 450
    self::$context = $context;

  }


  /**
451 452
   * Get specific CiviCRM context.
   *
453 454 455 456
   * Getter for determining how CiviCRM is currently being displayed in WordPress.
   *
   * @see $this->civicrm_context_set()
   *
457 458 459
   * @since 4.4
   *
   * @return string The context in which CiviCRM is displayed in WordPress.
460 461 462
   */
  public function civicrm_context_get() {

463 464 465 466 467 468 469 470
    /**
     * Allow specific context to be filtered.
     *
     * @since 4.4
     *
     * @param bool $context The existing context in which CiviCRM is displayed in WordPress.
     * @return bool $context The modified context in which CiviCRM is displayed in WordPress.
     */
471 472 473 474 475
    return apply_filters( 'civicrm_context', self::$context );

  }


476 477 478 479 480 481
  // ---------------------------------------------------------------------------
  // Files
  // ---------------------------------------------------------------------------


  /**
482
   * Include files.
483
   *
484
   * @since 4.4
485 486
   */
  public function include_files() {
487

488
    // Include users class
489 490
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.users.php';
    $this->users = new CiviCRM_For_WordPress_Users;
491

492
    // Include shortcodes class
493 494
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.shortcodes.php';
    $this->shortcodes = new CiviCRM_For_WordPress_Shortcodes;
495

496
    // Include shortcodes modal dialog class
497 498
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.shortcodes.modal.php';
    $this->modal = new CiviCRM_For_WordPress_Shortcodes_Modal;
499

500
    // Include basepage class
501 502
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.basepage.php';
    $this->basepage = new CiviCRM_For_WordPress_Basepage;
503

504 505 506
  }


507 508 509 510 511
  // ---------------------------------------------------------------------------
  // Hooks
  // ---------------------------------------------------------------------------


colemanw's avatar
colemanw committed
512
  /**
513
   * Register hooks on init.
haystack's avatar
haystack committed
514
   *
515
   * @since 4.4
colemanw's avatar
colemanw committed
516 517 518
   */
  public function register_hooks() {

519
    // Always add the common hooks
520
    $this->register_hooks_common();
521

522
    // When in WordPress admin...
523 524
    if ( is_admin() ) {

525
      // Set context
526
      $this->civicrm_context_set( 'admin' );
527

528
      // Handle WP admin context
529
      $this->register_hooks_admin();
530
      return;
531

532 533
    }

534
    // Go no further if CiviCRM not installed yet
535 536
    if ( ! CIVICRM_INSTALLED ) return;

537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559
    // Delay everything else until query has been parsed
    add_action( 'parse_query', array( $this, 'register_hooks_front_end' ) );

  }


  /**
   * Register hooks for the front end.
   *
   * @since 5.6
   */
  public function register_hooks_front_end() {

    // Prevent multiple calls
    static $alreadyRegistered = FALSE;
    if ( $alreadyRegistered ) {
      return;
    }
    $alreadyRegistered = TRUE;

    // Store context
    $this->civicrm_in_wordpress_set();

560
    // When embedded via wpBasePage or AJAX call...
561
    if ( $this->civicrm_in_wordpress() ) {
562

563
      /*
564 565 566 567 568 569 570 571
       * Directly output CiviCRM html only in a few cases and skip WP templating:
       *
       * (a) when a snippet is set
       * (b) when there is an AJAX call
       * (c) for an iCal feed (unless 'html' is specified)
       * (d) for file download URLs
       */
      if ( ! $this->is_page_request() ) {
572

573
        // Set context
574
        $this->civicrm_context_set( 'nonpage' );
575

576
        // Add core resources for front end
577
        add_action( 'wp', array( $this, 'front_end_page_load' ) );
578

579
        // Echo all output when WP has been set up but nothing has been rendered
580 581
        add_action( 'wp', array( $this, 'invoke' ) );
        return;
582

583
      }
584

585
      // Set context
586
      $this->civicrm_context_set( 'basepage' );
587

588
      // If we get here, we must be in a wpBasePage context
589
      $this->basepage->register_hooks();
590
      return;
591

592
    }
593

594
    // Set context
595
    $this->civicrm_context_set( 'shortcode' );
596

597
    // That leaves us with handling shortcodes, should they exist
598
    $this->shortcodes->register_hooks();
599 600 601

  }

colemanw's avatar
colemanw committed
602

603
  /**
604
   * Register hooks that must always be present.
605
   *
606
   * @since 4.4
607
   */
608
  public function register_hooks_common() {
609

610
    // Register user hooks.
611
    $this->users->register_hooks();
colemanw's avatar
colemanw committed
612

613 614
    // Register hooks for clean URLs.
    $this->register_hooks_clean_urls();
haystack's avatar
haystack committed
615

616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634
  }


  /**
   * Register hooks to handle Clean URLs.
   *
   * @since 5.12
   */
  public function register_hooks_clean_urls() {

    // Bail if CiviCRM is not installed.
    if (!CIVICRM_INSTALLED) {
      return;
    }

    // Bail if we can't initialize CiviCRM.
    if (!$this->initialize()) {
      return;
    }
haystack's avatar
haystack committed
635

636 637 638 639
    // Bail if CiviCRM is not using clean URLs.
    if (!defined('CIVICRM_CLEANURL') || CIVICRM_CLEANURL !== 1) {
      return;
    }
haystack's avatar
haystack committed
640

641 642
    // Have we flushed rewrite rules?
    if ( get_option( 'civicrm_rules_flushed' ) !== 'true' ) {
haystack's avatar
haystack committed
643

644 645
      // Apply custom rewrite rules, then flush rules afterwards.
      $this->rewrite_rules( true );
haystack's avatar
haystack committed
646

647 648
      // Set a one-time-only option to flag that this has been done.
      add_option( 'civicrm_rules_flushed', 'true' );
649

650
    } else {
haystack's avatar
haystack committed
651

652 653
      // Apply custom rewrite rules normally.
      $this->rewrite_rules();
haystack's avatar
haystack committed
654

655
    }
haystack's avatar
haystack committed
656

657 658 659
    // Add our query vars.
    add_filter( 'query_vars', array( $this, 'query_vars' ) );

660
  }
661 662


663
  /**
664
   * Register hooks to handle CiviCRM in a WordPress admin context.
665
   *
666
   * @since 4.4
667
   */
668
  public function register_hooks_admin() {
669

670
    // Modify the admin menu
671
    add_action( 'admin_menu', array( $this, 'add_menu_items' ) );
672

673
    // Set page title
674 675
    add_filter( 'admin_title', array( $this, 'set_admin_title' ) );

676
    // Print CiviCRM's header
677 678
    add_action('admin_head', array( $this, 'wp_head' ), 50);

679
    // If settings file does not exist, show notice with link to installer
680
    if ( ! CIVICRM_INSTALLED ) {
681
      if ( isset( $_GET['page'] ) && $_GET['page'] == 'civicrm-install' ) {
682 683
        // Set install type
        $_GET['civicrm_install_type'] = 'wordpress';
684
      } else {
685
        // Show notice
686
        add_action( 'admin_notices', array( $this, 'show_setup_warning' ) );
colemanw's avatar
colemanw committed
687
      }
688
    }
689

690
    // Enable shortcode modal
691 692
    $this->modal->register_hooks();

693
  }
colemanw's avatar
colemanw committed
694 695


haystack's avatar
haystack committed
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722
  /**
   * Add our rewrite rules.
   *
   * @since 5.7
   *
   * @param bool $flush_rewrite_rules True if rules should be flushed, false otherwise.
   */
  public function rewrite_rules( $flush_rewrite_rules = false ) {

    // Kick out if not CiviCRM
    if (!$this->initialize()) {
      return;
    }

    // Get config
    $config = CRM_Core_Config::singleton();

    // Get basepage object
    $basepage = get_page_by_path( $config->wpBasePage );

    // Sanity check
    if (!is_object($basepage)) {
        return;
    }

    // Let's add rewrite rule when viewing the basepage
    add_rewrite_rule(
haystack's avatar
haystack committed
723 724
      '^' . $config->wpBasePage . '/([^?]*)?',
      'index.php?page_id=' . $basepage->ID . '&page=CiviCRM&q=civicrm%2F$matches[1]',
haystack's avatar
haystack committed
725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789
      'top'
    );

    // Maybe force flush
    if ($flush_rewrite_rules) {
      flush_rewrite_rules();
    }

    /**
     * Broadcast the rewrite rules event.
     *
     * @since 5.7
     *
     * @param bool $flush_rewrite_rules True if rules flushed, false otherwise.
     */
    do_action( 'civicrm_after_rewrite_rules', $flush_rewrite_rules );

  }


  /**
   * Add our query vars.
   *
   * @since 5.7
   *
   * @param array $query_vars The existing query vars.
   * @return array $query_vars The modified query vars.
   */
  public function query_vars( $query_vars ) {

    // Sanity check
    if (!is_array($query_vars)) {
      $query_vars = array();
    }

    // Build our query vars
    $civicrm_query_vars = array(
      'page', 'q', 'reset', 'id', 'html', 'snippet', // URL query vars
      'action', 'mode', 'cid', 'gid', 'sid', 'cs', 'force', // Shortcode query vars
    );

    /**
     * Filter the default CiviCRM query vars.
     *
     * Use in combination with `civicrm_query_vars_assigned` action to ensure
     * that any other query vars are included in the assignment to the
     * super-global arrays.
     *
     * @since 5.7
     *
     * @param array $civicrm_query_vars The default set of query vars.
     * @return array $civicrm_query_vars The modified set of query vars.
     */
    $civicrm_query_vars = apply_filters( 'civicrm_query_vars', $civicrm_query_vars );

    // Now add them to WordPress
    foreach( $civicrm_query_vars as $civicrm_query_var ) {
      $query_vars[] = $civicrm_query_var;
    }

    return $query_vars;

  }


790 791 792 793 794
  // ---------------------------------------------------------------------------
  // CiviCRM Initialisation
  // ---------------------------------------------------------------------------


colemanw's avatar
colemanw committed
795
  /**
796 797 798
   * Initialize CiviCRM.
   *
   * @since 4.4
haystack's avatar
haystack committed
799
   *
800
   * @return bool $success True if CiviCRM is initialized, false otherwise.
colemanw's avatar
colemanw committed
801 802 803
   */
  public function initialize() {

yashodha's avatar
yashodha committed
804 805
    static $initialized = FALSE;
    static $failure = FALSE;
colemanw's avatar
colemanw committed
806 807

    if ( $failure ) {
yashodha's avatar
yashodha committed
808
      return FALSE;
colemanw's avatar
colemanw committed
809 810 811 812 813
    }

    if ( ! $initialized ) {

      // Check for php version and ensure its greater than minPhpVersion
totten's avatar
totten committed
814
      $minPhpVersion = '5.3.4';
colemanw's avatar
colemanw committed
815 816 817
      if ( version_compare( PHP_VERSION, $minPhpVersion ) < 0 ) {
        echo '<p>' .
           sprintf(
818
            __( 'CiviCRM requires PHP Version %s or greater. You are running PHP Version %s', 'civicrm' ),
colemanw's avatar
colemanw committed
819 820 821 822 823 824 825
            $minPhpVersion,
            PHP_VERSION
           ) .
           '<p>';
        exit();
      }

826
      // Check for settings
827
      if ( ! CIVICRM_INSTALLED ) {
yashodha's avatar
yashodha committed
828
        $error = FALSE;
829
      } elseif ( file_exists( CIVICRM_SETTINGS_PATH) ) {
colemanw's avatar
colemanw committed
830 831 832
        $error = include_once ( CIVICRM_SETTINGS_PATH );
      }

833
      // Autoload
colemanw's avatar
colemanw committed
834 835 836
      require_once 'CRM/Core/ClassLoader.php';
      CRM_Core_ClassLoader::singleton()->register();

837
      // Get ready for problems
colemanw's avatar
colemanw committed
838
      $installLink    = admin_url() . "options-general.php?page=civicrm-install";
839 840 841
      $docLinkInstall = "https://wiki.civicrm.org/confluence/display/CRMDOC/Installing+CiviCRM+for+WordPress";
      $docLinkTrouble = "https://wiki.civicrm.org/confluence/display/CRMDOC/Installation+and+Upgrades";
      $forumLink      = "https://civicrm.stackexchange.com/";
colemanw's avatar
colemanw committed
842 843


844
      // Construct message
colemanw's avatar
colemanw committed
845
      $errorMsgAdd = sprintf(
846
        __( 'Please review the <a href="%s">WordPress Installation Guide</a> and the <a href="%s">Trouble-shooting page</a> for assistance. If you still need help installing, you can often find solutions to your issue by searching for the error message in the <a href="%s">installation support section of the community forum</a>.', 'civicrm' ),
colemanw's avatar
colemanw committed
847 848 849 850 851
        $docLinkInstall,
        $docLinkTrouble,
        $forumLink
      );

852
      // Does install message get used?
colemanw's avatar
colemanw committed
853
      $installMessage = sprintf(
854
        __( 'Click <a href="%s">here</a> for fresh install.', 'civicrm' ),
colemanw's avatar
colemanw committed
855 856 857
        $installLink
      );

yashodha's avatar
yashodha committed
858
      if ($error == FALSE) {
colemanw's avatar
colemanw committed
859
        header( 'Location: ' . admin_url() . 'options-general.php?page=civicrm-install' );
yashodha's avatar
yashodha committed
860
        return FALSE;
colemanw's avatar
colemanw committed
861
      }
862

863
      // Access global defined in civicrm.settings.php
haystack's avatar
haystack committed
864
      global $civicrm_root;
865

866
      // This does pretty much all of the civicrm initialization
colemanw's avatar
colemanw committed
867
      if ( ! file_exists( $civicrm_root . 'CRM/Core/Config.php' ) ) {
yashodha's avatar
yashodha committed
868
        $error = FALSE;
colemanw's avatar
colemanw committed
869 870 871 872
      } else {
        $error = include_once ( 'CRM/Core/Config.php' );
      }

873
      // Have we got it?
yashodha's avatar
yashodha committed
874
      if ( $error == FALSE ) {
colemanw's avatar
colemanw committed
875

876
        // Set static flag
yashodha's avatar
yashodha committed
877
        $failure = TRUE;
colemanw's avatar
colemanw committed
878 879 880 881 882

        // FIX ME - why?
        wp_die(
          "<strong><p class='error'>" .
          sprintf(
883
            __( 'Oops! - The path for including CiviCRM code files is not set properly. Most likely there is an error in the <em>civicrm_root</em> setting in your CiviCRM settings file (%s).', 'civicrm' ),
colemanw's avatar
colemanw committed
884 885 886 887
            CIVICRM_SETTINGS_PATH
          ) .
          "</p><p class='error'> &raquo; " .
          sprintf(
888
            __( 'civicrm_root is currently set to: <em>%s</em>.', 'civicrm' ),
colemanw's avatar
colemanw committed
889 890 891 892 893
            $civicrm_root
          ) .
          "</p><p class='error'>" . $errorMsgAdd . "</p></strong>"
        );

894
        // Won't reach here!
yashodha's avatar
yashodha committed
895
        return FALSE;
colemanw's avatar
colemanw committed
896 897 898

      }

899
      // Set static flag
yashodha's avatar
yashodha committed
900
      $initialized = TRUE;
colemanw's avatar
colemanw committed
901

902
      // Initialize the system by creating a config object
colemanw's avatar
colemanw committed
903 904
      $config = CRM_Core_Config::singleton();

905
      // Sync the logged in user with WP
colemanw's avatar
colemanw committed
906 907 908
      global $current_user;
      if ( $current_user ) {

909
        // Sync procedure sets session values for logged in users
colemanw's avatar
colemanw committed
910 911
        require_once 'CRM/Core/BAO/UFMatch.php';
        CRM_Core_BAO_UFMatch::synchronize(
912 913
          $current_user, // User object
          FALSE, // Do not update
colemanw's avatar
colemanw committed
914
          'WordPress', // CMS
915
          $this->users->get_civicrm_contact_type('Individual')
colemanw's avatar
colemanw committed
916 917 918 919 920 921
        );

      }

    }

922 923 924 925 926
    /**
     * Broadcast that CiviCRM is now initialized.
     *
     * @since 4.4
     */
colemanw's avatar
colemanw committed
927 928
    do_action( 'civicrm_initialized' );

929
    // Success!
yashodha's avatar
yashodha committed
930
    return TRUE;
colemanw's avatar
colemanw committed
931 932

  }
haystack's avatar
haystack committed
933 934


935 936 937 938 939
  // ---------------------------------------------------------------------------
  // Plugin setup
  // ---------------------------------------------------------------------------


colemanw's avatar
colemanw committed
940
  /**
941 942
   * Load translation files.
   *
colemanw's avatar
colemanw committed
943 944
   * A good reference on how to implement translation in WordPress:
   * http://ottopress.com/2012/internationalization-youre-probably-doing-it-wrong/
haystack's avatar
haystack committed
945
   *
946
   * @since 4.4
colemanw's avatar
colemanw committed
947 948 949
   */
  public function enable_translation() {

950
    // Load translations
colemanw's avatar
colemanw committed
951
    load_plugin_textdomain(
952 953 954
      'civicrm', // Unique name
      FALSE, // Deprecated argument
      dirname( plugin_basename( __FILE__ ) ) . '/languages/' // Relative path to translation files
colemanw's avatar
colemanw committed
955 956 957 958
    );

  }

haystack's avatar
haystack committed
959

colemanw's avatar
colemanw committed
960
  /**
961 962 963
   * Adds menu items to WordPress admin menu.
   *
   * Callback method for 'admin_menu' hook as set in register_hooks().
haystack's avatar
haystack committed
964
   *
965
   * @since 4.4
colemanw's avatar
colemanw committed
966 967 968
   */
  public function add_menu_items() {

969
    $civilogo = file_get_contents( plugin_dir_path( __FILE__ ) . 'assets/civilogo.svg.b64' );
970

971 972 973 974 975 976 977 978 979 980 981 982
    /**
     * Filter the position of the CiviCRM menu item.
     *
     * Currently set to 3.9 + some random digits to reduce risk of conflict.
     *
     * @since 4.4
     *
     * @param float The default menu position.
     * @return float The modified menu position..
     */
    $position = apply_filters( 'civicrm_menu_item_position', '3.904981' );

983
    // Check for settings file
984
    if ( CIVICRM_INSTALLED ) {
colemanw's avatar
colemanw committed
985

986
      // Add top level menu item
987
      $menu_page = add_menu_page(
988 989
        __( 'CiviCRM', 'civicrm' ),
        __( 'CiviCRM', 'civicrm' ),
colemanw's avatar
colemanw committed
990 991 992
        'access_civicrm',
        'CiviCRM',
        array( $this, 'invoke' ),
993
        $civilogo,
994
        $position
colemanw's avatar
colemanw committed
995
      );
996

997
      // Add core resources prior to page load
998
      add_action( 'load-' . $menu_page, array( $this, 'admin_page_load' ) );
999

colemanw's avatar
colemanw committed
1000 1001
    } else {

1002
      // Add top level menu item
1003
      $menu_page = add_menu_page(
1004 1005
        __( 'CiviCRM Installer', 'civicrm' ),
        __( 'CiviCRM Installer', 'civicrm' ),
colemanw's avatar
colemanw committed
1006 1007
        'manage_options',
        'civicrm-install',
1008 1009
        array( $this, 'run_installer' ),
        $civilogo,
1010
        $position
colemanw's avatar
colemanw committed
1011 1012
      );

1013
      /*
1014
      // Add scripts and styles like this
1015 1016 1017
      add_action( 'admin_print_scripts-' . $menu_page, array( $this, 'admin_installer_js' ) );
      add_action( 'admin_print_styles-' . $menu_page, array( $this, 'admin_installer_css' ) );
      add_action( 'admin_head-' . $menu_page, array( $this, 'admin_installer_head' ), 50 );
1018
      */
1019

colemanw's avatar
colemanw committed
1020 1021 1022 1023 1024
    }

  }


1025 1026 1027 1028 1029
  // ---------------------------------------------------------------------------
  // Installation
  // ---------------------------------------------------------------------------


colemanw's avatar
colemanw committed
1030
  /**
1031
   * Callback method for add_options_page() that runs the CiviCRM installer.
haystack's avatar
haystack committed
1032
   *
1033
   * @since 4.4
colemanw's avatar
colemanw committed
1034 1035
   */
  public function run_installer() {
1036 1037 1038 1039 1040 1041 1042 1043 1044
    $civicrmCore = CIVICRM_PLUGIN_DIR . 'civicrm';

    $setupPaths = array(
      implode(DIRECTORY_SEPARATOR, ['vendor', 'civicrm', 'civicrm-setup']),
      implode(DIRECTORY_SEPARATOR, ['packages', 'civicrm-setup',]),
      implode(DIRECTORY_SEPARATOR, ['setup']),
    );
    foreach ($setupPaths as $setupPath) {
      $loader = implode(DIRECTORY_SEPARATOR, [$civicrmCore, $setupPath, 'civicrm-setup-autoload.php']);
1045
      if (file_exists($civicrmCore . DIRECTORY_SEPARATOR . '.use-civicrm-setup') && file_exists($loader)) {
1046 1047 1048
        require_once $loader;
        require_once implode(DIRECTORY_SEPARATOR, [$civicrmCore, 'CRM', 'Core', 'ClassLoader.php']);
        CRM_Core_ClassLoader::singleton()->register();
1049
        \Civi\Setup::assertProtocolCompatibility(1.0);
1050 1051 1052 1053 1054 1055 1056 1057 1058 1059 1060 1061 1062 1063 1064 1065
        \Civi\Setup::init([
          'cms' => 'WordPress',
          'srcPath' => $civicrmCore,
        ]);
        $ctrl = \Civi\Setup::instance()->createController()->getCtrl();
        $ctrl->setUrls(array(
          'ctrl' => admin_url() . "options-general.php?page=civicrm-install",
          'res' => CIVICRM_PLUGIN_URL . 'civicrm/' . strtr($setupPath, DIRECTORY_SEPARATOR, '/') . '/res/',
          'jquery.js' => CIVICRM_PLUGIN_URL . 'civicrm/bower_components/jquery/dist/jquery.min.js',
          'font-awesome.css' => CIVICRM_PLUGIN_URL . 'civicrm/bower_components/font-awesome/css/font-awesome.min.css',
          'finished' => admin_url('admin.php?page=CiviCRM&q=civicrm&reset=1'),
        ));
        \Civi\Setup\BasicRunner::run($ctrl);
        return;
      }
    }
colemanw's avatar
colemanw committed
1066

1067
    // Uses CIVICRM_PLUGIN_DIR instead of WP_PLUGIN_DIR
colemanw's avatar
colemanw committed
1068 1069 1070 1071 1072 1073 1074
    $installFile =
      CIVICRM_PLUGIN_DIR .
      'civicrm' . DIRECTORY_SEPARATOR .
      'install' . DIRECTORY_SEPARATOR .
      'index.php';

    // Notice: Undefined variable: siteDir in:
1075
    // CIVICRM_PLUGIN_DIR/civicrm/install/index.php on line 456
colemanw's avatar
colemanw committed
1076 1077 1078 1079 1080 1081
    include ( $installFile );

  }


  /**
1082
   * Callback method for missing settings file in register_hooks().
haystack's avatar
haystack committed
1083
   *
1084
   * @since 4.4
colemanw's avatar
colemanw committed
1085 1086 1087 1088 1089 1090
   */
  public function show_setup_warning() {

    $installLink = admin_url() . "options-general.php?page=civicrm-install";
    echo '<div id="civicrm-warning" class="updated fade">' .
       '<p><strong>' .
1091
       __( 'CiviCRM is almost ready.', 'civicrm' ) .
colemanw's avatar
colemanw committed
1092 1093
       '</strong> ' .
       sprintf(
1094
        __( 'You must <a href="%s">configure CiviCRM</a> for it to work.', 'civicrm' ),
colemanw's avatar
colemanw committed
1095 1096 1097 1098 1099 1100 1101
        $installLink
       ) .
       '</p></div>';

  }


1102 1103 1104 1105 1106
  // ---------------------------------------------------------------------------
  // HTML head
  // ---------------------------------------------------------------------------


1107 1108 1109
  /**
   * Perform necessary stuff prior to CiviCRM's admin page being loaded
   * This needs to be a method because it can then be hooked into WP at the
1110
   * right time.
1111
   *
1112
   * @since 4.6
1113 1114 1115
   */
  public function admin_page_load() {

1116
    // This is required for AJAX calls in WordPress admin
haystack's avatar
haystack committed
1117
    $_REQUEST['noheader'] = $_GET['noheader'] = TRUE;
1118

1119
    // Add resources for back end
1120 1121
    $this->add_core_resources( FALSE );

1122
    // Check setting for path to wp-load.php
haystack's avatar
haystack committed
1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135 1136 1137 1138 1139 1140 1141 1142 1143 1144 1145 1146 1147 1148 1149 1150 1151 1152
    $this->add_wpload_setting();

  }


  /**
   * When CiviCRM is loaded in WP Admin, check for the existence of a setting
   * which holds the path to wp-load.php. This is the only reliable way to
   * bootstrap WordPress from CiviCRM.
   *
   * CMW: I'm not entirely happy with this approach, because the value will be
   * different for different installs (e.g. when a dev site is migrated to live)
   * A better approach would be to store this setting in civicrm.settings.php as
   * a constant, but doing that involves a complicated process of getting a new
   * setting registered in the installer.
   *
   * Also, it needs to be decided whether this value should be tied to a CiviCRM
   * 'domain', since a single CiviCRM install could potentially be used by a
   * number of WordPress installs. This is not relevant to its use in WordPress
   * Multisite, because the path to wp-load.php is common to all sites on the
   * network.
   *
   * My final concern is that the value will only be set *after* someone visits
   * CiviCRM in the back end. I have restricted it to this so as not to add
   * overhead to the front end, but there remains the possibility that the value
   * could be missing. To repeat: this would be better in civicrm.settings.php.
   *
   * To get the path to wp-load.php, use:
   * $path = CRM_Core_BAO_Setting::getItem('CiviCRM Preferences', 'wpLoadPhp');
   *
1153
   * @since 4.6.3
haystack's avatar
haystack committed
1154 1155 1156 1157 1158 1159 1160
   */
  public function add_wpload_setting() {

    if (!$this->initialize()) {
      return;
    }

1161
    // Get path to wp-load.php
haystack's avatar
haystack committed
1162 1163
    $path = ABSPATH . 'wp-load.php';

1164
    // Get the setting, if it exists
haystack's avatar
haystack committed
1165 1166
    $setting = CRM_Core_BAO_Setting::getItem('CiviCRM Preferences', 'wpLoadPhp');

1167
    // If we don't have one, create it
haystack's avatar
haystack committed
1168 1169 1170 1171
    if ( is_null( $setting ) ) {
      CRM_Core_BAO_Setting::setItem($path, 'CiviCRM Preferences', 'wpLoadPhp');
    }

1172
    // Is it different to the one we've stored?
haystack's avatar
haystack committed
1173
    if ( $setting !== $path ) {
1174
      // Yes - set new path (this could be because we've changed server or location)
haystack's avatar
haystack committed
1175 1176 1177
      CRM_Core_BAO_Setting::setItem($path, 'CiviCRM Preferences', 'wpLoadPhp');
    }

1178 1179 1180 1181 1182 1183
  }


  /**
   * Perform necessary stuff prior to CiviCRM being loaded on the front end
   * This needs to be a method because it can then be hooked into WP at the
1184
   * right time.
1185
   *
1186
   * @since 4.6
1187 1188 1189
   */
  public function front_end_page_load() {

1190 1191 1192 1193 1194
    static $frontend_loaded = FALSE;
    if ( $frontend_loaded ) {
      return;
    }

1195
    // Add resources for front end
1196 1197
    $this->add_core_resources( TRUE );

1198
    // Merge CiviCRM's HTML header with the WordPress theme's header
1199
    add_action( 'wp_head', array( $this, 'wp_head' ) );
1200

1201
    // Set flag so this only happens once
1202 1203
    $frontend_loaded = TRUE;

1204 1205 1206
  }


1207
  /**
1208
   * Load only the CiviCRM CSS.
1209
   *
1210 1211 1212 1213 1214
   * This is needed because $this->front_end_page_load() is only called when
   * there is a single CiviCRM entity present on a page or archive and, whilst
   * we don't want all the Javascript to load, we do want stylesheets.
   *
   * @since 4.6
1215 1216
   */
  public function front_end_css_load() {
1217

1218 1219 1220 1221 1222
    static $frontend_css_loaded = FALSE;
    if ( $frontend_css_loaded ) {
      return;
    }

1223 1224 1225
    if (!$this->initialize()) {
      return;
    }
1226

1227
    $config = CRM_Core_Config::singleton();
1228

1229
    // Default custom CSS to standalone
1230
    $dependent = NULL;
1231

1232 1233
    // Load core CSS
    if (!CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'disable_core_css')) {
1234

1235
      // Enqueue stylesheet
1236 1237 1238
      wp_enqueue_style(
        'civicrm_css',
        $config->resourceBase . 'css/civicrm.css',
1239 1240 1241
        NULL, // Dependencies
        CIVICRM_PLUGIN_VERSION, // Version
        'all' // Media
1242
      );
1243

1244
      // Custom CSS is dependent
1245
      $dependent = array( 'civicrm_css' );
1246

1247
    }
1248

1249 1250 1251 1252 1253
    // Load custom CSS
    if (!empty($config->customCSSURL)) {
      wp_enqueue_style(
        'civicrm_custom_css',
        $config->customCSSURL,
1254 1255 1256
        $dependent, // Dependencies
        CIVICRM_PLUGIN_VERSION, // Version
        'all' // Media
1257 1258
      );
    }
1259

1260
    // Set flag so this only happens once
1261 1262
    $frontend_css_loaded = TRUE;

1263 1264 1265
  }


1266
  /**
1267 1268 1269
   * Add CiviCRM core resources.
   *
   * @since 4.6
1270
   *
1271
   * @param bool $front_end True if on WP front end, false otherwise.
1272
   */
1273
  public function add_core_resources( $front_end = TRUE ) {
1274

1275 1276 1277
    if (!$this->initialize()) {
      return;
    }
1278

1279 1280
    $config = CRM_Core_Config::singleton();
    $config->userFrameworkFrontend = $front_end;
1281

1282
    // Add CiviCRM core resources
1283
    CRM_Core_Resources::singleton()->addCoreResources();
1284

1285 1286 1287 1288
  }


  /**
1289 1290 1291
   * Merge CiviCRM's HTML header with the WordPress theme's header.
   *
   * Callback from WordPress 'admin_head' and 'wp_head' hooks.
1292
   *
1293
   * @since 4.4
1294 1295 1296
   */
  public function wp_head() {

1297
    // CRM-11823 - If CiviCRM bootstrapped, then merge its HTML header with the CMS's header
1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311
    global $civicrm_root;
    if ( empty( $civicrm_root ) ) {
      return;
    }

    $region = CRM_Core_Region::instance('html-header', FALSE);
    if ( $region ) {
      echo '<!-- CiviCRM html header -->';
      echo $region->render( '' );
    }

  }


haystack's avatar
haystack committed
1312 1313 1314 1315 1316 1317
  // ---------------------------------------------------------------------------
  // CiviCRM Invocation (this outputs Civi's markup)
  // ---------------------------------------------------------------------------


  /**
1318 1319
   * Invoke CiviCRM in a WordPress context.
   *
haystack's avatar
haystack committed
1320 1321
   * Callback function from add_menu_page()
   * Callback from WordPress 'init' and 'the_content' hooks
1322
   * Also called by shortcode_render() and _civicrm_update_user()
haystack's avatar
haystack committed
1323
   *
1324
   * @since 4.4
haystack's avatar
haystack committed
1325 1326 1327 1328 1329 1330 1331 1332
   */
  public function invoke() {

    static $alreadyInvoked = FALSE;
    if ( $alreadyInvoked ) {
      return;
    }

1333
    // Bail if this is called via a content-preprocessing plugin
1334
    if ( $this->is_page_request() && !in_the_loop() && !is_admin() ) {
1335 1336
      return;
    }