Merge pull request #22335 from braders/participant-static-method
[civicrm-core.git] / CRM / Mailing / BAO / TrackableURL.php
index 0113e53bb9b7cbd063c3ff63ed184369f0455515..7026e718f7932095da216f3f6b1e6e9a0ed8e7a1 100644 (file)
@@ -38,7 +38,15 @@ class CRM_Mailing_BAO_TrackableURL extends CRM_Mailing_DAO_TrackableURL {
    *   The redirect/tracking url
    */
   public static function getTrackerURL($url, $mailing_id, $queue_id) {
+    if (strpos($url, '{') !== FALSE) {
+      return self::getTrackerURLForUrlWithTokens($url, $mailing_id, $queue_id);
+    }
+    else {
+      return self::getBasicTrackerURL($url, $mailing_id, $queue_id);
+    }
+  }
 
+  private static function getBasicTrackerURL($url, $mailing_id, $queue_id) {
     static $urlCache = [];
 
     if (array_key_exists($mailing_id . $url, $urlCache)) {
@@ -46,10 +54,11 @@ class CRM_Mailing_BAO_TrackableURL extends CRM_Mailing_DAO_TrackableURL {
     }
 
     // hack for basic CRM-1014 and CRM-1151 and CRM-3492 compliance:
-    // let's not replace possible image URLs and CiviMail ones
+    // let's not replace possible image URLs, CiviMail URLs or internal anchor URLs
     if (preg_match('/\.(png|jpg|jpeg|gif|css)[\'"]?$/i', $url)
       or substr_count($url, 'civicrm/extern/')
       or substr_count($url, 'civicrm/mailing/')
+      or ($url[0] === '#')
     ) {
       // let's not cache these, so they don't get &qid= appended to them
       return $url;
@@ -87,6 +96,66 @@ class CRM_Mailing_BAO_TrackableURL extends CRM_Mailing_DAO_TrackableURL {
     return $returnUrl;
   }
 
+  /**
+   * Create a trackable URL for a URL with tokens.
+   *
+   * @param string $url
+   * @param int $mailing_id
+   * @param int|string $queue_id
+   *
+   * @return string
+   */
+  private static function getTrackerURLForUrlWithTokens($url, $mailing_id, $queue_id) {
+
+    // Parse the URL.
+    // (not using parse_url because it's messy to reassemble)
+    if (!preg_match('/^([^?#]+)([?][^#]*)?(#.*)?$/', $url, $parsed)) {
+      // Failed to parse it, give up and don't track it.
+      return $url;
+    }
+
+    // If we have a token in the URL + path section, we can't tokenise.
+    if (strpos($parsed[1], '{') !== FALSE) {
+      return $url;
+    }
+
+    $trackable_url = $parsed[1];
+
+    // Process the query parameters, if there are any.
+    $tokenised_params = [];
+    $static_params = [];
+    if (!empty($parsed[2])) {
+      $query_key_value_pairs = explode('&', substr($parsed[2], 1));
+
+      // Separate the tokenised from the static parts.
+      foreach ($query_key_value_pairs as $_) {
+        if (strpos($_, '{') === FALSE) {
+          $static_params[] = $_;
+        }
+        else {
+          $tokenised_params[] = $_;
+        }
+      }
+      // Add the static params to the trackable part.
+      if ($static_params) {
+        $trackable_url .= '?' . implode('&', $static_params);
+      }
+    }
+
+    // Get trackable URL.
+    $data = self::getBasicTrackerURL($trackable_url, $mailing_id, $queue_id);
+
+    // Append the tokenised bits and the fragment.
+    if ($tokenised_params) {
+      // We know the URL will already have the '?'
+      $data .= '&' . implode('&', $tokenised_params);
+    }
+    if (!empty($parsed[3])) {
+      $data .= $parsed[3];
+    }
+    return $data;
+  }
+
   /**
    * @param $url
    * @param $mailing_id