civicrm.php 45.2 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
/*
 +--------------------------------------------------------------------+
Kurund Jalmi's avatar
Kurund Jalmi committed
17
 | CiviCRM version 4.7                                                |
totten's avatar
totten committed
18
 +--------------------------------------------------------------------+
yashodha's avatar
yashodha committed
19
 | Copyright CiviCRM LLC (c) 2004-2018                                |
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
yashodha's avatar
yashodha committed
44
 * @copyright CiviCRM LLC (c) 2004-2018
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 341
    /*
     * 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.
     */
342
    if ( empty( session_id() ) && ! ( defined( 'WP_CLI' ) && WP_CLI ) ) {
colemanw's avatar
colemanw committed
343 344 345
      session_start();
    }

346
    // Get classes and instantiate
347
    $this->include_files();
348

349
    // Do plugin activation
350
    $this->activation();
351

352 353 354 355 356
    // Use translation files
    $this->enable_translation();

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

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

  }


  /**
369 370
   * Set broad CiviCRM context.
   *
371 372
   * Setter for determining if CiviCRM is currently being displayed in WordPress.
   * This becomes true whe CiviCRM is called in the following contexts:
373 374 375 376 377
   *
   * (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
   *
378
   * It is NOT true when CiviCRM is called via a shortcode.
haystack's avatar
haystack committed
379
   *
380
   * @since 4.4
381 382 383
   */
  public function civicrm_in_wordpress_set() {

384
    // Store
385 386 387 388 389 390 391 392 393 394
    self::$in_wordpress = ( isset( $_GET['page'] ) && $_GET['page'] == 'CiviCRM' ) ? TRUE : FALSE;

  }


  /**
   * Getter for testing if CiviCRM is currently being displayed in WordPress.
   *
   * @see $this->civicrm_in_wordpress_set()
   *
395 396 397
   * @since 4.4
   *
   * @return bool $in_wordpress True if CiviCRM is displayed in WordPress, false otherwise.
colemanw's avatar
colemanw committed
398 399 400
   */
  public function civicrm_in_wordpress() {

401 402 403 404 405 406 407 408
    /**
     * 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.
     */
409
    return apply_filters( 'civicrm_in_wordpress', self::$in_wordpress );
colemanw's avatar
colemanw committed
410 411 412 413

  }


414
  /**
415 416
   * Set specific CiviCRM context.
   *
417 418 419 420 421 422 423 424
   * 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
   *
425
   * The following codes correspond to the different contexts:
426 427 428 429 430 431
   *
   * (a) 'admin'
   * (b) 'basepage'
   * (c) 'nonpage'
   * (d) 'shortcode'
   *
432 433 434
   * @since 4.4
   *
   * @param string $context One of the four context codes above.
435 436 437
   */
  public function civicrm_context_set( $context ) {

438
    // Store
439 440 441 442 443 444
    self::$context = $context;

  }


  /**
445 446
   * Get specific CiviCRM context.
   *
447 448 449 450
   * Getter for determining how CiviCRM is currently being displayed in WordPress.
   *
   * @see $this->civicrm_context_set()
   *
451 452 453
   * @since 4.4
   *
   * @return string The context in which CiviCRM is displayed in WordPress.
454 455 456
   */
  public function civicrm_context_get() {

457 458 459 460 461 462 463 464
    /**
     * 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.
     */
465 466 467 468 469
    return apply_filters( 'civicrm_context', self::$context );

  }


470 471 472 473 474 475
  // ---------------------------------------------------------------------------
  // Files
  // ---------------------------------------------------------------------------


  /**
476
   * Include files.
477
   *
478
   * @since 4.4
479 480
   */
  public function include_files() {
481

482
    // Include users class
483 484
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.users.php';
    $this->users = new CiviCRM_For_WordPress_Users;
485

486
    // Include shortcodes class
487 488
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.shortcodes.php';
    $this->shortcodes = new CiviCRM_For_WordPress_Shortcodes;
489

490
    // Include shortcodes modal dialog class
491 492
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.shortcodes.modal.php';
    $this->modal = new CiviCRM_For_WordPress_Shortcodes_Modal;
493

494
    // Include basepage class
495 496
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.basepage.php';
    $this->basepage = new CiviCRM_For_WordPress_Basepage;
497

498 499 500
  }


501 502 503 504 505
  // ---------------------------------------------------------------------------
  // Hooks
  // ---------------------------------------------------------------------------


colemanw's avatar
colemanw committed
506
  /**
507
   * Register hooks on init.
haystack's avatar
haystack committed
508
   *
509
   * @since 4.4
colemanw's avatar
colemanw committed
510 511 512
   */
  public function register_hooks() {

513
    // Always add the common hooks
514
    $this->register_hooks_common();
515

516
    // When in WordPress admin...
517 518
    if ( is_admin() ) {

519
      // Set context
520
      $this->civicrm_context_set( 'admin' );
521

522
      // Handle WP admin context
523
      $this->register_hooks_admin();
524
      return;
525

526 527
    }

528
    // Go no further if CiviCRM not installed yet
529 530
    if ( ! CIVICRM_INSTALLED ) return;

531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553
    // 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();

554
    // When embedded via wpBasePage or AJAX call...
555
    if ( $this->civicrm_in_wordpress() ) {
556

557
      /*
558 559 560 561 562 563 564 565
       * 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() ) {
566

567
        // Set context
568
        $this->civicrm_context_set( 'nonpage' );
569

570
        // Add core resources for front end
571
        add_action( 'wp', array( $this, 'front_end_page_load' ) );
572

573
        // Echo all output when WP has been set up but nothing has been rendered
574 575
        add_action( 'wp', array( $this, 'invoke' ) );
        return;
576

577
      }
578

579
      // Set context
580
      $this->civicrm_context_set( 'basepage' );
581

582
      // If we get here, we must be in a wpBasePage context
583
      $this->basepage->register_hooks();
584
      return;
585

586
    }
587

588
    // Set context
589
    $this->civicrm_context_set( 'shortcode' );
590

591
    // That leaves us with handling shortcodes, should they exist
592
    $this->shortcodes->register_hooks();
593 594 595

  }

colemanw's avatar
colemanw committed
596

597
  /**
598
   * Register hooks that must always be present.
599
   *
600
   * @since 4.4
601
   */
602
  public function register_hooks_common() {
603

604
    // Register user hooks
605
    $this->users->register_hooks();
colemanw's avatar
colemanw committed
606

607
  }
608 609


610
  /**
611
   * Register hooks to handle CiviCRM in a WordPress admin context.
612
   *
613
   * @since 4.4
614
   */
615
  public function register_hooks_admin() {
616

617
    // Modify the admin menu
618
    add_action( 'admin_menu', array( $this, 'add_menu_items' ) );
619

620
    // Set page title
621 622
    add_filter( 'admin_title', array( $this, 'set_admin_title' ) );

623
    // Print CiviCRM's header
624 625
    add_action('admin_head', array( $this, 'wp_head' ), 50);

626
    // If settings file does not exist, show notice with link to installer
627
    if ( ! CIVICRM_INSTALLED ) {
628
      if ( isset( $_GET['page'] ) && $_GET['page'] == 'civicrm-install' ) {
629 630
        // Set install type
        $_GET['civicrm_install_type'] = 'wordpress';
631
      } else {
632
        // Show notice
633
        add_action( 'admin_notices', array( $this, 'show_setup_warning' ) );
colemanw's avatar
colemanw committed
634
      }
635
    }
636

637
    // Enable shortcode modal
638 639
    $this->modal->register_hooks();

640
  }
colemanw's avatar
colemanw committed
641 642


643 644 645 646 647
  // ---------------------------------------------------------------------------
  // CiviCRM Initialisation
  // ---------------------------------------------------------------------------


colemanw's avatar
colemanw committed
648
  /**
649 650 651
   * Initialize CiviCRM.
   *
   * @since 4.4
haystack's avatar
haystack committed
652
   *
653
   * @return bool $success True if CiviCRM is initialized, false otherwise.
colemanw's avatar
colemanw committed
654 655 656
   */
  public function initialize() {

yashodha's avatar
yashodha committed
657 658
    static $initialized = FALSE;
    static $failure = FALSE;
colemanw's avatar
colemanw committed
659 660

    if ( $failure ) {
yashodha's avatar
yashodha committed
661
      return FALSE;
colemanw's avatar
colemanw committed
662 663 664 665 666
    }

    if ( ! $initialized ) {

      // Check for php version and ensure its greater than minPhpVersion
totten's avatar
totten committed
667
      $minPhpVersion = '5.3.4';
colemanw's avatar
colemanw committed
668 669 670
      if ( version_compare( PHP_VERSION, $minPhpVersion ) < 0 ) {
        echo '<p>' .
           sprintf(
671
            __( 'CiviCRM requires PHP Version %s or greater. You are running PHP Version %s', 'civicrm' ),
colemanw's avatar
colemanw committed
672 673 674 675 676 677 678
            $minPhpVersion,
            PHP_VERSION
           ) .
           '<p>';
        exit();
      }

679
      // Check for settings
680
      if ( ! CIVICRM_INSTALLED ) {
yashodha's avatar
yashodha committed
681
        $error = FALSE;
682
      } elseif ( file_exists( CIVICRM_SETTINGS_PATH) ) {
colemanw's avatar
colemanw committed
683 684 685
        $error = include_once ( CIVICRM_SETTINGS_PATH );
      }

686
      // Autoload
colemanw's avatar
colemanw committed
687 688 689
      require_once 'CRM/Core/ClassLoader.php';
      CRM_Core_ClassLoader::singleton()->register();

690
      // Get ready for problems
colemanw's avatar
colemanw committed
691
      $installLink    = admin_url() . "options-general.php?page=civicrm-install";
692 693 694
      $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
695 696


697
      // Construct message
colemanw's avatar
colemanw committed
698
      $errorMsgAdd = sprintf(
699
        __( '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
700 701 702 703 704
        $docLinkInstall,
        $docLinkTrouble,
        $forumLink
      );

705
      // Does install message get used?
colemanw's avatar
colemanw committed
706
      $installMessage = sprintf(
707
        __( 'Click <a href="%s">here</a> for fresh install.', 'civicrm' ),
colemanw's avatar
colemanw committed
708 709 710
        $installLink
      );

yashodha's avatar
yashodha committed
711
      if ($error == FALSE) {
colemanw's avatar
colemanw committed
712
        header( 'Location: ' . admin_url() . 'options-general.php?page=civicrm-install' );
yashodha's avatar
yashodha committed
713
        return FALSE;
colemanw's avatar
colemanw committed
714
      }
715

716
      // Access global defined in civicrm.settings.php
haystack's avatar
haystack committed
717
      global $civicrm_root;
718

719
      // This does pretty much all of the civicrm initialization
colemanw's avatar
colemanw committed
720
      if ( ! file_exists( $civicrm_root . 'CRM/Core/Config.php' ) ) {
yashodha's avatar
yashodha committed
721
        $error = FALSE;
colemanw's avatar
colemanw committed
722 723 724 725
      } else {
        $error = include_once ( 'CRM/Core/Config.php' );
      }

726
      // Have we got it?
yashodha's avatar
yashodha committed
727
      if ( $error == FALSE ) {
colemanw's avatar
colemanw committed
728

729
        // Set static flag
yashodha's avatar
yashodha committed
730
        $failure = TRUE;
colemanw's avatar
colemanw committed
731 732 733 734 735

        // FIX ME - why?
        wp_die(
          "<strong><p class='error'>" .
          sprintf(
736
            __( '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
737 738 739 740
            CIVICRM_SETTINGS_PATH
          ) .
          "</p><p class='error'> &raquo; " .
          sprintf(
741
            __( 'civicrm_root is currently set to: <em>%s</em>.', 'civicrm' ),
colemanw's avatar
colemanw committed
742 743 744 745 746
            $civicrm_root
          ) .
          "</p><p class='error'>" . $errorMsgAdd . "</p></strong>"
        );

747
        // Won't reach here!
yashodha's avatar
yashodha committed
748
        return FALSE;
colemanw's avatar
colemanw committed
749 750 751

      }

752
      // Set static flag
yashodha's avatar
yashodha committed
753
      $initialized = TRUE;
colemanw's avatar
colemanw committed
754

755
      // Initialize the system by creating a config object
colemanw's avatar
colemanw committed
756 757
      $config = CRM_Core_Config::singleton();

758
      // Sync the logged in user with WP
colemanw's avatar
colemanw committed
759 760 761
      global $current_user;
      if ( $current_user ) {

762
        // Sync procedure sets session values for logged in users
colemanw's avatar
colemanw committed
763 764
        require_once 'CRM/Core/BAO/UFMatch.php';
        CRM_Core_BAO_UFMatch::synchronize(
765 766
          $current_user, // User object
          FALSE, // Do not update
colemanw's avatar
colemanw committed
767
          'WordPress', // CMS
768
          $this->users->get_civicrm_contact_type('Individual')
colemanw's avatar
colemanw committed
769 770 771 772 773 774
        );

      }

    }

775 776 777 778 779
    /**
     * Broadcast that CiviCRM is now initialized.
     *
     * @since 4.4
     */
colemanw's avatar
colemanw committed
780 781
    do_action( 'civicrm_initialized' );

782
    // Success!
yashodha's avatar
yashodha committed
783
    return TRUE;
colemanw's avatar
colemanw committed
784 785

  }
haystack's avatar
haystack committed
786 787


788 789 790 791 792
  // ---------------------------------------------------------------------------
  // Plugin setup
  // ---------------------------------------------------------------------------


colemanw's avatar
colemanw committed
793
  /**
794 795
   * Load translation files.
   *
colemanw's avatar
colemanw committed
796 797
   * 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
798
   *
799
   * @since 4.4
colemanw's avatar
colemanw committed
800 801 802
   */
  public function enable_translation() {

803
    // Load translations
colemanw's avatar
colemanw committed
804
    load_plugin_textdomain(
805 806 807
      'civicrm', // Unique name
      FALSE, // Deprecated argument
      dirname( plugin_basename( __FILE__ ) ) . '/languages/' // Relative path to translation files
colemanw's avatar
colemanw committed
808 809 810 811
    );

  }

haystack's avatar
haystack committed
812

colemanw's avatar
colemanw committed
813
  /**
814 815 816
   * Adds menu items to WordPress admin menu.
   *
   * Callback method for 'admin_menu' hook as set in register_hooks().
haystack's avatar
haystack committed
817
   *
818
   * @since 4.4
colemanw's avatar
colemanw committed
819 820 821
   */
  public function add_menu_items() {

822
    $civilogo = file_get_contents( plugin_dir_path( __FILE__ ) . 'assets/civilogo.svg.b64' );
823

824 825 826 827 828 829 830 831 832 833 834 835
    /**
     * 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' );

836
    // Check for settings file
837
    if ( CIVICRM_INSTALLED ) {
colemanw's avatar
colemanw committed
838

839
      // Add top level menu item
840
      $menu_page = add_menu_page(
841 842
        __( 'CiviCRM', 'civicrm' ),
        __( 'CiviCRM', 'civicrm' ),
colemanw's avatar
colemanw committed
843 844 845
        'access_civicrm',
        'CiviCRM',
        array( $this, 'invoke' ),
846
        $civilogo,
847
        $position
colemanw's avatar
colemanw committed
848
      );
849

850
      // Add core resources prior to page load
851
      add_action( 'load-' . $menu_page, array( $this, 'admin_page_load' ) );
852

colemanw's avatar
colemanw committed
853 854
    } else {

855
      // Add top level menu item
856
      $menu_page = add_menu_page(
857 858
        __( 'CiviCRM Installer', 'civicrm' ),
        __( 'CiviCRM Installer', 'civicrm' ),
colemanw's avatar
colemanw committed
859 860
        'manage_options',
        'civicrm-install',
861 862
        array( $this, 'run_installer' ),
        $civilogo,
863
        $position
colemanw's avatar
colemanw committed
864 865
      );

866
      /*
867
      // Add scripts and styles like this
868 869 870
      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 );
871
      */
872

colemanw's avatar
colemanw committed
873 874 875 876 877
    }

  }


878 879 880 881 882
  // ---------------------------------------------------------------------------
  // Installation
  // ---------------------------------------------------------------------------


colemanw's avatar
colemanw committed
883
  /**
884
   * Callback method for add_options_page() that runs the CiviCRM installer.
haystack's avatar
haystack committed
885
   *
886
   * @since 4.4
colemanw's avatar
colemanw committed
887 888
   */
  public function run_installer() {
889 890 891 892 893 894 895 896 897
    $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']);
898
      if (file_exists($civicrmCore . DIRECTORY_SEPARATOR . '.use-civicrm-setup') && file_exists($loader)) {
899 900 901
        require_once $loader;
        require_once implode(DIRECTORY_SEPARATOR, [$civicrmCore, 'CRM', 'Core', 'ClassLoader.php']);
        CRM_Core_ClassLoader::singleton()->register();
902
        \Civi\Setup::assertProtocolCompatibility(1.0);
903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918
        \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
919

920
    // Uses CIVICRM_PLUGIN_DIR instead of WP_PLUGIN_DIR
colemanw's avatar
colemanw committed
921 922 923 924 925 926 927
    $installFile =
      CIVICRM_PLUGIN_DIR .
      'civicrm' . DIRECTORY_SEPARATOR .
      'install' . DIRECTORY_SEPARATOR .
      'index.php';

    // Notice: Undefined variable: siteDir in:
928
    // CIVICRM_PLUGIN_DIR/civicrm/install/index.php on line 456
colemanw's avatar
colemanw committed
929 930 931 932 933 934
    include ( $installFile );

  }


  /**
935
   * Callback method for missing settings file in register_hooks().
haystack's avatar
haystack committed
936
   *
937
   * @since 4.4
colemanw's avatar
colemanw committed
938 939 940 941 942 943
   */
  public function show_setup_warning() {

    $installLink = admin_url() . "options-general.php?page=civicrm-install";
    echo '<div id="civicrm-warning" class="updated fade">' .
       '<p><strong>' .
944
       __( 'CiviCRM is almost ready.', 'civicrm' ) .
colemanw's avatar
colemanw committed
945 946
       '</strong> ' .
       sprintf(
947
        __( 'You must <a href="%s">configure CiviCRM</a> for it to work.', 'civicrm' ),
colemanw's avatar
colemanw committed
948 949 950 951 952 953 954
        $installLink
       ) .
       '</p></div>';

  }


955 956 957 958 959
  // ---------------------------------------------------------------------------
  // HTML head
  // ---------------------------------------------------------------------------


960 961 962
  /**
   * 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
963
   * right time.
964
   *
965
   * @since 4.6
966 967 968
   */
  public function admin_page_load() {

969 970 971
    // This is required for AJAX calls in WordPress admin
    $_GET['noheader'] = TRUE;

972
    // Add resources for back end
973 974
    $this->add_core_resources( FALSE );

975
    // Check setting for path to wp-load.php
haystack's avatar
haystack committed
976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005
    $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');
   *
1006
   * @since 4.6.3
haystack's avatar
haystack committed
1007 1008 1009 1010 1011 1012 1013
   */
  public function add_wpload_setting() {

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

1014
    // Get path to wp-load.php
haystack's avatar
haystack committed
1015 1016
    $path = ABSPATH . 'wp-load.php';

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

1020
    // If we don't have one, create it
haystack's avatar
haystack committed
1021 1022 1023 1024
    if ( is_null( $setting ) ) {
      CRM_Core_BAO_Setting::setItem($path, 'CiviCRM Preferences', 'wpLoadPhp');
    }

1025
    // Is it different to the one we've stored?
haystack's avatar
haystack committed
1026
    if ( $setting !== $path ) {
1027
      // Yes - set new path (this could be because we've changed server or location)
haystack's avatar
haystack committed
1028 1029 1030
      CRM_Core_BAO_Setting::setItem($path, 'CiviCRM Preferences', 'wpLoadPhp');
    }

1031 1032 1033 1034 1035 1036
  }


  /**
   * 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
1037
   * right time.
1038
   *
1039
   * @since 4.6
1040 1041 1042
   */
  public function front_end_page_load() {

1043 1044 1045 1046 1047
    static $frontend_loaded = FALSE;
    if ( $frontend_loaded ) {
      return;
    }

1048
    // Add resources for front end
1049 1050
    $this->add_core_resources( TRUE );

1051
    // Merge CiviCRM's HTML header with the WordPress theme's header
1052
    add_action( 'wp_head', array( $this, 'wp_head' ) );
1053

1054
    // Set flag so this only happens once
1055 1056
    $frontend_loaded = TRUE;

1057 1058 1059
  }


1060
  /**
1061
   * Load only the CiviCRM CSS.
1062
   *
1063 1064 1065 1066 1067
   * 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
1068 1069
   */
  public function front_end_css_load() {
1070

1071 1072 1073 1074 1075
    static $frontend_css_loaded = FALSE;
    if ( $frontend_css_loaded ) {
      return;
    }

1076 1077 1078
    if (!$this->initialize()) {
      return;
    }
1079

1080
    $config = CRM_Core_Config::singleton();
1081

1082
    // Default custom CSS to standalone
1083
    $dependent = NULL;
1084

1085 1086
    // Load core CSS
    if (!CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'disable_core_css')) {
1087

1088
      // Enqueue stylesheet
1089 1090 1091
      wp_enqueue_style(
        'civicrm_css',
        $config->resourceBase . 'css/civicrm.css',
1092 1093 1094
        NULL, // Dependencies
        CIVICRM_PLUGIN_VERSION, // Version
        'all' // Media
1095
      );
1096

1097
      // Custom CSS is dependent
1098
      $dependent = array( 'civicrm_css' );
1099

1100
    }
1101

1102 1103 1104 1105 1106
    // Load custom CSS
    if (!empty($config->customCSSURL)) {
      wp_enqueue_style(
        'civicrm_custom_css',
        $config->customCSSURL,
1107 1108 1109
        $dependent, // Dependencies
        CIVICRM_PLUGIN_VERSION, // Version
        'all' // Media
1110 1111
      );
    }
1112

1113
    // Set flag so this only happens once
1114 1115
    $frontend_css_loaded = TRUE;

1116 1117 1118
  }


1119
  /**
1120 1121 1122
   * Add CiviCRM core resources.
   *
   * @since 4.6
1123
   *
1124
   * @param bool $front_end True if on WP front end, false otherwise.
1125
   */
1126
  public function add_core_resources( $front_end = TRUE ) {
1127

1128 1129 1130
    if (!$this->initialize()) {
      return;
    }
1131

1132 1133
    $config = CRM_Core_Config::singleton();
    $config->userFrameworkFrontend = $front_end;
1134

1135
    // Add CiviCRM core resources
1136
    CRM_Core_Resources::singleton()->addCoreResources();
1137

1138 1139 1140 1141
  }


  /**
1142 1143 1144
   * Merge CiviCRM's HTML header with the WordPress theme's header.
   *
   * Callback from WordPress 'admin_head' and 'wp_head' hooks.
1145
   *
1146
   * @since 4.4
1147 1148 1149
   */
  public function wp_head() {

1150
    // CRM-11823 - If CiviCRM bootstrapped, then merge its HTML header with the CMS's header
1151 1152 1153 1154 1155 1156 1157 1158 1159 1160 1161 1162 1163 1164
    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
1165 1166 1167 1168 1169 1170
  // ---------------------------------------------------------------------------
  // CiviCRM Invocation (this outputs Civi's markup)
  // ---------------------------------------------------------------------------


  /**
1171 1172
   * Invoke CiviCRM in a WordPress context.
   *
haystack's avatar
haystack committed
1173 1174
   * Callback function from add_menu_page()
   * Callback from WordPress 'init' and 'the_content' hooks
1175
   * Also called by shortcode_render() and _civicrm_update_user()
haystack's avatar
haystack committed
1176
   *
1177
   * @since 4.4
haystack's avatar
haystack committed
1178 1179 1180 1181 1182 1183 1184 1185
   */
  public function invoke() {

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

1186
    // Bail if this is called via a content-preprocessing plugin
1187
    if ( $this->is_page_request() && !in_the_loop() && !is_admin() ) {
1188 1189
      return;
    }
1190

1191
    if (!$this->initialize()) {
1192
      return;
haystack's avatar
haystack committed
1193 1194
    }

1195 1196 1197 1198 1199 1200
    /*
     * CRM-12523
     * WordPress has it's own timezone calculations
     * CiviCRM relies on the php default timezone which WP
     * overrides with UTC in wp-settings.php
     */
haystack's avatar
haystack committed
1201 1202 1203 1204 1205 1206 1207
    $wpBaseTimezone = date_default_timezone_get();
    $wpUserTimezone = get_option('timezone_string');
    if ($wpUserTimezone) {
      date_default_timezone_set($wpUserTimezone);
      CRM_Core_Config::singleton()->userSystem->setMySQLTimeZone();
    }

1208 1209 1210 1211 1212
    /*
     * CRM-95XX
     * At this point we are calling a CiviCRM function
     * WP always quotes the request, CiviCRM needs to reverse what it just did.
     */
haystack's avatar
haystack committed
1213
    $this->remove_wp_magic_quotes();
1214

1215
    // Code inside invoke() requires the current user to be set up
1216
    $current_user = wp_get_current_user();
1217

1218
    /*
1219
     * Bypass synchronize if running upgrade to avoid any serious non-recoverable
1220
     * error which might hinder the upgrade process.
haystack's avatar
haystack committed
1221 1222
     */
    if ( CRM_Utils_Array::value('q', $_GET) != 'civicrm/upgrade' ) {
1223
      $this->users->sync_user( $current_user );
haystack's avatar
haystack committed
1224
    }
1225

1226
    // Set flag
haystack's avatar
haystack committed
1227 1228
    $alreadyInvoked = TRUE;

1229
    // Get args
1230
    $argdata = $this->get_request_args();
1231

1232
    // Set dashboard as default if args are empty
1233
   if ( !isset( $_GET['q'] ) ) {
1234 1235 1236 1237
      $_GET['q']      = 'civicrm/dashboard';
      $_GET['reset']  = 1;
      $argdata['args'] = array('civicrm', 'dashboard');
    }
1238

1239
    // Do the business
1240
    CRM_Core_Invoke::invoke($argdata['args']);
haystack's avatar
haystack committed
1241

1242
    // Restore WP's timezone
haystack's avatar
haystack committed
1243 1244 1245 1246
    if ($wpBaseTimezone) {
      date_default_timezone_set($wpBaseTimezone);
    }

1247
    // Restore WP's arrays
1248
    $this->restore_wp_magic_quotes();
1249

1250 1251 1252 1253 1254
    /**
     * Broadcast that CiviCRM has been invoked.
     *
     * @since 4.4
     */
haystack's avatar
haystack committed
1255 1256 1257 1258 1259 1260
    do_action( 'civicrm_invoked' );

  }


  /**
1261 1262 1263 1264
   * Non-destructively override WordPress magic quotes.
   *
   * Only called by invoke() to undo WordPress default behaviour.
   * CMW: Should probably be a private method.
haystack's avatar
haystack committed
1265
   *
1266
   * @since 4.4
haystack's avatar
haystack committed
1267 1268
   */
  public function remove_wp_magic_quotes() {
1269