GravityView  1.19.4
The best, easiest way to display Gravity Forms entries on your website.
class-gravityview-merge-tags.php
Go to the documentation of this file.
1 <?php
2 
3 /**
4  * Enhance Gravity Forms' merge tag functionality by adding additional merge tags
5  * @since 1.8.4
6  */
8 
9  /**
10  * @since 1.8.4
11  */
12  public function __construct() {
13  $this->add_hooks();
14  }
15 
16  /**
17  * Tap in to gform_replace_merge_tags to add merge tags
18  * @since 1.8.4
19  */
20  private function add_hooks() {
21 
22  /** @see GFCommon::replace_variables_prepopulate **/
23  add_filter( 'gform_replace_merge_tags', array( 'GravityView_Merge_Tags', 'replace_gv_merge_tags' ), 10, 7 );
24 
25  // Process after 10 priority
26  add_filter( 'gform_merge_tag_filter', array( 'GravityView_Merge_Tags', 'process_modifiers' ), 20, 5 );
27 
28  }
29 
30  /**
31  * Process custom GravityView modifiers for Merge Tags
32  *
33  * Is not processed on `{all_fields}` Merge Tag.
34  *
35  * @since 1.17
36  *
37  * @param string $value The current merge tag value to be filtered.
38  * @param string $merge_tag If the merge tag being executed is an individual field merge tag (i.e. {Name:3}), this variable will contain the field's ID. If not, this variable will contain the name of the merge tag (i.e. all_fields).
39  * @param string $modifier The string containing any modifiers for this merge tag. For example, "maxwords:10" would be the modifiers for the following merge tag: `{Text:2:maxwords:10}`.
40  * @param GF_Field $field The current field.
41  * @param mixed $raw_value The raw value submitted for this field.
42  *
43  * @return string If no modifiers passed, $raw_value is not a string, or {all_fields} Merge Tag is used, original value. Otherwise, output from modifier methods.
44  */
45  public static function process_modifiers( $value, $merge_tag, $modifier, $field, $raw_value ) {
46 
47  // No modifier was set or the raw value was empty
48  if( 'all_fields' === $merge_tag || '' === $modifier || ! is_string( $raw_value ) || '' === $raw_value ) {
49  return $value;
50  }
51 
52  // matching regex => the value is the method to call to replace the value.
53  $gv_modifiers = array(
54  'maxwords:(\d+)' => 'modifier_maxwords', /** @see modifier_maxwords */
55  'wpautop' => 'modifier_wpautop', /** @see modifier_wpautop */
56  'timestamp' => 'modifier_timestamp', /** @see modifier_timestamp */
57  );
58 
59  $return = $value;
60 
61  foreach ( $gv_modifiers as $gv_modifier => $method ) {
62 
63  // Only match the regex if it's the first modifer; this allows us to enforce our own modifier structure
64  preg_match( '/^' . $gv_modifier .'/ism', $modifier, $matches );
65 
66  if( ! empty( $matches ) ) {
67  // The called method is passed the raw value and the full matches array
68  $return = self::$method( $raw_value, $matches );
69  break;
70  }
71  }
72 
73  return $return;
74  }
75 
76  /**
77  * Convert Date field values to timestamp int
78  *
79  * @since 1.17
80  *
81  * @uses strtotime()
82  *
83  * @param string $raw_value Value to filter
84  * @param array $matches Regex matches group
85  *
86  * @return int Timestamp value of date. `-1` if not a valid timestamp.
87  */
88  private static function modifier_timestamp( $raw_value, $matches ) {
89 
90  if( empty( $matches[0] ) ) {
91  return $raw_value;
92  }
93 
94  $timestamp = strtotime( $raw_value );
95 
96  // Can return false or -1, depending on PHP version.
97  return ( $timestamp && $timestamp > 0 ) ? $timestamp : -1;
98  }
99 
100  /**
101  * Run the Merge Tag value through the wpautop function
102  *
103  * @since 1.17
104  *
105  * @uses wpautop
106  *
107  * @param string $raw_value Value to filter
108  * @param array $matches Regex matches group
109  *
110  * @return string Modified value, if longer than the passed `maxwords` modifier
111  */
112  private static function modifier_wpautop( $raw_value, $matches ) {
113 
114  if( empty( $matches[0] ) || ! function_exists( 'wpautop' ) ) {
115  return $raw_value;
116  }
117 
118  return trim( wpautop( $raw_value ) );
119  }
120 
121  /**
122  * Trim the Merge Tag's length in words.
123  *
124  * Notes:
125  * - HTML tags are preserved
126  * - HTML entities are encoded, but if they are separated by word breaks, they will be counted as words
127  * Example: "one & two" will be counted as three words, but "one& two" will be counted as two words
128  *
129  * @since 1.17
130  *
131  * @param string $raw_value Value to filter
132  * @param array $matches Regex matches group
133  *
134  * @return string Modified value, if longer than the passed `maxwords` modifier
135  */
136  private static function modifier_maxwords( $raw_value, $matches ) {
137 
138  if( ! is_string( $raw_value ) || empty( $matches[1] ) || ! function_exists( 'wp_trim_words' ) ) {
139  return $raw_value;
140  }
141 
142  $max = intval( $matches[1] );
143 
144  $more_placeholder = '[GVMORE]';
145 
146  /**
147  * Use htmlentities instead, so that entities are double-encoded, and decoding restores original values.
148  * @see https://core.trac.wordpress.org/ticket/29533#comment:3
149  */
150  $return = force_balance_tags( wp_specialchars_decode( wp_trim_words( htmlentities( $raw_value ), $max, $more_placeholder ) ) );
151 
152  $return = str_replace( $more_placeholder, '&hellip;', $return );
153 
154  return $return;
155  }
156 
157  /**
158  * Alias for GFCommon::replace_variables()
159  *
160  * Before 1.15.3, it would check for merge tags before passing to Gravity Forms to improve speed.
161  *
162  * @since 1.15.3 - Now that Gravity Forms added speed improvements in 1.9.15, it's more of an alias with a filter
163  * to disable or enable replacements.
164  * @since 1.8.4 - Moved to GravityView_Merge_Tags
165  * @since 1.15.1 - Add support for $url_encode and $esc_html arguments
166  *
167  * @param string $text Text to replace variables in
168  * @param array $form GF Form array
169  * @param array $entry GF Entry array
170  * @param bool $url_encode Pass return value through `url_encode()`
171  * @param bool $esc_html Pass return value through `esc_html()`
172  * @return string Text with variables maybe replaced
173  */
174  public static function replace_variables($text, $form = array(), $entry = array(), $url_encode = false, $esc_html = true ) {
175 
176  /**
177  * @filter `gravityview_do_replace_variables` Turn off merge tag variable replacements.\n
178  * Useful where you want to process variables yourself. We do this in the Math Extension.
179  * @since 1.13
180  *
181  * @param[in,out] boolean $do_replace_variables True: yes, replace variables for this text; False: do not replace variables.
182  * @param[in] string $text Text to replace variables in
183  * @param[in] array $form GF Form array
184  * @param[in] array $entry GF Entry array
185  */
186  $do_replace_variables = apply_filters( 'gravityview/merge_tags/do_replace_variables', true, $text, $form, $entry );
187 
188  if ( strpos( $text, '{' ) === false || ! $do_replace_variables ) {
189  return $text;
190  }
191 
192  /**
193  * Make sure the required keys are set for GFCommon::replace_variables
194  *
195  * @internal Reported to GF Support on 12/3
196  * @internal Fixed in Gravity Forms
197  */
198  $form['title'] = isset( $form['title'] ) ? $form['title'] : '';
199  $form['id'] = isset( $form['id'] ) ? $form['id'] : '';
200  $form['fields'] = isset( $form['fields'] ) ? $form['fields'] : array();
201 
202  return GFCommon::replace_variables( $text, $form, $entry, $url_encode, $esc_html );
203  }
204 
205  /**
206  * Run GravityView filters when using GFCommon::replace_variables()
207  *
208  * Instead of adding multiple hooks, add all hooks into this one method to improve speed
209  *
210  * @since 1.8.4
211  *
212  * @param string $text Text to replace
213  * @param array|bool $form Gravity Forms form array. When called inside {@see GFCommon::replace_variables()} (now deprecated), `false`
214  * @param array|bool $entry Entry array. When called inside {@see GFCommon::replace_variables()} (now deprecated), `false`
215  * @param bool $url_encode Whether to URL-encode output
216  * @param bool $esc_html Whether to apply `esc_html()` to output
217  *
218  * @return mixed
219  */
220  public static function replace_gv_merge_tags( $text, $form = array(), $entry = array(), $url_encode = false, $esc_html = false ) {
221 
222  /**
223  * This prevents the gform_replace_merge_tags filter from being called twice, as defined in:
224  * @see GFCommon::replace_variables()
225  * @see GFCommon::replace_variables_prepopulate()
226  * @todo Remove eventually: Gravity Forms fixed this issue in 1.9.14
227  */
228  if( false === $form ) {
229  return $text;
230  }
231 
232  $text = self::replace_get_variables( $text, $form, $entry, $url_encode );
233 
234  return $text;
235  }
236 
237  /**
238  * Format Merge Tags using GVCommon::format_date()
239  *
240  * @uses GVCommon::format_date()
241  *
242  * @see http://docs.gravityview.co/article/331-date-created-merge-tag for documentation
243  *
244  * @param string $date_created The Gravity Forms date created format
245  * @param string $property Any modifiers for the merge tag (`human`, `format:m/d/Y`)
246  *
247  * @return int|string If timestamp requested, timestamp int. Otherwise, string output.
248  */
249  public static function format_date( $date_created = '', $property = '' ) {
250 
251  // Expand all modifiers, skipping escaped colons. str_replace worked better than preg_split( "/(?<!\\):/" )
252  $exploded = explode( ':', str_replace( '\:', '|COLON|', $property ) );
253 
254  $atts = array(
255  'format' => self::get_format_from_modifiers( $exploded, false ),
256  'human' => in_array( 'human', $exploded ), // {date_created:human}
257  'diff' => in_array( 'diff', $exploded ), // {date_created:diff}
258  'raw' => in_array( 'raw', $exploded ), // {date_created:raw}
259  'timestamp' => in_array( 'timestamp', $exploded ), // {date_created:timestamp}
260  'time' => in_array( 'time', $exploded ), // {date_created:time}
261  );
262 
263  $formatted_date = GVCommon::format_date( $date_created, $atts );
264 
265  return $formatted_date;
266  }
267 
268  /**
269  * If there is a `:format` modifier in a merge tag, grab the formatting
270  *
271  * The `:format` modifier should always have the format follow it; it's the next item in the array
272  * In `foo:format:bar`, "bar" will be the returned format
273  *
274  * @since 1.16
275  *
276  * @param array $exploded Array of modifiers with a possible `format` value
277  * @param string $backup The backup value to use, if not found
278  *
279  * @return string If format is found, the passed format. Otherwise, the backup.
280  */
281  private static function get_format_from_modifiers( $exploded, $backup = '' ) {
282 
283  $return = $backup;
284 
285  $format_key_index = array_search( 'format', $exploded );
286 
287  // If there's a "format:[php date format string]" date format, grab it
288  if ( false !== $format_key_index && isset( $exploded[ $format_key_index + 1 ] ) ) {
289  // Return escaped colons placeholder
290  $return = str_replace( '|COLON|', ':', $exploded[ $format_key_index + 1 ] );
291  }
292 
293  return $return;
294  }
295 
296  /**
297  * Allow passing variables via URL to be displayed in Merge Tags
298  *
299  * Works with `[gvlogic]`:
300  * [gvlogic if="{get:example}" is="false"]
301  * ?example=false
302  * [else]
303  * ?example wasn't "false". It's {get:example}!
304  * [/gvlogic]
305  *
306  * Supports passing arrays:
307  * URL: `example[]=Example+One&example[]=Example+(with+comma)%2C+Two`
308  * Merge Tag: `{get:example}`
309  * Output: `Example One, Example (with comma), Two`
310  *
311  * @since 1.15
312  * @param string $text Text to replace
313  * @param array $form Gravity Forms form array
314  * @param array $entry Entry array
315  * @param bool $url_encode Whether to URL-encode output
316  */
317  public static function replace_get_variables( $text, $form = array(), $entry = array(), $url_encode = false ) {
318 
319  // Is there is {get:[xyz]} merge tag?
320  preg_match_all( "/{get:(.*?)}/ism", $text, $matches, PREG_SET_ORDER );
321 
322  // If there are no matches OR the Entry `created_by` isn't set or is 0 (no user)
323  if( empty( $matches ) ) {
324  return $text;
325  }
326 
327  foreach ( $matches as $match ) {
328 
329  $full_tag = $match[0];
330  $property = $match[1];
331 
332  $value = stripslashes_deep( rgget( $property ) );
333 
334  /**
335  * @filter `gravityview/merge_tags/get/glue/` Modify the glue used to convert an array of `{get}` values from an array to string
336  * @since 1.15
337  * @param[in,out] string $glue String used to `implode()` $_GET values Default: ', '
338  * @param[in] string $property The current name of the $_GET parameter being combined
339  */
340  $glue = apply_filters( 'gravityview/merge_tags/get/glue/', ', ', $property );
341 
342  $value = is_array( $value ) ? implode( $glue, $value ) : $value;
343 
344  $value = $url_encode ? urlencode( $value ) : $value;
345 
346  /**
347  * @filter `gravityview/merge_tags/get/esc_html/{url parameter name}` Disable esc_html() from running on `{get}` merge tag
348  * By default, all values passed through URLs will be escaped for security reasons. If for some reason you want to
349  * pass HTML in the URL, for example, you will need to return false on this filter. It is strongly recommended that you do
350  * not disable this filter.
351  * @since 1.15
352  * @param bool $esc_html Whether to esc_html() the value. Default: `true`
353  */
354  $esc_html = apply_filters('gravityview/merge_tags/get/esc_html/' . $property, true );
355 
356  $value = $esc_html ? esc_html( $value ) : $value;
357 
358  /**
359  * @filter `gravityview/merge_tags/get/esc_html/{url parameter name}` Modify the value of the `{get}` replacement before being used
360  * @param[in,out] string $value Value that will replace `{get}`
361  * @param[in] string $text Text that contains `{get}` (before replacement)
362  * @param[in] array $form Gravity Forms form array
363  * @param[in] array $entry Entry array
364  */
365  $value = apply_filters('gravityview/merge_tags/get/value/' . $property, $value, $text, $form, $entry );
366 
367  $text = str_replace( $full_tag, $value, $text );
368  }
369 
370  unset( $value, $glue, $matches );
371 
372  return $text;
373  }
374 }
375 
static modifier_wpautop($raw_value, $matches)
Run the Merge Tag value through the wpautop function.
Enhance Gravity Forms&#39; merge tag functionality by adding additional merge tags.
static get_format_from_modifiers($exploded, $backup= '')
If there is a :format modifier in a merge tag, grab the formatting.
$merge_tag
Merge tag is already generated by the class.
Definition: widget-poll.php:14
static format_date($date_string= '', $args=array())
Allow formatting date and time based on GravityView standards.
static process_modifiers($value, $merge_tag, $modifier, $field, $raw_value)
Process custom GravityView modifiers for Merge Tags.
static modifier_maxwords($raw_value, $matches)
Trim the Merge Tag&#39;s length in words.
static replace_get_variables($text, $form=array(), $entry=array(), $url_encode=false)
Allow passing variables via URL to be displayed in Merge Tags.
static modifier_timestamp($raw_value, $matches)
Convert Date field values to timestamp int.
static replace_variables($text, $form=array(), $entry=array(), $url_encode=false, $esc_html=true)
Alias for GFCommon::replace_variables()
$entry
Definition: notes.php:27
static format_date($date_created= '', $property= '')
Format Merge Tags using GVCommon::format_date()
add_hooks()
Tap in to gform_replace_merge_tags to add merge tags.
static replace_gv_merge_tags($text, $form=array(), $entry=array(), $url_encode=false, $esc_html=false)
Run GravityView filters when using GFCommon::replace_variables()
$field
Definition: gquiz_grade.php:11