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; |
|
|
|
|
140
|
1 |
|
$gv_field->start = 1; |
|
|
|
|
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; |
|
|
|
|
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; |
|
|
|
|
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 ); |
|
|
|
|
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 ) { |
|
|
|
|
233
|
|
|
$startlines[ $context_key ] = $context->view->get_entries()->total() - ( $pagenum * $pagesize ); |
234
|
|
|
$startlines[ $context_key ] += $context->field->start - 1; |
|
|
|
|
235
|
|
|
} else { |
236
|
|
|
$startlines[ $context_key ] = ( $pagenum * $pagesize ) + $context->field->start; |
|
|
|
|
237
|
|
|
} |
238
|
|
|
} |
239
|
|
|
|
240
|
|
|
return $context->field->reverse ? $startlines[ $context_key ]-- : $startlines[ $context_key ]++; |
|
|
|
|
241
|
|
|
} |
242
|
|
|
} |
243
|
|
|
|
244
|
|
|
new GravityView_Field_Sequence; |
245
|
|
|
|
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.