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
/*
 +--------------------------------------------------------------------+
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() {

387
    // Store
388 389 390 391 392 393 394 395 396 397
    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()
   *
398 399 400
   * @since 4.4
   *
   * @return bool $in_wordpress True if CiviCRM is displayed in WordPress, false otherwise.
colemanw's avatar
colemanw committed
401 402 403
   */
  public function civicrm_in_wordpress() {

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

  }


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

441
    // Store
442 443 444 445 446 447
    self::$context = $context;

  }


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

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

  }


473 474 475 476 477 478
  // ---------------------------------------------------------------------------
  // Files
  // ---------------------------------------------------------------------------


  /**
479
   * Include files.
480
   *
481
   * @since 4.4
482 483
   */
  public function include_files() {
484

485
    // Include users class
486 487
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.users.php';
    $this->users = new CiviCRM_For_WordPress_Users;
488

489
    // Include shortcodes class
490 491
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.shortcodes.php';
    $this->shortcodes = new CiviCRM_For_WordPress_Shortcodes;
492

493
    // Include shortcodes modal dialog class
494 495
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.shortcodes.modal.php';
    $this->modal = new CiviCRM_For_WordPress_Shortcodes_Modal;
496

497
    // Include basepage class
498 499
    include_once CIVICRM_PLUGIN_DIR . 'includes/civicrm.basepage.php';
    $this->basepage = new CiviCRM_For_WordPress_Basepage;
500

501 502 503
  }


504 505 506 507 508
  // ---------------------------------------------------------------------------
  // Hooks
  // ---------------------------------------------------------------------------


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

516
    // Always add the common hooks
517
    $this->register_hooks_common();
518

519
    // When in WordPress admin...
520 521
    if ( is_admin() ) {

522
      // Set context
523
      $this->civicrm_context_set( 'admin' );
524

525
      // Handle WP admin context
526
      $this->register_hooks_admin();
527
      return;
528

529 530
    }

531
    // Go no further if CiviCRM not installed yet
532 533
    if ( ! CIVICRM_INSTALLED ) return;

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

557
    // When embedded via wpBasePage or AJAX call...
558
    if ( $this->civicrm_in_wordpress() ) {
559

560
      /*
561 562 563 564 565 566 567 568
       * 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() ) {
569

570
        // Set context
571
        $this->civicrm_context_set( 'nonpage' );
572

573
        // Add core resources for front end
574
        add_action( 'wp', array( $this, 'front_end_page_load' ) );
575

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

580
      }
581

582
      // Set context
583
      $this->civicrm_context_set( 'basepage' );
584

585
      // If we get here, we must be in a wpBasePage context
586
      $this->basepage->register_hooks();
587
      return;
588

589
    }
590

591
    // Set context
592
    $this->civicrm_context_set( 'shortcode' );
593

594
    // That leaves us with handling shortcodes, should they exist
595
    $this->shortcodes->register_hooks();
596 597 598

  }

colemanw's avatar
colemanw committed
599

600
  /**
601
   * Register hooks that must always be present.
602
   *
603
   * @since 4.4
604
   */
605
  public function register_hooks_common() {
606

607
    // Register user hooks
608
    $this->users->register_hooks();
colemanw's avatar
colemanw committed
609

610
  }
611 612


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

620
    // Modify the admin menu
621
    add_action( 'admin_menu', array( $this, 'add_menu_items' ) );
622

623
    // Set page title
624 625
    add_filter( 'admin_title', array( $this, 'set_admin_title' ) );

626
    // Print CiviCRM's header
627 628
    add_action('admin_head', array( $this, 'wp_head' ), 50);

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

640
    // Enable shortcode modal
641 642
    $this->modal->register_hooks();

643
  }
colemanw's avatar
colemanw committed
644 645


646 647 648 649 650
  // ---------------------------------------------------------------------------
  // CiviCRM Initialisation
  // ---------------------------------------------------------------------------


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

yashodha's avatar
yashodha committed
660 661
    static $initialized = FALSE;
    static $failure = FALSE;
colemanw's avatar
colemanw committed
662 663

    if ( $failure ) {
yashodha's avatar
yashodha committed
664
      return FALSE;
colemanw's avatar
colemanw committed
665 666 667 668 669
    }

    if ( ! $initialized ) {

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

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

689
      // Autoload
colemanw's avatar
colemanw committed
690 691 692
      require_once 'CRM/Core/ClassLoader.php';
      CRM_Core_ClassLoader::singleton()->register();

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


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

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

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

719
      // Access global defined in civicrm.settings.php
haystack's avatar
haystack committed
720
      global $civicrm_root;
721

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

729
      // Have we got it?
yashodha's avatar
yashodha committed
730
      if ( $error == FALSE ) {
colemanw's avatar
colemanw committed
731

732
        // Set static flag
yashodha's avatar
yashodha committed
733
        $failure = TRUE;
colemanw's avatar
colemanw committed
734 735 736 737 738

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

750
        // Won't reach here!
yashodha's avatar
yashodha committed
751
        return FALSE;
colemanw's avatar
colemanw committed
752 753 754

      }

755
      // Set static flag
yashodha's avatar
yashodha committed
756
      $initialized = TRUE;
colemanw's avatar
colemanw committed
757

758
      // Initialize the system by creating a config object
colemanw's avatar
colemanw committed
759 760
      $config = CRM_Core_Config::singleton();

761
      // Sync the logged in user with WP
colemanw's avatar
colemanw committed
762 763 764
      global $current_user;
      if ( $current_user ) {

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

      }

    }

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

785
    // Success!
yashodha's avatar
yashodha committed
786
    return TRUE;
colemanw's avatar
colemanw committed
787 788

  }
haystack's avatar
haystack committed
789 790


791 792 793 794 795
  // ---------------------------------------------------------------------------
  // Plugin setup
  // ---------------------------------------------------------------------------


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

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

  }

haystack's avatar
haystack committed
815

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

825
    $civilogo = file_get_contents( plugin_dir_path( __FILE__ ) . 'assets/civilogo.svg.b64' );
826

827 828 829 830 831 832 833 834 835 836 837 838
    /**
     * 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' );

839
    // Check for settings file
840
    if ( CIVICRM_INSTALLED ) {
colemanw's avatar
colemanw committed
841

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

853
      // Add core resources prior to page load
854
      add_action( 'load-' . $menu_page, array( $this, 'admin_page_load' ) );
855

colemanw's avatar
colemanw committed
856 857
    } else {

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

869
      /*
870
      // Add scripts and styles like this
871 872 873
      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 );
874
      */
875

colemanw's avatar
colemanw committed
876 877 878 879 880
    }

  }


881 882 883 884 885
  // ---------------------------------------------------------------------------
  // Installation
  // ---------------------------------------------------------------------------


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

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

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

  }


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

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

  }


958 959 960 961 962
  // ---------------------------------------------------------------------------
  // HTML head
  // ---------------------------------------------------------------------------


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

972 973 974
    // This is required for AJAX calls in WordPress admin
    $_GET['noheader'] = TRUE;

975
    // Add resources for back end
976 977
    $this->add_core_resources( FALSE );

978
    // Check setting for path to wp-load.php
haystack's avatar
haystack committed
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 1006 1007 1008
    $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');
   *
1009
   * @since 4.6.3
haystack's avatar
haystack committed
1010 1011 1012 1013 1014 1015 1016
   */
  public function add_wpload_setting() {

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

1017
    // Get path to wp-load.php
haystack's avatar
haystack committed
1018 1019
    $path = ABSPATH . 'wp-load.php';

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

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

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

1034 1035 1036 1037 1038 1039
  }


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

1046 1047 1048 1049 1050
    static $frontend_loaded = FALSE;
    if ( $frontend_loaded ) {
      return;
    }

1051
    // Add resources for front end
1052 1053
    $this->add_core_resources( TRUE );

1054
    // Merge CiviCRM's HTML header with the WordPress theme's header
1055
    add_action( 'wp_head', array( $this, 'wp_head' ) );
1056

1057
    // Set flag so this only happens once
1058 1059
    $frontend_loaded = TRUE;

1060 1061 1062
  }


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

1074 1075 1076 1077 1078
    static $frontend_css_loaded = FALSE;
    if ( $frontend_css_loaded ) {
      return;
    }

1079 1080 1081
    if (!$this->initialize()) {
      return;
    }
1082

1083
    $config = CRM_Core_Config::singleton();
1084

1085
    // Default custom CSS to standalone
1086
    $dependent = NULL;
1087

1088 1089
    // Load core CSS
    if (!CRM_Core_BAO_Setting::getItem(CRM_Core_BAO_Setting::SYSTEM_PREFERENCES_NAME, 'disable_core_css')) {
1090

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

1100
      // Custom CSS is dependent
1101
      $dependent = array( 'civicrm_css' );
1102

1103
    }
1104

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

1116
    // Set flag so this only happens once
1117 1118
    $frontend_css_loaded = TRUE;

1119 1120 1121
  }


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

1131 1132 1133
    if (!$this->initialize()) {
      return;
    }
1134

1135 1136
    $config = CRM_Core_Config::singleton();
    $config->userFrameworkFrontend = $front_end;
1137

1138
    // Add CiviCRM core resources
1139
    CRM_Core_Resources::singleton()->addCoreResources();
1140

1141 1142 1143 1144
  }


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

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


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

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

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

1194
    if (!$this->initialize()) {
1195
      return;
haystack's avatar
haystack committed
1196 1197
    }

1198 1199 1200 1201 1202 1203
    /*
     * 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
1204 1205 1206 1207 1208 1209 1210
    $wpBaseTimezone = date_default_timezone_get();
    $wpUserTimezone = get_option('timezone_string');
    if ($wpUserTimezone) {
      date_default_timezone_set($wpUserTimezone);
      CRM_Core_Config::singleton()->userSystem->setMySQLTimeZone();
    }

1211 1212 1213 1214 1215
    /*
     * 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
1216
    $this->remove_wp_magic_quotes();
1217

1218
    // Code inside invoke() requires the current user to be set up
1219
    $current_user = wp_get_current_user();
1220

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

1229
    // Set flag
haystack's avatar
haystack committed
1230 1231
    $alreadyInvoked = TRUE;

1232
    // Get args
1233
    $argdata = $this->get_request_args();
1234

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

1242
    // Do the business
1243
    CRM_Core_Invoke::invoke($argdata['args']);
haystack's avatar
haystack committed
1244

1245
    // Restore WP's timezone
haystack's avatar
haystack committed
1246 1247 1248 1249
    if ($wpBaseTimezone) {
      date_default_timezone_set($wpBaseTimezone);
    }

1250
    // Restore WP's arrays
1251
    $this->restore_wp_magic_quotes();
1252

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

  }


  /**
1264 1265 1266 1267
   * 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
1268
   *
1269
   * @since 4.4
haystack's avatar
haystack committed