Completed
Push — master ( 92cdcd...79386d )
by Zack
20:40 queued 10:41
created

GravityView_Field::set_default_search_label()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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