Skip to content
Snippets Groups Projects
civicrm.wpml.php 10.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • <?php
    /*
     +--------------------------------------------------------------------+
     | Copyright CiviCRM LLC. All rights reserved.                        |
     |                                                                    |
     | This work is published under the GNU AGPLv3 license with some      |
     | permitted exceptions and without any warranty. For full license    |
     | and copyright information, see https://civicrm.org/licensing       |
     +--------------------------------------------------------------------+
     */
    
    /**
     *
     * @package CRM
     * @copyright CiviCRM LLC https://civicrm.org/licensing
     *
     */
    
    // This file must not accessed directly.
    if (!defined('ABSPATH')) {
      exit;
    }
    
    /**
     * WPML plugin compatatibility class.
     *
     * @since 5.66
     */
    class CiviCRM_For_WordPress_Compat_WPML {
    
      /**
       * @var object
       * Plugin object reference.
       * @since 5.66
       * @access public
       */
      public $civi;
    
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
      /**
       * @var array
       * Collected rewrites.
       * @since 5.75
       * @access private
       */
      private $rewrites = [];
    
    
      /**
       * Instance constructor.
       *
       * @since 5.66
       */
      public function __construct() {
    
        // Store reference to CiviCRM plugin object.
        $this->civi = civi_wp();
    
        // Register plugin compatibility hooks.
        $this->register_hooks();
    
      }
    
      /**
       * Register hooks.
       *
       * This is called via the constructor during the "plugins_loaded" action which
       * is much earlier that CiviCRM's own internal hooks. The reason for this is
       * that compability may need callbacks for events that fire well before "init"
       * which is when CiviCRM begins to load.
       *
       * @since 5.66
       */
      public function register_hooks() {
    
        // Bail if CiviCRM not installed yet.
        if (!CIVICRM_INSTALLED) {
          return;
        }
    
        // Bail if WPML is not present.
        if (!defined('ICL_SITEPRESS_VERSION')) {
          return;
        }
    
        // Register WPML compatibility callbacks.
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
        add_action('civicrm_after_rewrite_rules', [$this, 'rewrite_rules'], 10, 2);
        add_filter('icl_ls_languages', [$this, 'basepage_urls_rebuild']);
    
        // Register specific CiviCRM callbacks.
        add_filter('civicrm/basepage/match', [$this, 'basepage_match'], 10, 2);
        add_filter('civicrm/core/url/base', [$this, 'base_url_filter'], 10, 2);
    
        add_filter('civicrm/core/locale', [$this, 'locale_filter'], 10, 2);
    
      }
    
    
    Kevin Cristiano's avatar
    Kevin Cristiano committed
      /**
       * Setup the rewrite rules for WPML.
       *
       * @since 5.75
       *
       * @param bool $flush_rewrite_rules True if rules flushed, false otherwise.
       * @param WP_Post $basepage The Base Page post object.
       */
      public function rewrite_rules($flush_rewrite_rules, $basepage) {
    
        global $sitepress, $wpml_url_filters;
    
        /*
         * Collect all rewrite rules into an array.
         *
         * Because the array of specific Post IDs is added *after* the array of
         * paths for the Base Page ID, those specific rewrite rules will "win" over
         * the more general Base Page rules.
         */
        $collected_rewrites = [];
    
        // Grab information about configuration.
        $wpml_active = apply_filters('wpml_active_languages', NULL);
    
        // Support prefixes for a single Base Page.
        $wpml_url_filters->remove_global_hooks();
        remove_filter('page_link', [$wpml_url_filters, 'page_link_filter'], 1);
        $basepage_url = get_permalink($basepage->ID);
        add_filter('page_link', [$wpml_url_filters, 'page_link_filter'], 1, 2);
        $wpml_url_filters->add_global_hooks();
        foreach ($wpml_active as $slug => $data) {
          $language_url = $sitepress->convert_url($basepage_url, $slug);
          $parsed_url = wp_parse_url($language_url, PHP_URL_PATH);
          $regex_path = substr($parsed_url, 1);
          $collected_rewrites[$basepage->ID][] = $regex_path;
          $post_id = apply_filters('wpml_object_id', $basepage->ID, 'page', FALSE, $slug);
          if (!empty($post_id)) {
            $collected_rewrites[$post_id][] = $regex_path;
          }
        }
    
        // Support prefixes for Base Pages in multiple languages.
        foreach ($wpml_active as $slug => $data) {
    
          // Determine if there is a translation.
          $post_id = apply_filters('wpml_object_id', $basepage->ID, 'page', FALSE, $slug);
          if (empty($post_id)) {
            continue;
          }
    
          // Get the regex path for the unfiltered permalink.
          $wpml_url_filters->remove_global_hooks();
          remove_filter('page_link', [$wpml_url_filters, 'page_link_filter'], 1);
          $url = get_permalink($post_id);
          add_filter('page_link', [$wpml_url_filters, 'page_link_filter'], 1, 2);
          $wpml_url_filters->add_global_hooks();
          $parsed_url = wp_parse_url($url, PHP_URL_PATH);
          $regex_path = substr($parsed_url, 1);
    
          // Get the regex path for the converted permalink.
          $converted_url = $sitepress->convert_url($url, $slug);
          $parsed_url = wp_parse_url($converted_url, PHP_URL_PATH);
          $regex_path_converted = substr($parsed_url, 1);
    
          $collected_rewrites[$basepage->ID][] = $regex_path;
          $collected_rewrites[$basepage->ID][] = $regex_path_converted;
          $collected_rewrites[$post_id][] = $regex_path;
          $collected_rewrites[$post_id][] = $regex_path_converted;
    
        }
    
        // Make collection unique and add remaining rewrite rules.
        $this->rewrites = array_map('array_unique', $collected_rewrites);
        if (!empty($this->rewrites)) {
          foreach ($this->rewrites as $post_id => $rewrite) {
            foreach ($rewrite as $path) {
              add_rewrite_rule(
                '^' . $path . '([^?]*)?',
                'index.php?page_id=' . $post_id . '&civiwp=CiviCRM&q=civicrm%2F$matches[1]',
                'top'
              );
            }
          }
        }
    
        // Maybe force flush.
        if ($flush_rewrite_rules) {
          flush_rewrite_rules();
        }
    
      }
    
      /**
       * Fixes the CiviCRM Base Page URLs in the WPML language switcher.
       *
       * @since 5.75
       *
       * @param array $languages The array of language data for the current query.
       * @return array $languages The modified array of language data for the current query.
       */
      public function basepage_urls_rebuild($languages) {
    
        global $post, $sitepress;
    
        // We need the current Post object.
        if (empty($post) || !($post instanceof WP_Post)) {
          return $languages;
        }
    
        // Skip unless on Base Page.
        if (!civi_wp()->basepage->is_match($post->ID)) {
          return $languages;
        }
    
        // Get the canonical Base Page URL in the current language.
        $permalink = get_permalink($post->ID);
        $canonical = htmlspecialchars_decode(civi_wp()->basepage->basepage_canonical_url($permalink));
    
        // Let's deal with this first.
        foreach ($languages as &$language) {
          if ($language['active']) {
            $language['url'] = $canonical;
          }
        }
    
        // We do not want to filter Base Page URL.
        remove_filter('civicrm/core/url/base', [$this, 'base_url_filter'], 10);
    
        // Now handle remaining languages.
        foreach ($languages as &$language) {
          if ($language['active']) {
            continue;
          }
    
          // WPML will return the "naked" URL to the translated Base Page.
          $language_url = $sitepress->convert_url($canonical, $language['language_code']);
    
          /**
           * A custom callback that returns the translated Base Page URL.
           *
           * @since 5.75
           *
           * @param str $url The URL as built by CiviCRM.
           * @param bool $admin_request True if building an admin URL, false otherwise.
           * @return str $url The URL as modified by WPML.
           */
          $closure = function($url, $admin_request) use (&$language) {
            return $language['url'];
          };
    
          // Use the unmodified canonical URL.
          add_filter('civicrm/core/url/base', $closure, 10, 2);
          $canonical = htmlspecialchars_decode(civi_wp()->basepage->basepage_canonical_url($language_url));
          remove_filter('civicrm/core/url/base', $closure, 10);
          $language['url'] = $canonical;
    
        }
    
        // Reinstate Base Page URL filter.
        add_filter('civicrm/core/url/base', [$this, 'base_url_filter'], 10, 2);
    
        return $languages;
    
      }
    
      /**
       * Checks WPML for CiviCRM Base Page matches.
       *
       * @since 5.75
       *
       * @param bool $is_basepage TRUE if the Post ID matches the Base Page ID, FALSE otherwise.
       * @param int $post_id The WordPress Post ID to check.
       * @return bool $is_basepage TRUE if the Post ID matches the Base Page ID, FALSE otherwise.
       */
      public function basepage_match($is_basepage, $post_id) {
    
        // Bail if this is already the Base Page.
        if ($is_basepage) {
          return $is_basepage;
        }
    
        // Bail if there are no rewrites.
        if (empty($this->rewrites)) {
          return $is_basepage;
        }
    
        // Check rewrites to see if we have a match with this Post ID.
        if (isset($this->rewrites[$post_id]) && !empty($this->rewrites[$post_id])) {
          $is_basepage = TRUE;
        }
    
        return $is_basepage;
    
      }
    
      /**
       * Filters the CiviCRM Base URL for the current language reported by WPML.
       *
       * Only filters URLs that point to the front-end, since WordPress admin URLs are not
       * rewritten by WPML.
       *
       * @since 5.75
       *
       * @param str $url The URL as built by CiviCRM.
       * @param bool $admin_request True if building an admin URL, false otherwise.
       * @return str $url The URL as modified by WPML.
       */
      public function base_url_filter($url, $admin_request) {
    
        global $sitepress, $wpml_url_filters;
    
        // Skip when not defined.
        if (empty($url) || $admin_request) {
          return $url;
        }
    
        // Determine the language code.
        $code = apply_filters('wpml_current_language', NULL);
        if (empty($code)) {
          return $url;
        }
        if ($code === 'all') {
          $code = apply_filters('wpml_default_language', NULL);
        }
    
        // Get the converted URL.
        $language_url = $sitepress->convert_url($url, $code);
    
        // Let's use that if there are no rewrites.
        if (empty($this->rewrites)) {
          return $language_url;
        }
    
        // Let's find the Base Page in the appropriate language.
        $parsed_url = wp_parse_url($language_url, PHP_URL_PATH);
        $regex_path = trailingslashit(substr($parsed_url, 1));
        foreach (array_reverse($this->rewrites, TRUE) as $page_id => $rewrite) {
          if (in_array($regex_path, $rewrite)) {
            $post_id = $page_id;
            break;
          }
        }
    
        // Bail if we don't find the post.
        if (empty($post_id)) {
          return $language_url;
        }
    
        // Get the Base Page permalink in the right language.
        $language_url = get_permalink($post_id);
    
        return $language_url;
    
      }
    
    
      /**
       * Filters the CiviCRM locale for the current language as set by WPML.
       *
       * @since 5.66
       *
       * @param str $locale The locale as reported by WordPress.
       * @return str $locale The locale as modified by Polylang.
       */
      public function locale_filter($locale) {
    
        $languages = apply_filters('wpml_active_languages', NULL);
        foreach ($languages as $language) {
          if ($language['active']) {
            $locale = $language['default_locale'];
            break;
          }
        }
    
        return $locale;
    
      }
    
    }