Completed
Push — develop ( 7f13c6...c06441 )
by Zack
06:21
created

GravityView_Field::add_field_support()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 5

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 10
rs 9.4285
cc 2
eloc 5
nc 2
nop 2
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
	 * @internal Not yet implemented
36
	 * @since 1.15.2
37
	 * @type string The group belongs to this field in the field picker
38
	 */
39
	public $group;
40
41
	/**
42
	 * @internal Not yet implemented
43
	 * @type boolean Can the field be searched?
44
	 * @since 1.15.2
45
	 */
46
	public $is_searchable;
47
48
	/**
49
	 * @internal Not yet implemented
50
	 * @type array $search_operators The type of search operators available for this field
51
	 * @since 1.15.2
52
	 */
53
	public $search_operators;
54
55
	/**
56
	 * @type boolean Can the field be sorted in search?
57
	 * @since 1.15.2
58
	 */
59
	public $is_sortable = true;
60
61
	/**
62
	 * @type boolean Is field content number-based?
63
	 * @since 1.15.2
64
	 */
65
	public $is_numeric;
66
67
	/**
68
	 * @internal Not yet implemented
69
	 * @todo implement supports_context() method
70
	 * The contexts in which a field is available. Some fields aren't editable, for example.
71
	 * - `singular` is an alias for both `single` and `edit`
72
	 * - `multiple` is an alias for `directory` (backward compatibility)
73
	 * @type array
74
	 * @since 1.15.2
75
	 */
76
	public $contexts = array( 'single', 'multiple', 'edit', 'export' );
77
78
	/**
79
	 * @since 1.15.2
80
	 * @since TODO Changed access to public (previously, protected)
81
	 * @type string The name of a corresponding Gravity Forms GF_Field class, if exists
82
	 */
83
	public $_gf_field_class_name;
84
85
	/**
86
	 * @var string The field ID being requested
87
	 * @since 1.14
88
	 */
89
	protected $_field_id = '';
90
91
	/**
92
	 * @var string Field options to be rendered
93
	 * @since 1.14
94
	 */
95
	protected $_field_options = array();
96
97
	/**
98
	 * @var bool|string Name of merge tag (without curly brackets), if the field has custom GravityView merge tags to add. Otherwise, false.
99
	 * @since 1.16
100
	 */
101
	protected $_custom_merge_tag = false;
102
103
	function __construct() {
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...
104
105
		// Modify the field options based on the name of the field type
106
		add_filter( sprintf( 'gravityview_template_%s_options', $this->name ), array( &$this, 'field_options' ), 10, 5 );
107
108
		add_filter( 'gravityview/sortable/field_blacklist', array( $this, '_filter_sortable_fields' ), 1 );
109
110
		if( $this->_custom_merge_tag ) {
111
			add_filter( 'gform_custom_merge_tags', array( $this, '_filter_gform_custom_merge_tags' ), 10, 4 );
112
			add_filter( 'gform_replace_merge_tags', array( $this, '_filter_gform_replace_merge_tags' ), 10, 7 );
113
		}
114
115
		GravityView_Fields::register( $this );
116
	}
117
118
	/**
119
	 * Match the merge tag in replacement text for the field.  DO NOT OVERRIDE.
120
	 *
121
	 * @see replace_merge_tag Override replace_merge_tag() to handle any matches
122
	 *
123
	 * @since 1.16
124
	 *
125
	 * @param string $text Text to replace
126
	 * @param array $form Gravity Forms form array
127
	 * @param array $entry Entry array
128
	 * @param bool $url_encode Whether to URL-encode output
129
	 *
130
	 * @return string Original text if {_custom_merge_tag} isn't found. Otherwise, replaced text.
131
	 */
132
	public function _filter_gform_replace_merge_tags( $text, $form = array(), $entry = array(), $url_encode = false, $esc_html = false  ) {
133
134
		/**
135
		 * This prevents the gform_replace_merge_tags filter from being called twice, as defined in:
136
		 * @see GFCommon::replace_variables()
137
		 * @see GFCommon::replace_variables_prepopulate()
138
		 * @todo Remove eventually: Gravity Forms fixed this issue in 1.9.14
139
		 */
140
		if( false === $form ) {
141
			return $text;
142
		}
143
144
		// Is there is field merge tag? Strip whitespace off the ned, too.
145
		preg_match_all( '/{' . preg_quote( $this->_custom_merge_tag ) . ':?(.*?)(?:\s)?}/ism', $text, $matches, PREG_SET_ORDER );
146
147
		// If there are no matches, return original text
148
		if ( empty( $matches ) ) {
149
			return $text;
150
		}
151
152
		return $this->replace_merge_tag( $matches, $text, $form, $entry, $url_encode, $esc_html );
153
	}
154
155
	/**
156
	 * Run GravityView filters when using GFCommon::replace_variables()
157
	 *
158
	 * Instead of adding multiple hooks, add all hooks into this one method to improve speed
159
	 *
160
	 * @since 1.8.4
161
	 *
162
	 * @param array $matches Array of Merge Tag matches found in text by preg_match_all
163
	 * @param string $text Text to replace
164
	 * @param array|bool $form Gravity Forms form array. When called inside {@see GFCommon::replace_variables()} (now deprecated), `false`
165
	 * @param array|bool $entry Entry array.  When called inside {@see GFCommon::replace_variables()} (now deprecated), `false`
166
	 * @param bool $url_encode Whether to URL-encode output
167
	 * @param bool $esc_html Whether to apply `esc_html()` to output
168
	 *
169
	 * @return mixed
170
	 */
171
	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...
172
173
		foreach( $matches as $match ) {
174
175
			$full_tag = $match[0];
176
177
			// Strip the Merge Tags
178
			$tag = str_replace( array( '{', '}'), '', $full_tag );
179
180
			// Replace the value from the entry, if exists
181
			if( isset( $entry[ $tag ] ) ) {
182
183
				$value = $entry[ $tag ];
184
185
				if( is_callable( array( $this, 'get_content') ) ) {
186
					$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...
187
				}
188
189
				$text = str_replace( $full_tag, $value, $text );
190
			}
191
		}
192
193
		unset( $value, $tag, $full_tag );
194
195
		return $text;
196
	}
197
198
	/**
199
	 * Add custom merge tags to merge tag options. DO NOT OVERRIDE.
200
	 *
201
	 * @internal Not to be overridden by fields
202
	 *
203
	 * @since 1.8.4
204
	 *
205
	 * @param array $existing_merge_tags
0 ignored issues
show
Bug introduced by
There is no parameter named $existing_merge_tags. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
206
	 * @param int $form_id GF Form ID
207
	 * @param GF_Field[] $fields Array of fields in the form
208
	 * @param string $element_id The ID of the input that Merge Tags are being used on
209
	 *
210
	 * @return array Modified merge tags
211
	 */
212
	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...
213
214
		$form = GVCommon::get_form( $form_id );
215
216
		$field_merge_tags = $this->custom_merge_tags( $form, $fields );
217
218
		return array_merge( $custom_merge_tags, $field_merge_tags );
219
	}
220
221
	/**
222
	 * Add custom Merge Tags to Merge Tag options, if custom Merge Tags exist
223
	 *
224
	 * Should be overridden if there's more than one Merge Tag to add or if the Merge Tag isn't {_custom_merge_tag}
225
	 *
226
	 * @since 1.16
227
	 *
228
	 * @param array $form GF Form array
229
	 * @param GF_Field[] $fields Array of fields in the form
230
	 *
231
	 * @return array Merge tag array with `label` and `tag` keys based on class `label` and `_custom_merge_tag` variables
232
	 */
233
	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...
234
235
		// Use variables to make it unnecessary for other fields to override
236
		$merge_tags = array(
237
			array(
238
				'label' => $this->label,
239
				'tag' => '{' . $this->_custom_merge_tag . '}',
240
			),
241
		);
242
243
		return $merge_tags;
244
	}
245
246
	/**
247
	 * Use field settings to modify whether a field is sortable
248
	 *
249
	 * @see GravityView_frontend::is_field_sortable
250
	 * @since 1.15.3
251
	 *
252
	 * @param array $not_sortable Existing field types that aren't sortable
253
	 *
254
	 * @return array
255
	 */
256
	public function _filter_sortable_fields( $not_sortable ) {
257
258
		if( ! $this->is_sortable ) {
259
			$not_sortable[] = $this->name;
260
		}
261
262
		return $not_sortable;
263
	}
264
265
	private function field_support_options() {
266
		$options = array(
267
			'link_to_post' => array(
268
				'type' => 'checkbox',
269
				'label' => __( 'Link to the post', 'gravityview' ),
270
				'desc' => __( 'Link to the post created by the entry.', 'gravityview' ),
271
				'value' => false,
272
			),
273
			'link_to_term' => array(
274
				'type' => 'checkbox',
275
				'label' => __( 'Link to the category or tag', 'gravityview' ),
276
				'desc' => __( 'Link to the current category or tag. "Link to single entry" must be unchecked.', 'gravityview' ),
277
				'value' => false,
278
			),
279
			'dynamic_data' => array(
280
				'type' => 'checkbox',
281
				'label' => __( 'Use the live post data', 'gravityview' ),
282
				'desc' => __( 'Instead of using the entry data, instead use the current post data.', 'gravityview' ),
283
				'value' => true,
284
			),
285
			'date_display' => array(
286
				'type' => 'text',
287
				'label' => __( 'Override Date Format', 'gravityview' ),
288
				'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>' ),
289
				/**
290
				 * @filter `gravityview_date_format` Override the date format with a [PHP date format](https://codex.wordpress.org/Formatting_Date_and_Time)
291
				 * @param[in,out] null|string $date_format Date Format (default: null)
292
				 */
293
				'value' => apply_filters( 'gravityview_date_format', null )
294
			),
295
			'new_window' => array(
296
				'type' => 'checkbox',
297
				'label' => __( 'Open link in a new tab or window?', 'gravityview' ),
298
				'value' => false,
299
			),
300
		);
301
302
		/**
303
		 * @filter `gravityview_field_support_options` Modify the settings that a field supports
304
		 * @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
305
		 */
306
		return apply_filters( 'gravityview_field_support_options', $options );
307
	}
308
309
	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...
310
311
		$options = $this->field_support_options();
312
313
		if( isset( $options[ $key ] ) ) {
314
			$field_options[ $key ] = $options[ $key ];
315
		}
316
317
		return $field_options;
318
	}
319
320
	/**
321
	 * Tap in here to modify field options.
322
	 *
323
	 * Here's an example:
324
	 *
325
	 * <pre>
326
	 * $field_options['name_display'] = array(
327
	 * 	'type' => 'select',
328
	 * 	'label' => __( 'User Format', 'gravityview' ),
329
	 * 	'desc' => __( 'How should the User information be displayed?', 'gravityview'),
330
	 * 	'choices' => array(
331
	 * 		array(
332
	 *		 	'value' => 'display_name',
333
	 *		  	'label' => __('Display Name (Example: "Ellen Ripley")', 'gravityview'),
334
	 *		),
335
	 *  	array(
336
	 *			'value' => 'user_login',
337
	 *			'label' => __('Username (Example: "nostromo")', 'gravityview')
338
	 *		),
339
	 * 	 'value' => 'display_name'
340
	 * );
341
	 * </pre>
342
	 *
343
	 * @param  [type]      $field_options [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
344
	 * @param  [type]      $template_id   [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
345
	 * @param  [type]      $field_id      [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
346
	 * @param  [type]      $context       [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
347
	 * @param  [type]      $input_type    [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
348
	 * @return [type]                     [description]
0 ignored issues
show
Documentation introduced by
The doc-type [type] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
349
	 */
350
	function field_options( $field_options, $template_id, $field_id, $context, $input_type ) {
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...
351
352
		$this->_field_options = $field_options;
353
		$this->_field_id = $field_id;
354
355
		return $field_options;
356
	}
357
358
}
359