Completed
Push — develop ( 87b48c...882b48 )
by Gennady
23:47 queued 03:49
created

GravityView_Field_Sequence   A

Complexity

Total Complexity 22

Size/Duplication

Total Lines 231
Duplicated Lines 0 %

Coupling/Cohesion

Components 0
Dependencies 8

Test Coverage

Coverage 66.22%

Importance

Changes 0
Metric Value
dl 0
loc 231
ccs 49
cts 74
cp 0.6622
rs 10
c 0
b 0
f 0
wmc 22
lcom 0
cbo 8

5 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 8 1
A field_tooltips() 0 11 1
C replace_merge_tag() 0 83 10
B get_sequence() 0 50 9
A field_options() 0 22 1
1
<?php
2
/**
3
 * @file class-gravityview-field-sequence.php
4
 * @package GravityView
5
 * @subpackage includes\fields
6
 */
7
8
/**
9
 * Add a sequence field.
10
 * @since 2.3.3
11
 */
12
class GravityView_Field_Sequence extends GravityView_Field {
13
14
	var $name = 'sequence';
15
16
	var $contexts = array( 'single', 'multiple' );
17
18
	/**
19
	 * @var bool
20
	 */
21
	var $is_sortable = false;
22
23
	/**
24
	 * @var bool
25
	 */
26
	var $is_searchable = false;
27
28
	/**
29
	 * @var bool
30
	 */
31
	var $is_numeric = true;
32
33
	var $_custom_merge_tag = 'sequence';
34
35
	var $group = 'gravityview';
36
37
	public function __construct() {
38
39
		$this->label = esc_html__( 'Number Sequence', 'gravityview' );
40
41
		add_filter( 'gravityview/metaboxes/tooltips', array( $this, 'field_tooltips') );
42
43
		parent::__construct();
44
	}
45
46
	/**
47
	 * Add tooltips
48
	 * @param  array $tooltips Existing tooltips
49
	 * @return array           Modified tooltips
50
	 */
51
	public function field_tooltips( $tooltips ) {
52
53
		$return = $tooltips;
54
55
		$return['reverse_sequence'] = array(
56
			'title' => __('Reverse the order of the result numbers', 'gravityview'),
57
			'value' => __('Output the number sequence in descending order. If enabled, numbers will count down from high to low.', 'gravityview'),
58
		);
59
60
		return $return;
61
	}
62
63
	public function field_options( $field_options, $template_id, $field_id, $context, $input_type, $form_id ) {
64
65
		unset ( $field_options['search_filter'] );
66
67
		$new_fields = array(
68
			'start' => array(
69
				'type' => 'number',
70
				'label' => __( 'First Number in the Sequence', 'gravityview' ),
71
				'desc' => __('For each entry, the displayed number will increase by one. When displaying ten entries, the first entry will display "1", and the last entry will show "10".', 'gravityview'),
72
				'value' => '1',
73
				'merge_tags' => false,
74
			),
75
			'reverse' => array(
76
				'type' => 'checkbox',
77
				'label' => __( 'Reverse the order of the number sequence (high to low)', 'gravityview' ),
78
				'tooltip' => 'reverse_sequence',
79
				'value' => '',
80
			),
81
		);
82
83
		return $new_fields + $field_options;
84
	}
85
86
	/**
87
	 * Replace {sequence} Merge Tags inside Custom Content fields
88
	 *
89
	 * TODO:
90
	 * - Find a better way to infer current View data (without using legacy code)
91
	 *
92
	 * @param array $matches
93
	 * @param string $text
94
	 * @param array $form
95
	 * @param array $entry
96
	 * @param bool $url_encode
97
	 * @param bool $esc_html
98
	 *
99
	 * @return string
100
	 */
101 1
	public function replace_merge_tag( $matches = array(), $text = '', $form = array(), $entry = array(), $url_encode = false, $esc_html = false ) {
102
		/**
103
		 * An internal cache for sequence tag reuse within one field.
104
		 * Avoids calling get_sequence over and over again, off-by-many increments, etc.
105
		 */
106 1
		static $merge_tag_sequences = array();
107
108 1
		$view_data = gravityview_get_current_view_data(); // TODO: Don't use legacy code...
109
110
		// If we're not in a View or embed, don't replace the merge tag
111 1
		if ( empty( $view_data ) ) {
112
			gravityview()->log->error( '{sequence} Merge Tag used outside of a GravityView View.', array( 'data' => $matches ) );
113
			return $text;
114
		}
115
116 1
		$legacy_field = \GravityView_View::getInstance()->getCurrentField(); // TODO: Don't use legacy code...
117
118
		// If we're outside field context (like a GV widget), don't replace the merge tag
119 1
		if ( ! $legacy_field ) {
120 1
			gravityview()->log->error( '{sequence} Merge Tag was used without outside of the GravityView entry loop.', array( 'data' => $matches ) );
121 1
			return $text;
122
		}
123
124 1
		$return = $text;
125
126 1
		$context = new \GV\Template_Context();
127 1
		$context->view = \GV\View::by_id( $view_data['view_id'] );
128 1
		$context->entry = \GV\GF_Entry::from_entry( $entry );
129
130 1
		$gv_field = \GV\Internal_Field::by_id( 'sequence' );
131 1
		$merge_tag_context = \GV\Utils::get( $legacy_field, 'UID' );
132 1
		$merge_tag_context = $entry['id'] . "/{$merge_tag_context}";
133
134 1
		foreach ( $matches as $match ) {
135
136 1
			$full_tag = $match[0];
137 1
			$property = $match[1];
138
139 1
			$gv_field->reverse = false;
0 ignored issues
show
Bug introduced by
The property reverse does not seem to exist in GV\Internal_Field.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
140 1
			$gv_field->start = 1;
0 ignored issues
show
Bug introduced by
The property start does not seem to exist in GV\Internal_Field.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
141
142 1
			$modifiers = explode( ',', trim( $property ) );
143
144 1
			foreach ( $modifiers as $modifier ) {
145
146 1
				$modifier = trim( $modifier );
147
148 1
				if ( 'reverse' === $modifier ) {
149 1
					$gv_field->reverse = true;
150
				}
151
152 1
				$maybe_start = explode( ':', $modifier );
153
154
				// If there is a field with the ID of the start number, the merge tag won't work.
155
				// In that case, you can use "=" instead: `{sequence start=10}`
156 1
				if( 1 === sizeof( $maybe_start ) ) {
157 1
					$maybe_start = explode( '=', $modifier );
158
				}
159
160 1
				if ( 'start' === rgar( $maybe_start, 0 ) && is_numeric( rgar( $maybe_start, 1 ) ) ) {
161 1
					$gv_field->start = (int) rgar( $maybe_start, 1 );
162
				}
163
			}
164
165
			/**
166
			 * We make sure that distinct sequence modifiers have their own
167
			 * output counters.
168
			 */
169 1
			$merge_tag_context_modifiers = $merge_tag_context . '/' . var_export( $gv_field->reverse, true ) . '/' . $gv_field->start;
0 ignored issues
show
Documentation introduced by
The property reverse does not exist on object<GV\Internal_Field>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property start does not exist on object<GV\Internal_Field>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
170
171 1
			if ( ! isset( $merge_tag_sequences[ $merge_tag_context_modifiers ] ) ) {
172 1
				$gv_field->UID = $legacy_field['UID'] . '/' . var_export( $gv_field->reverse, true ) . '/' . $gv_field->start;
0 ignored issues
show
Documentation introduced by
The property reverse does not exist on object<GV\Internal_Field>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
Documentation introduced by
The property start does not exist on object<GV\Internal_Field>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
173 1
				$context->field = $gv_field;
174 1
				$sequence = $merge_tag_sequences[ $merge_tag_context_modifiers ] = $this->get_sequence( $context );
175
			} else {
176 1
				$sequence = $merge_tag_sequences[ $merge_tag_context_modifiers ];
177
			}
178
179 1
			$return = str_replace( $full_tag, $sequence, $return );
180
		}
181
182 1
		return $return;
183
	}
184
185
	/**
186
	 * Calculate the current sequence number for the context.
187
	 *
188
	 * @param  \GV\Template_Context $context The context.
189
	 *
190
	 * @return int The sequence number for the field/entry within the view results.
191
	 */
192 2
	public function get_sequence( $context ) {
193 2
		static $startlines = array();
194
195 2
		$context_key = md5( json_encode(
196
			array(
197 2
				$context->view->ID,
198 2
				\GV\Utils::get( $context, 'field/UID' ),
199
			)
200
		) );
201
202
		/**
203
		 * Figure out the starting number.
204
		 */
205 2
		if ( $context->request && $entry = $context->request->is_entry() ) {
206
207 1
			$sql_query = array();
208
209 1
			add_filter( 'gform_gf_query_sql', $callback = function( $sql ) use ( &$sql_query ) {
210 1
				$sql_query = $sql;
211 1
				return $sql;
212 1
			} );
213
214
			$total = $context->view->get_entries()->total();
215
			remove_filter( 'gform_gf_query_sql', $callback );
216
217
			unset( $sql_query['paginate'] );
218
219
			global $wpdb;
220
221
			foreach ( $wpdb->get_results( implode( ' ', $sql_query ), ARRAY_A ) as $n => $result ) {
222
				if ( in_array( $entry->ID, $result ) ) {
223
					return $context->field->reverse ? ( $total - $n ) : ( $n + 1 );
0 ignored issues
show
Documentation introduced by
The property reverse does not exist on object<GV\Field>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
224
				}
225
			}
226
227
			return 0;
228
		} elseif ( ! isset( $startlines[ $context_key ] ) ) {
229
			$pagenum  = max( 0, \GV\Utils::_GET( 'pagenum', 1 ) - 1 );
230
			$pagesize = $context->view->settings->get( 'page_size', 25 );
231
232
			if ( $context->field->reverse ) {
0 ignored issues
show
Documentation introduced by
The property reverse does not exist on object<GV\Field>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
233
				$startlines[ $context_key ] = $context->view->get_entries()->total() - ( $pagenum * $pagesize );
234
				$startlines[ $context_key ] += $context->field->start - 1;
0 ignored issues
show
Documentation introduced by
The property start does not exist on object<GV\Field>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
235
			} else {
236
				$startlines[ $context_key ] = ( $pagenum * $pagesize ) + $context->field->start;
0 ignored issues
show
Documentation introduced by
The property start does not exist on object<GV\Field>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
237
			}
238
		}
239
240
		return $context->field->reverse ? $startlines[ $context_key ]-- : $startlines[ $context_key ]++;
0 ignored issues
show
Documentation introduced by
The property reverse does not exist on object<GV\Field>. Since you implemented __get, maybe consider adding a @property annotation.

Since your code implements the magic getter _get, this function will be called for any read access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

If the property has read access only, you can use the @property-read annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
241
	}
242
}
243
244
new GravityView_Field_Sequence;
245