GravityView  2.10.1
The best, easiest way to display Gravity Forms entries on your website.
class-gv-template-field.php
Go to the documentation of this file.
1 <?php
2 namespace GV;
3 
4 /** If this file is called directly, abort. */
5 if ( ! defined( 'GRAVITYVIEW_DIR' ) ) {
6  die();
7 }
8 
9 /**
10  * Load up the Gamajo Template Loader.
11  *
12  * @see https://github.com/GaryJones/Gamajo-Template-Loader
13  */
14 if ( ! class_exists( '\GV\Gamajo_Template_Loader' ) ) {
15  require gravityview()->plugin->dir( 'future/lib/class-gamajo-template-loader.php' );
16 }
17 
18 /**
19  * The Field Template class.
20  *
21  * Attached to a \GV\Field and used by a \GV\Field_Renderer.
22  */
23 abstract class Field_Template extends Template {
24  /**
25  * Prefix for filter names.
26  * @var string
27  */
28  protected $filter_prefix = 'gravityview/template/fields';
29 
30  /**
31  * Directory name where custom templates for this plugin should be found in the theme.
32  * @var string
33  */
34  protected $theme_template_directory = 'gravityview/fields/';
35 
36  /**
37  * Directory name where the default templates for this plugin are found.
38  * @var string
39  */
40  protected $plugin_template_directory = 'templates/fields/';
41 
42  /**
43  * @var \GV\Field The field connected to this template.
44  */
45  public $field;
46 
47  /**
48  * @var \GV\View The view context.
49  */
50  public $view;
51 
52  /**
53  * @var \GV\Source The source context.
54  */
55  public $source;
56 
57  /**
58  * @var \GV\Entry The entry context.
59  */
60  public $entry;
61 
62  /**
63  * @var \GV\Request The request context.
64  */
65  public $request;
66 
67  /**
68  * @var string The template slug to be loaded (like "table", "list")
69  */
70  public static $slug;
71 
72  /**
73  * Initializer.
74  *
75  * @param \GV\Field $field The field about to be rendered.
76  * @param \GV\View $view The view in this context, if applicable.
77  * @param \GV\Source $source The source (form) in this context, if applicable.
78  * @param \GV\Entry $entry The entry in this context, if applicable.
79  * @param \GV\Request $request The request in this context, if applicable.
80  */
81  public function __construct( Field $field, View $view = null, Source $source = null, Entry $entry = null, Request $request = null ) {
82  $this->field = $field;
83  $this->view = $view;
84  $this->source = $source;
85  $this->entry = $entry;
86  $this->request = $request;
87 
88  /** Add granular overrides. */
89  add_filter( $this->filter_prefix . '_get_template_part', $this->_add_id_specific_templates_callback = self::add_id_specific_templates( $this ), 10, 3 );
90 
91  parent::__construct();
92  }
93 
94  public function __destruct() {
95  remove_filter( $this->filter_prefix . '_get_template_part', $this->_add_id_specific_templates_callback );;
96  }
97 
98  /**
99  * Enable granular template overrides based on current post, view, form, field types, etc.
100  *
101  * Why? See https://github.com/gravityview/GravityView/issues/1024
102  *
103  * @param \GV\Field_Template $template The template instance.
104  * @return callable The callback bound to `get_template_part`. See `\GV\Field_Template::__construct`
105  */
106  public static function add_id_specific_templates( $template ) {
107 
108  $inputType = null;
109  $field_type = null;
110  $field_id = null;
111  $view_id = null;
112  $form_id = null;
113  $is_view = $template->request && $template->request->is_view();
114 
115  if ( $template->field ) {
116  $inputType = $template->field->inputType;
117  $field_type = $template->field->type;
118  $field_id = $template->field->ID;
119  }
120 
121  if ( $template->view ) {
122  $view_id = $template->view->ID;
123  $form_id = $template->view->form ? $template->view->form->ID : null;
124  }
125 
126  $class = get_class( $template );
127 
128  /**
129  * Enable granular template overrides based on current post, view, form, field types, etc.
130  *
131  * The hierarchy is as follows:
132  *
133  * - post-[ID of post of page where view is embedded]-view-[View ID]-field-[Field type]-html.php
134  * - post-[ID of post of page where view is embedded]-view-[View ID]-field-[Field inputType]-html.php
135  * - post-[ID of post of page where view is embedded]-view-[View ID]-field-html.php
136  * - post-[ID of post of page where view is embedded]-field-[Field type]-html.php
137  * - post-[ID of post of page where view is embedded]-field-[Field inputType]-html.php
138  * - post-[ID of post of page where view is embedded]-field-html.php
139  * - post-[ID of post of page where view is embedded]-view-[View ID]-field-[Field type].php
140  * - post-[ID of post of page where view is embedded]-view-[View ID]-field-[Field inputType].php
141  * - post-[ID of post of page where view is embedded]-view-[View ID]-field.php
142  * - post-[ID of post of page where view is embedded]-field-[Field type].php
143  * - post-[ID of post of page where view is embedded]-field-[Field inputType].php
144  * - post-[ID of post of page where view is embedded]-field.php
145  * - form-[Form ID]-field-[Field ID]-html.php
146  * - form-[Form ID]-field-[Field ID].php
147  * - form-[Form ID]-field-[Field type]-html.php
148  * - form-[Form ID]-field-[Field inputType]-html.php
149  * - form-[Form ID]-field-[Field type].php
150  * - form-[Form ID]-field-[Field inputType].php
151  * - view-[View ID]-field-[Field type]-html.php
152  * - view-[View ID]-field-[Field inputType]-html.php
153  * - view-[View ID]-field-[Field type].php
154  * - view-[View ID]-field-[Field inputType].php
155  * - field-[Field type]-html.php
156  * - field-[Field inputType]-html.php
157  * - field-[Field type].php
158  * - field-[Field inputType].php
159  * - field-html.php
160  * - field.php
161  *
162  * @see Gamajo_Template_Loader::get_template_file_names() Where the filter is
163  * @param array $templates Existing list of templates.
164  * @param string $slug Name of the template base, example: `html`, `json`, `xml`
165  * @param string $name Name of the template part.
166  *
167  * @return array $templates Modified template array, merged with existing $templates values
168  */
169  return function( $templates, $slug, $name ) use ( $class, $inputType, $field_type, $view_id, $is_view, $form_id, $field_id ) {
170  $specifics = array();
171 
172  list( $slug_dir, $slug_name ) = $class::split_slug( $slug, $name );
173 
174  global $post;
175 
176  if ( $is_view && $post ) {
177  if ( $field_type ) {
178  $specifics []= sprintf( '%spost-%d-view-%d-field-%s-%s.php', $slug_dir, $post->ID, $view_id, $field_type, $slug_name );
179  $inputType && $specifics []= sprintf( '%spost-%d-view-%d-field-%s-%s.php', $slug_dir, $post->ID, $view_id, $inputType, $slug_name );
180  $specifics []= sprintf( '%spost-%d-view-%d-field-%s.php', $slug_dir, $post->ID, $view_id, $field_type );
181  $inputType && $specifics []= sprintf( '%spost-%d-view-%d-field-%s.php', $slug_dir, $post->ID, $view_id, $inputType );
182  $specifics []= sprintf( '%spost-%d-field-%s-%s.php', $slug_dir, $post->ID, $field_type, $slug_name );
183  $inputType && $specifics []= sprintf( '%spost-%d-field-%s-%s.php', $slug_dir, $post->ID, $inputType, $slug_name );
184  $specifics []= sprintf( '%spost-%d-field-%s.php', $slug_dir, $post->ID, $field_type );
185  $inputType && $specifics []= sprintf( '%spost-%d-field-%s.php', $slug_dir, $post->ID, $inputType );
186  }
187 
188  $specifics []= sprintf( '%spost-%d-view-%d-field-%s.php', $slug_dir, $post->ID, $view_id, $slug_name );
189  $specifics []= sprintf( '%spost-%d-view-%d-field.php', $slug_dir, $post->ID, $view_id );
190  $specifics []= sprintf( '%spost-%d-field-%s.php', $slug_dir, $post->ID, $slug_name );
191  $specifics []= sprintf( '%spost-%d-field.php', $slug_dir, $post->ID );
192  }
193 
194  /** Field-specific */
195  if ( $field_id && $form_id ) {
196 
197  if ( $field_id ) {
198  $specifics []= sprintf( '%sform-%d-field-%d-%s.php', $slug_dir, $form_id, $field_id, $slug_name );
199  $specifics []= sprintf( '%sform-%d-field-%d.php', $slug_dir, $form_id, $field_id );
200 
201  if ( $view_id ) {
202  $specifics []= sprintf( '%sview-%d-field-%d.php', $slug_dir, $view_id, $field_id );
203  }
204  }
205 
206  if ( $field_type ) {
207  $specifics []= sprintf( '%sform-%d-field-%s-%s.php', $slug_dir, $form_id, $field_type, $slug_name );
208  $inputType && $specifics []= sprintf( '%sform-%d-field-%s-%s.php', $slug_dir, $form_id, $inputType, $slug_name );
209  $specifics []= sprintf( '%sform-%d-field-%s.php', $slug_dir, $form_id, $field_type );
210  $inputType && $specifics []= sprintf( '%sform-%d-field-%s.php', $slug_dir, $form_id, $inputType );
211 
212  $specifics []= sprintf( '%sview-%d-field-%s-%s.php', $slug_dir, $view_id, $field_type, $slug_name );
213  $inputType && $specifics []= sprintf( '%sview-%d-field-%s-%s.php', $slug_dir, $view_id, $inputType, $slug_name );
214  $specifics []= sprintf( '%sview-%d-field-%s.php', $slug_dir, $view_id, $field_type );
215  $inputType && $specifics []= sprintf( '%sview-%d-field-%s.php', $slug_dir, $view_id, $inputType );
216 
217  $specifics []= sprintf( '%sfield-%s-%s.php', $slug_dir, $field_type, $slug_name );
218  $inputType && $specifics []= sprintf( '%sfield-%s-%s.php', $slug_dir, $inputType, $slug_name );
219  $specifics []= sprintf( '%sfield-%s.php', $slug_dir, $field_type );
220  $inputType && $specifics []= sprintf( '%sfield-%s.php', $slug_dir, $inputType );
221  }
222  }
223 
224  if ( $form_id ) {
225  /** Generic field templates */
226  $specifics []= sprintf( '%sview-%d-field-%s.php', $slug_dir, $view_id, $slug_name );
227  $specifics []= sprintf( '%sform-%d-field-%s.php', $slug_dir, $form_id, $slug_name );
228 
229  $specifics []= sprintf( '%sview-%d-field.php', $slug_dir, $view_id );
230  $specifics []= sprintf( '%sform-%d-field.php', $slug_dir, $form_id );
231  }
232 
233  /**
234  * Legacy.
235  * Ignore some types that conflict.
236  */
237  if ( ! in_array( $field_type, array( 'notes' ) ) ) {
238  $specifics []= sprintf( '%s.php', $field_type );
239  $specifics []= sprintf( 'fields/%s.php', $field_type );
240  }
241 
242  $specifics []= sprintf( '%sfield-%s.php', $slug_dir, $slug_name );
243  $specifics []= sprintf( '%sfield.php', $slug_dir );
244 
245  return array_merge( $specifics, $templates );
246  };
247  }
248 
249  /**
250  * Output some HTML.
251  *
252  * @todo Move to \GV\Field_HTML_Template, but call filters here?
253  *
254  * @return void
255  */
256  public function render() {
257  if ( ! $entry = $this->entry->from_field( $this->field ) ) {
258  gravityview()->log->error( 'Entry is invalid for field. Returning empty.' );
259  return;
260  }
261 
262  /** Retrieve the value. */
263  $display_value = $value = $this->field->get_value( $this->view, $this->source, $entry );
264 
265  $source = $this->source;
266  $source_backend = $source ? $source::$backend : null;
267 
269  'field' => $this->field,
270  ) );
271 
272  /** Alter the display value according to Gravity Forms. */
273  if ( \GV\Source::BACKEND_GRAVITYFORMS === $source_backend && ! $this->field instanceof Internal_Field ) {
274 
275  /** Prevent any PHP warnings that may be generated. */
276  ob_start();
277 
278  $display_value = \GFCommon::get_lead_field_display( $this->field->field, $value, $entry['currency'], false, 'html' );
279 
280  if ( $errors = ob_get_clean() ) {
281  gravityview()->log->error( 'Errors when calling GFCommon::get_lead_field_display()', array( 'data' => $errors ) );
282  }
283 
284  // `gform_entry_field_value` expects a GF_Field, but $this->field->field can be NULL
285  if ( ! $this->field->field instanceof GF_Field ) {
286  $gf_field = \GF_Fields::create( $this->field->field );
287  }
288 
289  /** Call the Gravity Forms field value filter. */
290  $display_value = apply_filters( 'gform_entry_field_value', $display_value, $gf_field, $entry->as_entry(), $this->source->form );
291 
292  unset( $gf_field );
293 
294  /** Replace merge tags for admin-only fields. */
295  if ( ! empty( $this->field->field->adminOnly ) ) {
296  $display_value = \GravityView_API::replace_variables( $display_value, $this->form->form, $entry->as_entry(), false, false );
297  }
298  }
299 
300  $context = Template_Context::from_template( $this, compact( 'display_value', 'value' ) );
301 
302  /**
303  * Make various pieces of data available to the template
304  * under the $gravityview scoped variable.
305  *
306  * @filter `gravityview/template/field/context`
307  * @param \GV\Template_Context $context The context for this template.
308  * @since 2.0
309  */
310  $this->push_template_data( apply_filters( 'gravityview/template/field/context', $context ), 'gravityview' );
311 
312  /** Bake the template. */
313  ob_start();
314  $this->located_template = $this->get_template_part( static::$slug );
315  $output = ob_get_clean();
316 
317  if ( empty( $output ) ) {
318  /**
319  * @filter `gravityview_empty_value` What to display when a field is empty
320  * @deprecated Use the `gravityview/field/value/empty` filter instead
321  * @param string $value (empty string)
322  */
323  $output = apply_filters( 'gravityview_empty_value', $output );
324 
325  /**
326  * @filter `gravityview/field/value/empty` What to display when this field is empty.
327  * @param string $value The value to display (Default: empty string)
328  * @param \GV\Template_Context The template context this is being called from.
329  */
330  $output = apply_filters( 'gravityview/field/value/empty', $output, Template_Context::from_template( $this ) );
331 
332  $context = Template_Context::from_template( $this, compact( 'display_value', 'value' ) );
333  }
334 
335  gravityview()->log->info( 'Field template for field #{field_id} loaded: {located_template}', array( 'field_id' => $this->field->ID, 'located_template' => $this->located_template ) );
336 
337  $this->pop_template_data( 'gravityview' );
338 
339  /** A compatibility array that's required by some of the deprecated filters. */
340  $field_compat = array(
341  'form' => $source_backend == \GV\Source::BACKEND_GRAVITYFORMS ? $this->source->form : ( $this->view->form ? $this->view->form->form : null ),
342  'field_id' => $this->field->ID,
343  'field' => $this->field->field,
344  'field_settings' => $this->field->as_configuration(),
345  'value' => $value,
346  'display_value' => $display_value,
347  'format' => 'html',
348  'entry' => $entry->as_entry(),
349  'field_type' => $this->field->type,
350  'field_path' => $this->located_template,
351  );
352 
353  /**
354  * Wrap output in a link, if enabled in the field settings
355  *
356  * @todo Cleanup
357  *
358  * @param string $output HTML value output
359  * @param \GV\Template_Context $context
360  *
361  * @return mixed|string|void
362  */
363  $pre_link_compat_callback = function( $output, $context ) use ( $field_compat ) {
364  $field = $context->field;
365 
366  /**
367  * @filter `gravityview_field_entry_value_{$field_type}_pre_link` Modify the field value output for a field type before Show As Link setting is applied. Example: `gravityview_field_entry_value_number_pre_link`
368  * @since 1.16
369  * @param string $output HTML value output
370  * @param array $entry The GF entry array
371  * @param array $field_settings Settings for the particular GV field
372  * @param array $field Field array, as fetched from GravityView_View::getCurrentField()
373  *
374  * @deprecated Use the `gravityview/field/{$field_type}/output` or `gravityview/field/output` filters instead.
375  */
376  $output = apply_filters( "gravityview_field_entry_value_{$field->type}_pre_link", $output, $context->entry->as_entry(), $field->as_configuration(), $field_compat );
377 
378  $output = apply_filters( 'gravityview_field_entry_value_pre_link', $output, $context->entry->as_entry(), $field->as_configuration(), $field_compat );
379 
380  /**
381  * Link to the single entry by wrapping the output in an anchor tag
382  *
383  * Fields can override this by modifying the field data variable inside the field. See /templates/fields/post_image.php for an example.
384  */
385  if ( ! empty( $field->show_as_link ) && ! \gv_empty( $output, false, false ) ) {
386  $link_atts = empty( $field->new_window ) ? array() : array( 'target' => '_blank' );
387 
388  $permalink = $context->entry->get_permalink( $context->view, $context->request );
389  $output = \gravityview_get_link( $permalink, $output, $link_atts );
390 
391  /**
392  * @filter `gravityview_field_entry_link` Modify the link HTML
393  * @param string $link HTML output of the link
394  * @param string $href URL of the link
395  * @param array $entry The GF entry array
396  * @param array $field_settings Settings for the particular GV field
397  * @deprecated Use `gravityview/template/field/entry_link`
398  */
399  $output = apply_filters( 'gravityview_field_entry_link', $output, $permalink, $context->entry->as_entry(), $field->as_configuration() );
400 
401  /**
402  * @filter `gravityview/template/field/entry_link` Modify the link HTML
403  * @since 2.0
404  * @param string $link HTML output of the link
405  * @param string $href URL of the link
406  * @param \GV\Template_Context $context The context
407  */
408  $output = apply_filters( 'gravityview/template/field/entry_link', $output, $permalink, $context );
409  }
410 
411  return $output;
412  };
413 
414  // TODO Cleanup
415  $post_link_compat_callback = function( $output, $context ) use ( $field_compat ) {
416  $field = $context->field;
417 
418  /**
419  * @filter `gravityview_field_entry_value_{$field_type}` Modify the field value output for a field type. Example: `gravityview_field_entry_value_number`
420  * @since 1.6
421  * @param string $output HTML value output
422  * @param array $entry The GF entry array
423  * @param array $field_settings Settings for the particular GV field
424  * @param array $field Current field being displayed
425  *
426  * @deprecated Use the `gravityview/field/{$field_type}/output` or `gravityview/field/output` filters instead.
427  */
428  $output = apply_filters( "gravityview_field_entry_value_{$field->type}", $output, $context->entry->as_entry(), $field->as_configuration(), $field_compat );
429 
430  /**
431  * @filter `gravityview_field_entry_value` Modify the field value output for all field types
432  * @param string $output HTML value output
433  * @param array $entry The GF entry array
434  * @param array $field_settings Settings for the particular GV field
435  * @param array $field_data {@since 1.6}
436  *
437  * @deprecated Use the `gravityview/field/{$field_type}/output` or `gravityview/field/output` filters instead.
438  */
439  $output = apply_filters( 'gravityview_field_entry_value', $output, $context->entry->as_entry(), $field->as_configuration(), $field_compat );
440 
441  /**
442  * @filter `gravityview/template/field/{$field_type}/output` Modify the field output for a field type.
443  *
444  * @since 2.0
445  *
446  * @param string $output The current output.
447  * @param \GV\Template_Context The template context this is being called from.
448  */
449  return apply_filters( "gravityview/template/field/{$field->type}/output", $output, $context );
450  };
451 
452  /**
453  * Okay, what's this whole pre/post_link compat deal, huh?
454  *
455  * Well, the `gravityview_field_entry_value_{$field_type}_pre_link` filter
456  * is expected to be applied before the value is turned into an entry link.
457  *
458  * And then `gravityview_field_entry_value_{$field_type}` and `gravityview_field_entry_value`
459  * are called afterwards.
460  *
461  * So we're going to use filter priorities to make sure this happens inline with
462  * our new filters, in the correct sequence. Pre-link called with priority 5 and
463  * post-link called with priority 9. Then everything else.
464  *
465  * If a new code wants to alter the value before it is hyperlinked (hyperlinkified?),
466  * it should hook into a priority between -inf. and 8. Afterwards: 10 to +inf.
467  */
468  add_filter( 'gravityview/template/field/output', $pre_link_compat_callback, 5, 2 );
469  add_filter( 'gravityview/template/field/output', $post_link_compat_callback, 9, 2 );
470 
471  /**
472  * @filter `gravityview/template/field/output` Modify the field output for a field.
473  *
474  * @since 2.0
475  *
476  * @param string $output The current output.
477  * @param \GV\Template_Context The template this is being called from.
478  */
479  echo apply_filters( "gravityview/template/field/output", $output, $context );
480 
481  remove_filter( 'gravityview/template/field/output', $pre_link_compat_callback, 5 );
482  remove_filter( 'gravityview/template/field/output', $post_link_compat_callback, 9 );
483  }
484 }
485 
486 /** Load implementations. */
487 require gravityview()->plugin->dir( 'future/includes/class-gv-template-field-html.php' );
488 require gravityview()->plugin->dir( 'future/includes/class-gv-template-field-csv.php' );
If this file is called directly, abort.
If this file is called directly, abort.
__construct(Field $field, View $view=null, Source $source=null, Entry $entry=null, Request $request=null)
Initializer.
static add_id_specific_templates( $template)
Enable granular template overrides based on current post, view, form, field types, etc.
if(! isset( $gravityview)||empty( $gravityview->template)) $template
The entry loop for the list output.
$display_value
render()
Output some HTML.
$class
gravityview_get_link( $href='', $anchor_text='', $atts=array())
Generate an HTML anchor tag with a list of supported attributes.
If this file is called directly, abort.
gravityview()
Definition: _stubs.php:26
$templates
global $post
Definition: delete-entry.php:7
static load( $configuration)
Hydrates the legacy context globals as needed.
Definition: _mocks.php:552
If this file is called directly, abort.
$field_id
Definition: time.php:17
static replace_variables( $text, $form=array(), $entry=array(), $url_encode=false, $esc_html=true, $nl2br=true, $format='html', $aux_data=array())
Alias for GravityView_Merge_Tags::replace_variables()
Definition: class-api.php:118
If this file is called directly, abort.
if(empty( $created_by)) $form_id
If this file is called directly, abort.
If this file is called directly, abort.
If this file is called directly, abort.
gv_empty( $value, $zero_is_empty=true, $allow_string_booleans=true)
Is the value empty?
$entry
Definition: notes.php:27
If this file is called directly, abort.
$field
Definition: gquiz_grade.php:11