Completed
Push — develop ( 7b8d22...20c6d6 )
by Zack
05:12
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 %

Importance

Changes 1
Bugs 0 Features 1
Metric Value
cc 4
eloc 13
c 1
b 0
f 1
nc 6
nop 0
dl 0
loc 26
rs 8.5806
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
	 * `standard`, `advanced`, `post`, `pricing`, `meta`, `gravityview`
35
	 * @since 1.15.2
36
	 * @type string The group belongs to this field in the field picker
37
	 */
38
	public $group;
39
40
	/**
41
	 * @internal Not yet implemented
42
	 * @type boolean Can the field be searched?
43
	 * @since 1.15.2
44
	 */
45
	public $is_searchable;
46
47
	/**
48
	 * @internal Not yet implemented
49
	 * @type array $search_operators The type of search operators available for this field
50
	 * @since 1.15.2
51
	 */
52
	public $search_operators;
53
54
	/**
55
	 * @type boolean Can the field be sorted in search?
56
	 * @since 1.15.2
57
	 */
58
	public $is_sortable = true;
59
60
	/**
61
	 * @type boolean Is field content number-based?
62
	 * @since 1.15.2
63
	 */
64
	public $is_numeric;
65
66
	/**
67
	 * @internal Not yet implemented
68
	 * @todo implement supports_context() method
69
	 * The contexts in which a field is available. Some fields aren't editable, for example.
70
	 * - `singular` is an alias for both `single` and `edit`
71
	 * - `multiple` is an alias for `directory` (backward compatibility)
72
	 * @type array
73
	 * @since 1.15.2
74
	 */
75
	public $contexts = array( 'single', 'multiple', 'edit', 'export' );
76
77
	/**
78
	 * @since 1.15.2
79
	 * @since 1.16.2.2 Changed access to public (previously, protected)
80
	 * @type string The name of a corresponding Gravity Forms GF_Field class, if exists
81
	 */
82
	public $_gf_field_class_name;
83
84
	/**
85
	 * @var string The field ID being requested
86
	 * @since 1.14
87
	 */
88
	protected $_field_id = '';
89
90
	/**
91
	 * @var string Field options to be rendered
92
	 * @since 1.14
93
	 */
94
	protected $_field_options = array();
95
96
	/**
97
	 * @var bool|string Name of merge tag (without curly brackets), if the field has custom GravityView merge tags to add. Otherwise, false.
98
	 * @since 1.16
99
	 */
100
	protected $_custom_merge_tag = false;
101
102
	/**
103
	 * GravityView_Field constructor.
104
	 */
105
	public function __construct() {
106
107
		// Modify the field options based on the name of the field type
108
		add_filter( sprintf( 'gravityview_template_%s_options', $this->name ), array( &$this, 'field_options' ), 10, 5 );
109
110
		add_filter( 'gravityview/sortable/field_blacklist', array( $this, '_filter_sortable_fields' ), 1 );
111
112
		if( $this->_custom_merge_tag ) {
113
			add_filter( 'gform_custom_merge_tags', array( $this, '_filter_gform_custom_merge_tags' ), 10, 4 );
114
			add_filter( 'gform_replace_merge_tags', array( $this, '_filter_gform_replace_merge_tags' ), 10, 7 );
115
		}
116
117
		GravityView_Fields::register( $this );
118
	}
119
120
	/**
121
	 * Match the merge tag in replacement text for the field.  DO NOT OVERRIDE.
122
	 *
123
	 * @see replace_merge_tag Override replace_merge_tag() to handle any matches
124
	 *
125
	 * @since 1.16
126
	 *
127
	 * @param string $text Text to replace
128
	 * @param array $form Gravity Forms form array
129
	 * @param array $entry Entry array
130
	 * @param bool $url_encode Whether to URL-encode output
131
	 *
132
	 * @return string Original text if {_custom_merge_tag} isn't found. Otherwise, replaced text.
133
	 */
134
	public function _filter_gform_replace_merge_tags( $text, $form = array(), $entry = array(), $url_encode = false, $esc_html = false  ) {
135
136
		/**
137
		 * This prevents the gform_replace_merge_tags filter from being called twice, as defined in:
138
		 * @see GFCommon::replace_variables()
139
		 * @see GFCommon::replace_variables_prepopulate()
140
		 * @todo Remove eventually: Gravity Forms fixed this issue in 1.9.14
141
		 */
142
		if( false === $form ) {
143
			return $text;
144
		}
145
146
		// Is there is field merge tag? Strip whitespace off the ned, too.
147
		preg_match_all( '/{' . preg_quote( $this->_custom_merge_tag ) . ':?(.*?)(?:\s)?}/ism', $text, $matches, PREG_SET_ORDER );
148
149
		// If there are no matches, return original text
150
		if ( empty( $matches ) ) {
151
			return $text;
152
		}
153
154
		return $this->replace_merge_tag( $matches, $text, $form, $entry, $url_encode, $esc_html );
155
	}
156
157
	/**
158
	 * Run GravityView filters when using GFCommon::replace_variables()
159
	 *
160
	 * Instead of adding multiple hooks, add all hooks into this one method to improve speed
161
	 *
162
	 * @since 1.8.4
163
	 *
164
	 * @param array $matches Array of Merge Tag matches found in text by preg_match_all
165
	 * @param string $text Text to replace
166
	 * @param array|bool $form Gravity Forms form array. When called inside {@see GFCommon::replace_variables()} (now deprecated), `false`
167
	 * @param array|bool $entry Entry array.  When called inside {@see GFCommon::replace_variables()} (now deprecated), `false`
168
	 * @param bool $url_encode Whether to URL-encode output
169
	 * @param bool $esc_html Whether to apply `esc_html()` to output
170
	 *
171
	 * @return mixed
172
	 */
173
	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...
174
175
		foreach( $matches as $match ) {
176
177
			$full_tag = $match[0];
178
179
			// Strip the Merge Tags
180
			$tag = str_replace( array( '{', '}'), '', $full_tag );
0 ignored issues
show
introduced by
No space before closing parenthesis of array is bad style
Loading history...
181
182
			// Replace the value from the entry, if exists
183
			if( isset( $entry[ $tag ] ) ) {
184
185
				$value = $entry[ $tag ];
186
187
				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...
188
					$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...
189
				}
190
191
				$text = str_replace( $full_tag, $value, $text );
192
			}
193
		}
194
195
		unset( $value, $tag, $full_tag );
196
197
		return $text;
198
	}
199
200
	/**
201
	 * Add custom merge tags to merge tag options. DO NOT OVERRIDE.
202
	 *
203
	 * @internal Not to be overridden by fields
204
	 *
205
	 * @since 1.8.4
206
	 *
207
	 * @param array $custom_merge_tags
208
	 * @param int $form_id GF Form ID
209
	 * @param GF_Field[] $fields Array of fields in the form
210
	 * @param string $element_id The ID of the input that Merge Tags are being used on
211
	 *
212
	 * @return array Modified merge tags
213
	 */
214
	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...
215
216
		$form = GVCommon::get_form( $form_id );
217
218
		$field_merge_tags = $this->custom_merge_tags( $form, $fields );
219
220
		return array_merge( $custom_merge_tags, $field_merge_tags );
221
	}
222
223
	/**
224
	 * Add custom Merge Tags to Merge Tag options, if custom Merge Tags exist
225
	 *
226
	 * Should be overridden if there's more than one Merge Tag to add or if the Merge Tag isn't {_custom_merge_tag}
227
	 *
228
	 * @since 1.16
229
	 *
230
	 * @param array $form GF Form array
231
	 * @param GF_Field[] $fields Array of fields in the form
232
	 *
233
	 * @return array Merge tag array with `label` and `tag` keys based on class `label` and `_custom_merge_tag` variables
234
	 */
235
	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...
236
237
		// Use variables to make it unnecessary for other fields to override
238
		$merge_tags = array(
239
			array(
240
				'label' => $this->label,
241
				'tag' => '{' . $this->_custom_merge_tag . '}',
242
			),
243
		);
244
245
		return $merge_tags;
246
	}
247
248
	/**
249
	 * Use field settings to modify whether a field is sortable
250
	 *
251
	 * @see GravityView_frontend::is_field_sortable
252
	 * @since 1.15.3
253
	 *
254
	 * @param array $not_sortable Existing field types that aren't sortable
255
	 *
256
	 * @return array
257
	 */
258
	public function _filter_sortable_fields( $not_sortable ) {
259
260
		if( ! $this->is_sortable ) {
261
			$not_sortable[] = $this->name;
262
		}
263
264
		return $not_sortable;
265
	}
266
267
	private function field_support_options() {
268
		$options = array(
269
			'link_to_post' => array(
270
				'type' => 'checkbox',
271
				'label' => __( 'Link to the post', 'gravityview' ),
272
				'desc' => __( 'Link to the post created by the entry.', 'gravityview' ),
273
				'value' => false,
274
			),
275
			'link_to_term' => array(
276
				'type' => 'checkbox',
277
				'label' => __( 'Link to the category or tag', 'gravityview' ),
278
				'desc' => __( 'Link to the current category or tag. "Link to single entry" must be unchecked.', 'gravityview' ),
279
				'value' => false,
280
			),
281
			'dynamic_data' => array(
282
				'type' => 'checkbox',
283
				'label' => __( 'Use the live post data', 'gravityview' ),
284
				'desc' => __( 'Instead of using the entry data, instead use the current post data.', 'gravityview' ),
285
				'value' => true,
286
			),
287
			'date_display' => array(
288
				'type' => 'text',
289
				'label' => __( 'Override Date Format', 'gravityview' ),
290
				'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...
291
				/**
292
				 * @filter `gravityview_date_format` Override the date format with a [PHP date format](https://codex.wordpress.org/Formatting_Date_and_Time)
293
				 * @param[in,out] null|string $date_format Date Format (default: null)
294
				 */
295
				'value' => apply_filters( 'gravityview_date_format', null )
296
			),
297
			'new_window' => array(
298
				'type' => 'checkbox',
299
				'label' => __( 'Open link in a new tab or window?', 'gravityview' ),
300
				'value' => false,
301
			),
302
		);
303
304
		/**
305
		 * @filter `gravityview_field_support_options` Modify the settings that a field supports
306
		 * @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
307
		 */
308
		return apply_filters( 'gravityview_field_support_options', $options );
309
	}
310
311
	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...
312
313
		$options = $this->field_support_options();
314
315
		if( isset( $options[ $key ] ) ) {
316
			$field_options[ $key ] = $options[ $key ];
317
		}
318
319
		return $field_options;
320
	}
321
322
	/**
323
	 * Tap in here to modify field options.
324
	 *
325
	 * Here's an example:
326
	 *
327
	 * <pre>
328
	 * $field_options['name_display'] = array(
329
	 * 	'type' => 'select',
330
	 * 	'label' => __( 'User Format', 'gravityview' ),
331
	 * 	'desc' => __( 'How should the User information be displayed?', 'gravityview'),
332
	 * 	'choices' => array(
333
	 * 		array(
334
	 *		 	'value' => 'display_name',
335
	 *		  	'label' => __('Display Name (Example: "Ellen Ripley")', 'gravityview'),
336
	 *		),
337
	 *  	array(
338
	 *			'value' => 'user_login',
339
	 *			'label' => __('Username (Example: "nostromo")', 'gravityview')
340
	 *		),
341
	 * 	 'value' => 'display_name'
342
	 * );
343
	 * </pre>
344
	 *
345
	 * @param  array      $field_options [description]
346
	 * @param  string      $template_id   [description]
347
	 * @param  string      $field_id      [description]
348
	 * @param  string      $context       [description]
349
	 * @param  string      $input_type    [description]
350
	 * @return array                     [description]
351
	 */
352
	public function field_options( $field_options, $template_id, $field_id, $context, $input_type ) {
353
354
		$this->_field_options = $field_options;
355
		$this->_field_id = $field_id;
356
357
		return $field_options;
358
	}
359
360
	/**
361
	 * Check whether the `enableChoiceValue` flag is set for a GF field
362
	 *
363
	 * Gets the current form ID, gets the field at that ID, then checks for the enableChoiceValue value.
364
	 *
365
	 * @access protected
366
	 *
367
	 * @uses GFAPI::get_form
368
	 *
369
	 * @since 1.17
370
	 *
371
	 * @return bool True: Enable Choice Value is active for field; False: not active, or form invalid, or form not found.
372
	 */
373
	protected function is_choice_value_enabled() {
374
375
		// If "Add Field" button is processing, get the Form ID
376
		$connected_form = rgpost( 'form_id' );
377
378
		// Otherwise, get the Form ID from the Post page
379
		if( empty( $connected_form ) ) {
380
			$connected_form = gravityview_get_form_id( get_the_ID() );
381
		}
382
383
		if( empty( $connected_form ) ) {
384
			do_action( 'gravityview_log_error', sprintf( '%s: Form not found for form ID "%s"', __METHOD__, $connected_form ) );
385
			return false;
386
		}
387
388
		$form = GFAPI::get_form( $connected_form );
389
390
		if ( ! $form ) {
391
			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 ) );
392
			return false;
393
		}
394
395
		$field = gravityview_get_field( $form, $this->_field_id );
396
397
		return ! empty( $field->enableChoiceValue );
398
	}
399
400
}
401