GravityView  1.22.6
The best, easiest way to display Gravity Forms entries on your website.
class-gvlogic-shortcode.php
Go to the documentation of this file.
1 <?php
2 
3 /**
4  * Shortcode to handle showing/hiding content in merge tags. Works great with GravityView Custom Content fields
5  */
7 
8  private static $SUPPORTED_SCALAR_OPERATORS = array( 'is', 'isnot', 'contains', 'starts_with', 'ends_with' );
9 
10  private static $SUPPORTED_NUMERIC_OPERATORS = array( 'greater_than', 'less_than' );
11 
12  private static $SUPPORTED_ARRAY_OPERATORS = array( 'in', 'not_in', 'isnot', 'contains' );
13 
14  private static $SUPPORTED_CUSTOM_OPERATORS = array( 'equals', 'greater_than_or_is', 'greater_than_or_equals', 'less_than_or_is', 'less_than_or_equals', 'not_contains' );
15 
16  /**
17  * Attributes passed to the shortcode
18  * @var array
19  */
21 
22  /**
23  * Content inside the shortcode, displayed if matched
24  * @var string
25  */
27 
28  /**
29  * Parsed attributes
30  * @var array
31  */
32  var $atts = array();
33 
34  /**
35  * Parsed content, shown if matched
36  * @var string
37  */
38  var $content = '';
39 
40  /**
41  * Content shown if not matched
42  * This is set by having `[else]` inside the $content block
43  * @var string
44  */
45  var $else_content = '';
46 
47  /**
48  * The current shortcode name being processed
49  * @var string
50  */
51  var $shortcode = 'gvlogic';
52 
53  /**
54  * The left side of the comparison
55  * @var string
56  */
57  var $if = '';
58 
59  /**
60  * The right side of the comparison
61  * @var string
62  */
63  var $comparison = '';
64 
65  /**
66  * The comparison operator
67  * @var string
68  */
69  var $operation = 'is';
70 
71  /**
72  * Does the comparison pass?
73  * @var bool
74  */
75  var $is_match = false;
76 
77  /**
78  * @var GVLogic_Shortcode
79  */
80  private static $instance;
81 
82  /**
83  * Instantiate!
84  * @return GVLogic_Shortcode
85  */
86  public static function get_instance() {
87 
88  if( empty( self::$instance ) ) {
89  self::$instance = new self;
90  }
91 
92  return self::$instance;
93  }
94 
95  /**
96  * Add the WordPress hooks
97  * @return void
98  */
99  private function __construct() {
100  $this->add_hooks();
101  }
102 
103  /**
104  * Register the shortcode
105  * @return void
106  */
107  private function add_hooks() {
108  add_shortcode( 'gvlogic', array( $this, 'shortcode' ) );
109  add_shortcode( 'gvlogicelse', array( $this, 'shortcode' ) );
110  }
111 
112  /**
113  * Get array of supported operators
114  * @param bool $with_values
115  *
116  * @return array
117  */
118  private function get_operators( $with_values = false ) {
119 
120  $operators = array_merge( self::$SUPPORTED_ARRAY_OPERATORS, self::$SUPPORTED_NUMERIC_OPERATORS, self::$SUPPORTED_SCALAR_OPERATORS, self::$SUPPORTED_CUSTOM_OPERATORS );
121 
122  if( $with_values ) {
123  $operators_with_values = array();
124  foreach( $operators as $key ) {
125  $operators_with_values[ $key ] = '';
126  }
127  return $operators_with_values;
128  } else {
129  return $operators;
130  }
131  }
132 
133  /**
134  * Set the operation for the shortcode.
135  * @param string $operation
136  *
137  * @return bool True: it's an allowed operation type and was added. False: invalid operation type
138  */
139  private function set_operation( $operation = '' ) {
140 
141  if( empty( $operation ) ) {
142  return false;
143  }
144 
145  $operators = $this->get_operators( false );
146 
147  if( !in_array( $operation, $operators ) ) {
148  do_action( 'gravityview_log_debug', __METHOD__ .' Attempted to add invalid operation type.', $operation );
149  return false;
150  }
151 
152  $this->operation = $operation;
153  return true;
154  }
155 
156  /**
157  * Set the operation and comparison for the shortcode
158  *
159  * Loop through each attribute passed to the shortcode and see if it's a valid operator. If so, set it.
160  * Example: [gvlogic if="{example}" greater_than="5"]
161  * `greater_than` will be set as the operator
162  * `5` will be set as the comparison value
163  *
164  * @return bool True: we've got an operation and comparison value; False: no, we don't
165  */
166  private function setup_operation_and_comparison() {
167 
168  foreach( $this->atts as $key => $value ) {
169 
170  $valid = $this->set_operation( $key );
171 
172  if( $valid ) {
173  $this->comparison = $value;
174  return true;
175  }
176  }
177 
178  return false;
179  }
180 
181  /**
182  * @param array $atts User defined attributes in shortcode tag.
183  * @param null $content
184  * @param string $shortcode_tag
185  *
186  * @return string|null
187  */
188  public function shortcode( $atts = array(), $content = NULL, $shortcode_tag = '' ) {
189 
190  // Don't process except on frontend
191  if ( defined( 'GRAVITYVIEW_FUTURE_CORE_LOADED' ) && gravityview()->request->is_admin() ) {
192  return null;
193  /** Deprecated in favor of gravityview()->request->is_admin(). */
194  } else if ( GravityView_Plugin::is_admin() ) {
195  return null;
196  }
197 
198  if( empty( $atts ) ) {
199  do_action( 'gravityview_log_error', __METHOD__.' $atts are empty.', $atts );
200  return null;
201  }
202 
203  $this->passed_atts = $atts;
204  $this->passed_content = $content;
205  $this->content = '';
206  $this->else_content = '';
207  $this->atts = array();
208  $this->shortcode = $shortcode_tag;
209 
210  $this->parse_atts();
211 
212  // We need an "if"
213  if( false === $this->if ) {
214  do_action( 'gravityview_log_error', __METHOD__.' $atts->if is empty.', $this->passed_atts );
215  return null;
216  }
217 
218  $setup = $this->setup_operation_and_comparison();
219 
220  // We need an operation and comparison value
221  if( ! $setup ) {
222  do_action( 'gravityview_log_error', __METHOD__.' No valid operators were passed.', $this->atts );
223  return null;
224  }
225 
226  // Check if it's a match
227  $this->set_is_match();
228 
229  // Set the content and else_content
231 
232  // Return the value!
233  $output = $this->get_output();
234 
235  return $output;
236  }
237 
238  /**
239  * Does the if and the comparison match?
240  * @uses GVCommon::matches_operation
241  *
242  * @return void
243  */
244  private function set_is_match() {
245  $this->is_match = GVCommon::matches_operation( $this->if, $this->comparison, $this->operation );
246  }
247 
248  /**
249  * Get the output for the shortcode, based on whether there's a matched value
250  *
251  * @return string HTML/Text output of the shortcode
252  */
253  private function get_output() {
254 
255  if( $this->is_match ) {
257  } else {
259  }
260 
261  // Get recursive!
262  $output = do_shortcode( $output );
263 
264  if ( class_exists( 'GFCommon' ) ) {
265  $output = GFCommon::replace_variables( $output, array(), array(), false, true, false );
266  }
267 
268  /**
269  * @filter `gravityview/gvlogic/output` Modify the [gvlogic] output
270  * @param string $output HTML/text output
271  * @param GVLogic_Shortcode $this This class
272  */
273  $output = apply_filters('gravityview/gvlogic/output', $output, $this );
274 
275  do_action( 'gravityview_log_debug', __METHOD__ .' Output: ', $output );
276 
277  return $output;
278  }
279 
280  /**
281  * Check for `[else]` tag inside the shortcode content. If exists, set the else_content variable.
282  * If not, use the `else` attribute passed by the shortcode, if exists.
283  *
284  * @return void
285  */
286  private function set_content_and_else_content() {
287 
288  $passed_content = $this->passed_content;
289 
290  list( $before_else, $after_else ) = array_pad( explode( '[else]', $passed_content ), 2, NULL );
291  list( $before_else_if, $after_else_if ) = array_pad( explode( '[else', $passed_content ), 2, NULL );
292 
293  $else_attr = isset( $this->atts['else'] ) ? $this->atts['else'] : NULL;
294  $else_content = isset( $after_else ) ? $after_else : $else_attr;
295 
296  // The content is everything OTHER than the [else]
297  $this->content = $before_else_if;
298 
299  if ( ! $this->is_match ) {
300  if( $elseif_content = $this->process_elseif( $before_else ) ) {
301  $this->else_content = $elseif_content;
302  } else {
303  $this->else_content = $else_content;
304  }
305  }
306  }
307 
308  /**
309  * Handle additional conditional logic inside the [else] pseudo-shortcode
310  *
311  * @since 1.21.2
312  *
313  * @param string $before_else Shortcode content before the [else] tag (if it exists)
314  *
315  * @return bool|string False: No [else if] statements found. Otherwise, return the matched content.
316  */
317  private function process_elseif( $before_else ) {
318 
319  $regex = get_shortcode_regex( array( 'else' ) );
320 
321  // 2. Check if there are any ELSE IF statements
322  preg_match_all( '/' . $regex . '/', $before_else . '[/else]', $else_if_matches, PREG_SET_ORDER );
323 
324  // 3. The ELSE IF statements that remain should be processed to see if they are valid
325  foreach ( $else_if_matches as $key => $else_if_match ) {
326 
327  // If $else_if_match[5] exists and has content, check for more shortcodes
328  preg_match_all( '/' . $regex . '/', $else_if_match[5] . '[/else]', $recursive_matches, PREG_SET_ORDER );
329 
330  // If the logic passes, this is the value that should be used for $this->else_content
331  $else_if_value = $else_if_match[5];
332  $check_elseif_match = $else_if_match[0];
333 
334  // Retrieve the value of the match that is currently being checked, without any other [else] tags
335  if( ! empty( $recursive_matches[0][0] ) ) {
336  $else_if_value = str_replace( $recursive_matches[0][0], '', $else_if_value );
337  $check_elseif_match = str_replace( $recursive_matches[0][0], '', $check_elseif_match );
338  }
339 
340  $check_elseif_match = str_replace( '[else', '[gvlogicelse', $check_elseif_match );
341  $check_elseif_match = str_replace( '[/else', '[/gvlogicelse', $check_elseif_match );
342 
343  // Make sure to close the tag
344  if ( '[/gvlogicelse]' !== substr( $check_elseif_match, -14, 14 ) ) {
345  $check_elseif_match .= '[/gvlogicelse]';
346  }
347 
348  // The shortcode returned a value; it was a match
349  if ( $result = do_shortcode( $check_elseif_match ) ) {
350  return $else_if_value;
351  }
352 
353  // Process any remaining [else] tags
354  return $this->process_elseif( $else_if_match[5] );
355  }
356 
357  return false;
358  }
359 
360  /**
361  * Process the attributes passed to the shortcode. Make sure they're valid
362  * @return void
363  */
364  private function parse_atts() {
365 
366  $supported = array(
367  'if' => false,
368  'else' => false,
369  );
370 
371  $supported_args = $supported + $this->get_operators( true );
372 
373  // Whittle down the attributes to only valid pairs
374  $this->atts = shortcode_atts( $supported_args, $this->passed_atts, $this->shortcode );
375 
376  // Only keep the passed attributes after making sure that they're valid pairs
377  $this->atts = function_exists( 'array_intersect_key' ) ? array_intersect_key( $this->passed_atts, $this->atts ) : $this->atts;
378 
379  // Strip whitespace if it's not default false
380  $this->if = ( isset( $this->atts['if'] ) && is_string( $this->atts['if'] ) ) ? trim( $this->atts['if'] ) : false;
381 
382  /**
383  * @action `gravityview/gvlogic/parse_atts/after` Modify shortcode attributes after it's been parsed
384  * @see https://gist.github.com/zackkatz/def9b295b80c4ae109760ffba200f498 for an example
385  * @since 1.21.5
386  * @param GVLogic_Shortcode $this The GVLogic_Shortcode instance
387  */
388  do_action( 'gravityview/gvlogic/parse_atts/after', $this );
389 
390  // Make sure the "if" isn't processed in self::setup_operation_and_comparison()
391  unset( $this->atts['if'] );
392  }
393 }
394 
shortcode( $atts=array(), $content=NULL, $shortcode_tag='')
get_operators( $with_values=false)
Get array of supported operators.
parse_atts()
Process the attributes passed to the shortcode.
add_hooks()
Register the shortcode.
__construct()
Add the WordPress hooks.
static matches_operation( $val1, $val2, $operation)
Wrapper for the GFFormsModel::matches_operation() method that adds additional comparisons, including: &#39;equals&#39;, &#39;greater_than_or_is&#39;, &#39;greater_than_or_equals&#39;, &#39;less_than_or_is&#39;, &#39;less_than_or_equals&#39;, &#39;not_contains&#39;, &#39;in&#39;, and &#39;not_in&#39;.
set_operation( $operation='')
Set the operation for the shortcode.
static get_instance()
Instantiate!
process_elseif( $before_else)
Handle additional conditional logic inside the [else] pseudo-shortcode.
set_content_and_else_content()
Check for [else] tag inside the shortcode content.
static is_admin()
Check if is_admin(), and make sure not DOING_AJAX.
set_is_match()
Does the if and the comparison match? GVCommon::matches_operation.
setup_operation_and_comparison()
Set the operation and comparison for the shortcode.
gravityview()
The main GravityView wrapper function.
Shortcode to handle showing/hiding content in merge tags.
get_output()
Get the output for the shortcode, based on whether there&#39;s a matched value.