Completed
Push — develop ( 78978d...6baaed )
by
unknown
04:57
created

GravityView_Field::is_choice_value_enabled()   B

Complexity

Conditions 4
Paths 6

Size

Total Lines 26
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 20

Importance

Changes 0
Metric Value
cc 4
eloc 13
nc 6
nop 0
dl 0
loc 26
ccs 0
cts 14
cp 0
crap 20
rs 8.5806
c 0
b 0
f 0
1
<?php
2
/**
3
 * @file class-gravityview-field.php
4
 * @package GravityView
5
 * @subpackage includes\fields
6
 */
7
8
/**
9
 * Modify field settings by extending this class.
10
 */
11
abstract class GravityView_Field {
12
13
	/**
14
	 * The name of the GravityView field type
15
	 * Example: `created_by`, `text`, `fileupload`, `address`, `entry_link`
16
	 * @var string
17
	 */
18
	public $name;
19
20
	/**
21
	 * @internal Not yet implemented
22
	 * @since 1.15.2
23
	 * @type string The description of the field in the field picker
24
	 */
25
	public $description;
26
27
	/**
28
	 * @since 1.15.2
29
	 * @type string The label of the field in the field picker
30
	 */
31
	public $label;
32
33
	/**
34
	 * @var string The default search label used by the search widget, if not set
35
	 */
36
	public $default_search_label;
37
38
	/**
39
	 * `standard`, `advanced`, `post`, `pricing`, `meta`, `gravityview`
40
	 * @since 1.15.2
41
	 * @type string The group belongs to this field in the field picker
42
	 */
43
	public $group;
44
45
	/**
46
	 * @internal Not yet implemented
47
	 * @type boolean Can the field be searched?
48
	 * @since 1.15.2
49
	 */
50
	public $is_searchable = true;
51
52
	/**
53
	 * @internal Not yet implemented
54
	 * @type array $search_operators The type of search operators available for this field
55
	 * @since 1.15.2
56
	 */
57
	public $search_operators;
58
59
	/**
60
	 * @type boolean Can the field be sorted in search?
61
	 * @since 1.15.2
62
	 */
63
	public $is_sortable = true;
64
65
	/**
66
	 * @type boolean Is field content number-based?
67
	 * @since 1.15.2
68
	 */
69
	public $is_numeric;
70
71
	/**
72
	 * @var null|string The key used to search and sort entry meta in Gravity Forms. Used if the field stores data as custom entry meta.
73
	 * @see https://www.gravityhelp.com/documentation/article/gform_entry_meta/
74
	 * @since TODO
75
	 */
76
	public $entry_meta_key = null;
77
78
	/**
79
	 * @var string|array Optional. The callback function after entry meta is updated, only used if $entry_meta_key is set.
80
	 * @see https://www.gravityhelp.com/documentation/article/gform_entry_meta/
81
	 * @since TODO
82
	 */
83
	var $entry_meta_update_callback = null;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $entry_meta_update_callback.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
84
85
	/**
86
	 * @var bool Whether to show meta when set to true automatically adds the column to the entry list, without having to edit and add the column for display
87
	 * @since TODO
88
	 */
89
	var $entry_meta_is_default_column = false;
0 ignored issues
show
Coding Style introduced by
The visibility should be declared for property $entry_meta_is_default_column.

The PSR-2 coding standard requires that all properties in a class have their visibility explicitly declared. If you declare a property using

class A {
    var $property;
}

the property is implicitly global.

To learn more about the PSR-2, please see the PHP-FIG site on the PSR-2.

Loading history...
90
91
	/**
92
	 * @internal Not yet implemented
93
	 * @todo implement supports_context() method
94
	 * The contexts in which a field is available. Some fields aren't editable, for example.
95
	 * - `singular` is an alias for both `single` and `edit`
96
	 * - `multiple` is an alias for `directory` (backward compatibility)
97
	 * @type array
98
	 * @since 1.15.2
99
	 */
100
	public $contexts = array( 'single', 'multiple', 'edit', 'export' );
101
102
	/**
103
	 * @since 1.15.2
104
	 * @since 1.16.2.2 Changed access to public (previously, protected)
105
	 * @type string The name of a corresponding Gravity Forms GF_Field class, if exists
106
	 */
107
	public $_gf_field_class_name;
108
109
	/**
110
	 * @var string The field ID being requested
111
	 * @since 1.14
112
	 */
113
	protected $_field_id = '';
114
115
	/**
116
	 * @var string Field options to be rendered
117
	 * @since 1.14
118
	 */
119
	protected $_field_options = array();
120
121
	/**
122
	 * @var bool|string Name of merge tag (without curly brackets), if the field has custom GravityView merge tags to add. Otherwise, false.
123
	 * @since 1.16
124
	 */
125
	protected $_custom_merge_tag = false;
126
127
	/**
128
	 * GravityView_Field constructor.
129
	 */
130
	public function __construct() {
131
132
		// Modify the field options based on the name of the field type
133
		add_filter( sprintf( 'gravityview_template_%s_options', $this->name ), array( &$this, 'field_options' ), 10, 5 );
134
135
		add_filter( 'gravityview/sortable/field_blacklist', array( $this, '_filter_sortable_fields' ), 1 );
136
137
		if( $this->entry_meta_key ) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $this->entry_meta_key of type null|string is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
138
			add_filter( 'gform_entry_meta', array( $this, 'add_entry_meta' ) );
139
			add_filter( 'gravityview/common/sortable_fields', array( $this, 'add_sortable_field' ), 10, 2 );
140
		}
141
142
		if( $this->_custom_merge_tag ) {
143
			add_filter( 'gform_custom_merge_tags', array( $this, '_filter_gform_custom_merge_tags' ), 10, 4 );
144
			add_filter( 'gform_replace_merge_tags', array( $this, '_filter_gform_replace_merge_tags' ), 10, 7 );
145
		}
146
147
		if( 'meta' === $this->group || '' !== $this->default_search_label ) {
148
			add_filter( 'gravityview_search_field_label', array( $this, 'set_default_search_label' ), 10, 3 );
149
		}
150
151
		/**
152
		 * Auto-assign label from Gravity Forms label, if exists
153
		 * @since 1.20
154
		 */
155
		if( empty( $this->label ) && ! empty( $this->_gf_field_class_name ) && class_exists( $this->_gf_field_class_name ) ) {
156
			$this->label = ucfirst( GF_Fields::get( $this->name )->get_form_editor_field_title() );
157
		}
158
159
		GravityView_Fields::register( $this );
160
	}
161
162
	/**
163
	 * Add the field to the Filter & Sort available fields
164
	 *
165
	 * @since TODO
166
	 *
167
	 * @param array $fields Sub-set of GF form fields that are sortable
168
	 *
169
	 * @return array Modified $fields array to include approval status in the sorting dropdown
170
	 */
171
	public function add_sortable_field( $fields ) {
172
173
		$added_field = array(
174
			'label' => $this->label,
175
			'type'  => $this->name
0 ignored issues
show
introduced by
Each line in an array declaration must end in a comma
Loading history...
176
		);
177
178
		$fields["{$this->entry_meta_key}"] = $added_field;
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
179
180
		return $fields;
181
	}
182
183
	/**
184
	 * Allow setting a default search label for search fields based on the field type
185
	 *
186
	 * Useful for entry meta "fields" that don't have Gravity Forms labels, like `created_by`
187
	 *
188
	 * @since 1.17.3
189
	 *
190
	 * @param string $label Existing label text, sanitized.
191
	 * @param array $gf_field Gravity Forms field array, as returned by `GFFormsModel::get_field()`
192
	 * @param array $field Field setting as sent by the GV configuration - has `field`, `input` (input type), and `label` keys
193
	 *
194
	 * @return string
195
	 */
196
	function set_default_search_label( $label = '', $gf_field = null, $field = array() ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
197
198
		if( $this->name === $field['field'] && '' === $label ) {
199
			$label = esc_html( $this->default_search_label );
200
		}
201
202
		return $label;
203
	}
204
205
	/**
206
	 * Match the merge tag in replacement text for the field.  DO NOT OVERRIDE.
207
	 *
208
	 * @see replace_merge_tag Override replace_merge_tag() to handle any matches
209
	 *
210
	 * @since 1.16
211
	 *
212
	 * @param string $text Text to replace
213
	 * @param array $form Gravity Forms form array
214
	 * @param array $entry Entry array
215
	 * @param bool $url_encode Whether to URL-encode output
216
	 *
217
	 * @return string Original text if {_custom_merge_tag} isn't found. Otherwise, replaced text.
218
	 */
219
	public function _filter_gform_replace_merge_tags( $text, $form = array(), $entry = array(), $url_encode = false, $esc_html = false  ) {
220
221
		// Is there is field merge tag? Strip whitespace off the ned, too.
222
		preg_match_all( '/{' . preg_quote( $this->_custom_merge_tag ) . ':?(.*?)(?:\s)?}/ism', $text, $matches, PREG_SET_ORDER );
223
224
		// If there are no matches, return original text
225
		if ( empty( $matches ) ) {
226
			return $text;
227
		}
228
229
		return $this->replace_merge_tag( $matches, $text, $form, $entry, $url_encode, $esc_html );
230
	}
231
232
	/**
233
	 * Run GravityView filters when using GFCommon::replace_variables()
234
	 *
235
	 * Instead of adding multiple hooks, add all hooks into this one method to improve speed
236
	 *
237
	 * @since 1.8.4
238
	 *
239
	 * @param array $matches Array of Merge Tag matches found in text by preg_match_all
240
	 * @param string $text Text to replace
241
	 * @param array|bool $form Gravity Forms form array. When called inside {@see GFCommon::replace_variables()} (now deprecated), `false`
242
	 * @param array|bool $entry Entry array.  When called inside {@see GFCommon::replace_variables()} (now deprecated), `false`
243
	 * @param bool $url_encode Whether to URL-encode output
244
	 * @param bool $esc_html Whether to apply `esc_html()` to output
245
	 *
246
	 * @return mixed
247
	 */
248 1
	public function replace_merge_tag( $matches = array(), $text = '', $form = array(), $entry = array(), $url_encode = false, $esc_html = false ) {
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
249
250 1
		foreach( $matches as $match ) {
251
252 1
			$full_tag = $match[0];
253
254
			// Strip the Merge Tags
255 1
			$tag = str_replace( array( '{', '}'), '', $full_tag );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
256
257
			// Replace the value from the entry, if exists
258 1
			if( isset( $entry[ $tag ] ) ) {
259
260 1
				$value = $entry[ $tag ];
261
262 1
				if( is_callable( array( $this, 'get_content') ) ) {
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
263 1
					$value = $this->get_content( $value );
0 ignored issues
show
Bug introduced by
It seems like you code against a specific sub-type and not the parent class GravityView_Field as the method get_content() does only exist in the following sub-classes of GravityView_Field: GravityView_Field_Date_Created, GravityView_Field_Is_Fulfilled, GravityView_Field_Payment_Amount, GravityView_Field_Payment_Date, GravityView_Field_Transaction_Type. Maybe you want to instanceof check for one of these explicitly?

Let’s take a look at an example:

abstract class User
{
    /** @return string */
    abstract public function getPassword();
}

class MyUser extends User
{
    public function getPassword()
    {
        // return something
    }

    public function getDisplayName()
    {
        // return some name.
    }
}

class AuthSystem
{
    public function authenticate(User $user)
    {
        $this->logger->info(sprintf('Authenticating %s.', $user->getDisplayName()));
        // do something.
    }
}

In the above example, the authenticate() method works fine as long as you just pass instances of MyUser. However, if you now also want to pass a different sub-classes of User which does not have a getDisplayName() method, the code will break.

Available Fixes

  1. Change the type-hint for the parameter:

    class AuthSystem
    {
        public function authenticate(MyUser $user) { /* ... */ }
    }
    
  2. Add an additional type-check:

    class AuthSystem
    {
        public function authenticate(User $user)
        {
            if ($user instanceof MyUser) {
                $this->logger->info(/** ... */);
            }
    
            // or alternatively
            if ( ! $user instanceof MyUser) {
                throw new \LogicException(
                    '$user must be an instance of MyUser, '
                   .'other instances are not supported.'
                );
            }
    
        }
    }
    
Note: PHP Analyzer uses reverse abstract interpretation to narrow down the types inside the if block in such a case.
  1. Add the method to the parent class:

    abstract class User
    {
        /** @return string */
        abstract public function getPassword();
    
        /** @return string */
        abstract public function getDisplayName();
    }
    
Loading history...
264 1
				}
265
266 1
				$text = str_replace( $full_tag, $value, $text );
267 1
			}
268 1
		}
269
270 1
		unset( $value, $tag, $full_tag );
271
272 1
		return $text;
273
	}
274
275
	/**
276
	 * Add custom merge tags to merge tag options. DO NOT OVERRIDE.
277
	 *
278
	 * @internal Not to be overridden by fields
279
	 *
280
	 * @since 1.8.4
281
	 *
282
	 * @param array $custom_merge_tags
283
	 * @param int $form_id GF Form ID
284
	 * @param GF_Field[] $fields Array of fields in the form
285
	 * @param string $element_id The ID of the input that Merge Tags are being used on
286
	 *
287
	 * @return array Modified merge tags
288
	 */
289
	public function _filter_gform_custom_merge_tags( $custom_merge_tags = array(), $form_id, $fields = array(), $element_id = '' ) {
0 ignored issues
show
Unused Code introduced by
The parameter $element_id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
290
291
		$form = GVCommon::get_form( $form_id );
292
293
		$field_merge_tags = $this->custom_merge_tags( $form, $fields );
294
295
		return array_merge( $custom_merge_tags, $field_merge_tags );
296
	}
297
298
	/**
299
	 * Add custom Merge Tags to Merge Tag options, if custom Merge Tags exist
300
	 *
301
	 * Should be overridden if there's more than one Merge Tag to add or if the Merge Tag isn't {_custom_merge_tag}
302
	 *
303
	 * @since 1.16
304
	 *
305
	 * @param array $form GF Form array
306
	 * @param GF_Field[] $fields Array of fields in the form
307
	 *
308
	 * @return array Merge tag array with `label` and `tag` keys based on class `label` and `_custom_merge_tag` variables
309
	 */
310
	protected function custom_merge_tags( $form = array(), $fields = array() ) {
0 ignored issues
show
Unused Code introduced by
The parameter $form is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $fields is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
311
312
		// Use variables to make it unnecessary for other fields to override
313
		$merge_tags = array(
314
			array(
315
				'label' => $this->label,
316
				'tag' => '{' . $this->_custom_merge_tag . '}',
317
			),
318
		);
319
320
		return $merge_tags;
321
	}
322
323
	/**
324
	 * Use field settings to modify whether a field is sortable
325
	 *
326
	 * @see GravityView_frontend::is_field_sortable
327
	 * @since 1.15.3
328
	 *
329
	 * @param array $not_sortable Existing field types that aren't sortable
330
	 *
331
	 * @return array
332
	 */
333
	public function _filter_sortable_fields( $not_sortable ) {
334
335
		if( ! $this->is_sortable ) {
336
			$not_sortable[] = $this->name;
337
		}
338
339
		return $not_sortable;
340
	}
341
342
	/**
343
	 * Add the custom entry meta key to make it searchable and sortable
344
	 *
345
	 * @see https://www.gravityhelp.com/documentation/article/gform_entry_meta/
346
	 *
347
	 * @param array $entry_meta Array of custom entry meta keys with associative arrays
348
	 *
349
	 * @return mixed
350
	 */
351
	function add_entry_meta( $entry_meta ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
352
353
		if( ! isset( $entry_meta["{$this->entry_meta_key}"] ) ) {
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
354
355
			$added_meta = array(
356
				'label'             => $this->label,
357
				'is_numeric'        => $this->is_numeric,
358
				'is_default_column' => $this->entry_meta_is_default_column,
359
			);
360
361
			if ( $this->entry_meta_update_callback && is_callable( $this->entry_meta_update_callback ) ) {
362
				$added_meta['update_entry_meta_callback'] = $this->entry_meta_update_callback;
363
			}
364
365
			$entry_meta["{$this->entry_meta_key}"] = $added_meta;
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
366
367
		} else {
368
			do_action( 'gravityview_log_error', __METHOD__ . ' Entry meta already set: ' . $this->entry_meta_key, $entry_meta["{$this->entry_meta_key}"] );
0 ignored issues
show
introduced by
Array keys should be surrounded by spaces unless they contain a string or an integer.
Loading history...
369
		}
370
371
		return $entry_meta;
372
	}
373
374
	private function field_support_options() {
375
		$options = array(
376
			'link_to_post' => array(
377
				'type' => 'checkbox',
378
				'label' => __( 'Link to the post', 'gravityview' ),
379
				'desc' => __( 'Link to the post created by the entry.', 'gravityview' ),
380
				'value' => false,
381
			),
382
			'link_to_term' => array(
383
				'type' => 'checkbox',
384
				'label' => __( 'Link to the category or tag', 'gravityview' ),
385
				'desc' => __( 'Link to the current category or tag. "Link to single entry" must be unchecked.', 'gravityview' ),
386
				'value' => false,
387
			),
388
			'dynamic_data' => array(
389
				'type' => 'checkbox',
390
				'label' => __( 'Use the live post data', 'gravityview' ),
391
				'desc' => __( 'Instead of using the entry data, instead use the current post data.', 'gravityview' ),
392
				'value' => true,
393
			),
394
			'date_display' => array(
395
				'type' => 'text',
396
				'label' => __( 'Override Date Format', 'gravityview' ),
397
				'desc' => sprintf( __( 'Define how the date is displayed (using %sthe PHP date format%s)', 'gravityview'), '<a href="https://codex.wordpress.org/Formatting_Date_and_Time">', '</a>' ),
0 ignored issues
show
Coding Style introduced by
Expected 1 spaces before closing bracket; 0 found
Loading history...
398
				/**
399
				 * @filter `gravityview_date_format` Override the date format with a [PHP date format](https://codex.wordpress.org/Formatting_Date_and_Time)
400
				 * @param[in,out] null|string $date_format Date Format (default: null)
401
				 */
402
				'value' => apply_filters( 'gravityview_date_format', null )
403
			),
404
			'new_window' => array(
405
				'type' => 'checkbox',
406
				'label' => __( 'Open link in a new tab or window?', 'gravityview' ),
407
				'value' => false,
408
			),
409
		);
410
411
		/**
412
		 * @filter `gravityview_field_support_options` Modify the settings that a field supports
413
		 * @param array $options Options multidimensional array with each key being the input name, with each array setting having `type`, `label`, `desc` and `value` (default values) keys
414
		 */
415
		return apply_filters( 'gravityview_field_support_options', $options );
416
	}
417
418
	function add_field_support( $key = '', &$field_options ) {
0 ignored issues
show
Best Practice introduced by
It is generally recommended to explicitly declare the visibility for methods.

Adding explicit visibility (private, protected, or public) is generally recommend to communicate to other developers how, and from where this method is intended to be used.

Loading history...
419
420
		$options = $this->field_support_options();
421
422
		if( isset( $options[ $key ] ) ) {
423
			$field_options[ $key ] = $options[ $key ];
424
		}
425
426
		return $field_options;
427
	}
428
429
	/**
430
	 * Tap in here to modify field options.
431
	 *
432
	 * Here's an example:
433
	 *
434
	 * <pre>
435
	 * $field_options['name_display'] = array(
436
	 * 	'type' => 'select',
437
	 * 	'label' => __( 'User Format', 'gravityview' ),
438
	 * 	'desc' => __( 'How should the User information be displayed?', 'gravityview'),
439
	 * 	'choices' => array(
440
	 * 		array(
441
	 *		 	'value' => 'display_name',
442
	 *		  	'label' => __('Display Name (Example: "Ellen Ripley")', 'gravityview'),
443
	 *		),
444
	 *  	array(
445
	 *			'value' => 'user_login',
446
	 *			'label' => __('Username (Example: "nostromo")', 'gravityview')
447
	 *		),
448
	 * 	 'value' => 'display_name'
449
	 * );
450
	 * </pre>
451
	 *
452
	 * @param  array      $field_options [description]
453
	 * @param  string      $template_id   [description]
454
	 * @param  string      $field_id      [description]
455
	 * @param  string      $context       [description]
456
	 * @param  string      $input_type    [description]
457
	 * @return array                     [description]
458
	 */
459
	public function field_options( $field_options, $template_id, $field_id, $context, $input_type ) {
460
461
		$this->_field_options = $field_options;
462
		$this->_field_id = $field_id;
463
464
		return $field_options;
465
	}
466
467
	/**
468
	 * Check whether the `enableChoiceValue` flag is set for a GF field
469
	 *
470
	 * Gets the current form ID, gets the field at that ID, then checks for the enableChoiceValue value.
471
	 *
472
	 * @access protected
473
	 *
474
	 * @uses GFAPI::get_form
475
	 *
476
	 * @since 1.17
477
	 *
478
	 * @return bool True: Enable Choice Value is active for field; False: not active, or form invalid, or form not found.
479
	 */
480
	protected function is_choice_value_enabled() {
481
482
		// If "Add Field" button is processing, get the Form ID
483
		$connected_form = rgpost( 'form_id' );
484
485
		// Otherwise, get the Form ID from the Post page
486
		if( empty( $connected_form ) ) {
487
			$connected_form = gravityview_get_form_id( get_the_ID() );
488
		}
489
490
		if( empty( $connected_form ) ) {
491
			do_action( 'gravityview_log_error', sprintf( '%s: Form not found for form ID "%s"', __METHOD__, $connected_form ) );
492
			return false;
493
		}
494
495
		$form = GFAPI::get_form( $connected_form );
496
497
		if ( ! $form ) {
498
			do_action( 'gravityview_log_error', sprintf( '%s: Form not found for field ID of "%s", when checking for a form with ID of "%s"', __METHOD__, $this->_field_id, $connected_form ) );
499
			return false;
500
		}
501
502
		$field = gravityview_get_field( $form, $this->_field_id );
503
504
		return ! empty( $field->enableChoiceValue );
505
	}
506
507
}
508