PluginProbe
Advanced Custom Fields (ACF®) / 4.4.12
Advanced Custom Fields (ACF®) v4.4.12
6.8.9 6.8.8 6.8.7 6.8.6 6.8.5 6.8.4 6.8.3 6.8.2 6.8.1 5.8.5 5.8.6 5.8.7 5.8.8 5.8.9 5.9.0 5.9.1 5.9.2 5.9.3 5.9.4 5.9.5 5.9.6 5.9.7 5.9.8 5.9.9 6.0.0 All 230 releases
advanced-custom-fields / core / fields / relationship.php
relationship.php
905 lines 17.9 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 class acf_field_relationship extends acf_field
4 {
5 /*
6 * __construct
7 *
8 * Set name / label needed for actions / filters
9 *
10 * @since 3.6
11 * @date 23/01/13
12 */
13
14 function __construct()
15 {
16 // vars
17 $this->name = 'relationship';
18 $this->label = __("Relationship",'acf');
19 $this->category = __("Relational",'acf');
20 $this->defaults = array(
21 'post_type' => array('all'),
22 'max' => '',
23 'taxonomy' => array('all'),
24 'filters' => array('search'),
25 'result_elements' => array('post_title', 'post_type'),
26 'return_format' => 'object'
27 );
28 $this->l10n = array(
29 'max' => __("Maximum values reached ( {max} values )",'acf')
30 );
31
32
33 // do not delete!
34 parent::__construct();
35
36
37 // extra
38 add_action('wp_ajax_acf/fields/relationship/query_posts', array($this, 'query_posts'));
39 add_action('wp_ajax_nopriv_acf/fields/relationship/query_posts', array($this, 'query_posts'));
40 }
41
42
43 /*
44 * load_field()
45 *
46 * This filter is appied to the $field after it is loaded from the database
47 *
48 * @type filter
49 * @since 3.6
50 * @date 23/01/13
51 *
52 * @param $field - the field array holding all the field options
53 *
54 * @return $field - the field array holding all the field options
55 */
56
57 function load_field( $field )
58 {
59 // validate post_type
60 if( !$field['post_type'] || !is_array($field['post_type']) || in_array('', $field['post_type']) )
61 {
62 $field['post_type'] = array( 'all' );
63 }
64
65
66 // validate taxonomy
67 if( !$field['taxonomy'] || !is_array($field['taxonomy']) || in_array('', $field['taxonomy']) )
68 {
69 $field['taxonomy'] = array( 'all' );
70 }
71
72
73 // validate result_elements
74 if( !is_array( $field['result_elements'] ) )
75 {
76 $field['result_elements'] = array();
77 }
78
79 if( !in_array('post_title', $field['result_elements']) )
80 {
81 $field['result_elements'][] = 'post_title';
82 }
83
84
85 // filters
86 if( !is_array( $field['filters'] ) )
87 {
88 $field['filters'] = array();
89 }
90
91
92 // return
93 return $field;
94 }
95
96
97 /*
98 * get_result
99 *
100 * description
101 *
102 * @type function
103 * @date 5/02/2015
104 * @since 5.1.5
105 *
106 * @param $post_id (int)
107 * @return $post_id (int)
108 */
109
110 function get_result( $post, $field, $the_post, $options = array() ) {
111
112 // right aligned info
113 $title = '<span class="relationship-item-info">';
114
115 if( in_array('post_type', $field['result_elements']) ) {
116
117 $post_type_object = get_post_type_object( $post->post_type );
118 $title .= $post_type_object->labels->singular_name;
119
120 }
121
122
123 // WPML
124 if( !empty($options['lang']) ) {
125
126 $title .= ' (' . $options['lang'] . ')';
127
128 } elseif( defined('ICL_LANGUAGE_CODE') ) {
129
130 $title .= ' (' . ICL_LANGUAGE_CODE . ')';
131
132 }
133
134 $title .= '</span>';
135
136
137 // featured_image
138 if( in_array('featured_image', $field['result_elements']) ) {
139
140 $image = '';
141
142 if( $post->post_type == 'attachment' ) {
143
144 $image = wp_get_attachment_image( $post->ID, array(21, 21) );
145
146 } else {
147
148 $image = get_the_post_thumbnail( $post->ID, array(21, 21) );
149
150 }
151
152 $title .= '<div class="result-thumbnail">' . $image . '</div>';
153
154 }
155
156
157 // title
158 $post_title = get_the_title( $post->ID );
159
160
161 // empty
162 if( $post_title === '' ) {
163
164 $post_title = __('(no title)', 'acf');
165
166 }
167
168
169 $title .= $post_title;
170
171
172 // status
173 if( get_post_status( $post->ID ) != "publish" ) {
174
175 $title .= ' (' . get_post_status( $post->ID ) . ')';
176
177 }
178
179
180 // filters
181 $title = apply_filters('acf/fields/relationship/result', $title, $post, $field, $the_post);
182 $title = apply_filters('acf/fields/relationship/result/name=' . $field['_name'] , $title, $post, $field, $the_post);
183 $title = apply_filters('acf/fields/relationship/result/key=' . $field['key'], $title, $post, $field, $the_post);
184
185
186 // return
187 return $title;
188
189 }
190
191
192 /*
193 * query_posts
194 *
195 * @description:
196 * @since: 3.6
197 * @created: 27/01/13
198 */
199
200 function query_posts()
201 {
202 // vars
203 $r = array(
204 'next_page_exists' => 1,
205 'html' => ''
206 );
207
208
209 // options
210 $options = array(
211 'post_type' => 'all',
212 'taxonomy' => 'all',
213 'posts_per_page' => 10,
214 'paged' => 1,
215 'orderby' => 'title',
216 'order' => 'ASC',
217 'post_status' => 'any',
218 'suppress_filters' => false,
219 's' => '',
220 'lang' => false,
221 'update_post_meta_cache' => false,
222 'field_key' => '',
223 'nonce' => '',
224 'ancestor' => false,
225 );
226
227 $options = array_merge( $options, $_POST );
228
229
230 // validate
231 if( ! wp_verify_nonce($options['nonce'], 'acf_nonce') )
232 {
233 die();
234 }
235
236
237 // WPML
238 if( $options['lang'] )
239 {
240 global $sitepress;
241
242 if( !empty($sitepress) )
243 {
244 $sitepress->switch_lang( $options['lang'] );
245 }
246 }
247
248
249 // convert types
250 $options['post_type'] = explode(',', $options['post_type']);
251 $options['taxonomy'] = explode(',', $options['taxonomy']);
252
253
254 // load all post types by default
255 if( in_array('all', $options['post_type']) )
256 {
257 $options['post_type'] = apply_filters('acf/get_post_types', array());
258 }
259
260
261 // attachment doesn't work if it is the only item in an array???
262 if( is_array($options['post_type']) && count($options['post_type']) == 1 )
263 {
264 $options['post_type'] = $options['post_type'][0];
265 }
266
267
268 // create tax queries
269 if( ! in_array('all', $options['taxonomy']) )
270 {
271 // vars
272 $taxonomies = array();
273 $options['tax_query'] = array();
274
275 foreach( $options['taxonomy'] as $v )
276 {
277
278 // find term (find taxonomy!)
279 // $term = array( 0 => $taxonomy, 1 => $term_id )
280 $term = explode(':', $v);
281
282
283 // validate
284 if( !is_array($term) || !isset($term[1]) )
285 {
286 continue;
287 }
288
289
290 // add to tax array
291 $taxonomies[ $term[0] ][] = $term[1];
292
293 }
294
295
296 // now create the tax queries
297 foreach( $taxonomies as $k => $v )
298 {
299 $options['tax_query'][] = array(
300 'taxonomy' => $k,
301 'field' => 'id',
302 'terms' => $v,
303 );
304 }
305 }
306
307 unset( $options['taxonomy'] );
308
309
310 // load field
311 $field = array();
312 if( $options['ancestor'] )
313 {
314 $ancestor = apply_filters('acf/load_field', array(), $options['ancestor'] );
315 $field = acf_get_child_field_from_parent_field( $options['field_key'], $ancestor );
316 }
317 else
318 {
319 $field = apply_filters('acf/load_field', array(), $options['field_key'] );
320 }
321
322
323 // get the post from which this field is rendered on
324 $the_post = get_post( $options['post_id'] );
325
326
327 // filters
328 $options = apply_filters('acf/fields/relationship/query', $options, $field, $the_post);
329 $options = apply_filters('acf/fields/relationship/query/name=' . $field['_name'], $options, $field, $the_post );
330 $options = apply_filters('acf/fields/relationship/query/key=' . $field['key'], $options, $field, $the_post );
331
332
333 // query
334 $wp_query = new WP_Query( $options );
335
336
337 // global
338 global $post;
339
340
341 // loop
342 while( $wp_query->have_posts() ) {
343
344 $wp_query->the_post();
345
346
347 // get title
348 $title = $this->get_result($post, $field, $the_post, $options);
349
350
351 // update html
352 $r['html'] .= '<li><a href="' . get_permalink($post->ID) . '" data-post_id="' . $post->ID . '">' . $title . '<span class="acf-button-add"></span></a></li>';
353
354 }
355
356
357 // next page
358 if( (int)$options['paged'] >= $wp_query->max_num_pages ) {
359
360 $r['next_page_exists'] = 0;
361
362 }
363
364
365 // reset
366 wp_reset_postdata();
367
368
369 // return JSON
370 echo json_encode( $r );
371
372 die();
373
374 }
375
376
377 /*
378 * create_field()
379 *
380 * Create the HTML interface for your field
381 *
382 * @param $field - an array holding all the field's data
383 *
384 * @type action
385 * @since 3.6
386 * @date 23/01/13
387 */
388
389 function create_field( $field )
390 {
391 // global
392 global $post;
393
394
395 // no row limit?
396 if( !$field['max'] || $field['max'] < 1 )
397 {
398 $field['max'] = 9999;
399 }
400
401
402 // class
403 $class = '';
404 if( $field['filters'] )
405 {
406 foreach( $field['filters'] as $filter )
407 {
408 $class .= ' has-' . $filter;
409 }
410 }
411
412 $attributes = array(
413 'max' => $field['max'],
414 's' => '',
415 'paged' => 1,
416 'post_type' => implode(',', $field['post_type']),
417 'taxonomy' => implode(',', $field['taxonomy']),
418 'field_key' => $field['key']
419 );
420
421
422 // Lang
423 if( defined('ICL_LANGUAGE_CODE') )
424 {
425 $attributes['lang'] = ICL_LANGUAGE_CODE;
426 }
427
428
429 // parent
430 preg_match('/\[(field_.*?)\]/', $field['name'], $ancestor);
431 if( isset($ancestor[1]) && $ancestor[1] != $field['key'])
432 {
433 $attributes['ancestor'] = $ancestor[1];
434 }
435
436 ?>
437 <div class="acf_relationship<?php echo $class; ?>"<?php foreach( $attributes as $k => $v ): ?> data-<?php echo $k; ?>="<?php echo $v; ?>"<?php endforeach; ?>>
438
439
440 <!-- Hidden Blank default value -->
441 <input type="hidden" name="<?php echo $field['name']; ?>" value="" />
442
443
444 <!-- Left List -->
445 <div class="relationship_left">
446 <table class="widefat">
447 <thead>
448 <?php if(in_array( 'search', $field['filters']) ): ?>
449 <tr>
450 <th>
451 <input class="relationship_search" placeholder="<?php _e("Search...",'acf'); ?>" type="text" id="relationship_<?php echo $field['name']; ?>" />
452 </th>
453 </tr>
454 <?php endif; ?>
455 <?php if(in_array( 'post_type', $field['filters']) ): ?>
456 <tr>
457 <th>
458 <?php
459
460 // vars
461 $choices = array(
462 'all' => __("Filter by post type",'acf')
463 );
464
465
466 if( in_array('all', $field['post_type']) )
467 {
468 $post_types = apply_filters( 'acf/get_post_types', array() );
469 $choices = array_merge( $choices, $post_types);
470 }
471 else
472 {
473 foreach( $field['post_type'] as $post_type )
474 {
475 $choices[ $post_type ] = $post_type;
476 }
477 }
478
479
480 // create field
481 do_action('acf/create_field', array(
482 'type' => 'select',
483 'name' => '',
484 'class' => 'select-post_type',
485 'value' => '',
486 'choices' => $choices,
487 ));
488
489 ?>
490 </th>
491 </tr>
492 <?php endif; ?>
493 </thead>
494 </table>
495 <ul class="bl relationship_list">
496 <li class="load-more">
497 <div class="acf-loading"></div>
498 </li>
499 </ul>
500 </div>
501 <!-- /Left List -->
502
503 <!-- Right List -->
504 <div class="relationship_right">
505 <ul class="bl relationship_list">
506 <?php
507
508 if( $field['value'] )
509 {
510 foreach( $field['value'] as $p )
511 {
512 $title = $this->get_result($p, $field, $post);
513
514
515 echo '<li>
516 <a href="' . get_permalink($p->ID) . '" class="" data-post_id="' . $p->ID . '">' . $title . '<span class="acf-button-remove"></span></a>
517 <input type="hidden" name="' . $field['name'] . '[]" value="' . $p->ID . '" />
518 </li>';
519
520
521 }
522 }
523
524 ?>
525 </ul>
526 </div>
527 <!-- / Right List -->
528
529 </div>
530 <?php
531 }
532
533
534
535 /*
536 * create_options()
537 *
538 * Create extra options for your field. This is rendered when editing a field.
539 * The value of $field['name'] can be used (like bellow) to save extra data to the $field
540 *
541 * @type action
542 * @since 3.6
543 * @date 23/01/13
544 *
545 * @param $field - an array holding all the field's data
546 */
547
548 function create_options( $field )
549 {
550 // vars
551 $key = $field['name'];
552
553 ?>
554 <tr class="field_option field_option_<?php echo $this->name; ?>">
555 <td class="label">
556 <label><?php _e("Return Format",'acf'); ?></label>
557 <p><?php _e("Specify the returned value on front end",'acf') ?></p>
558 </td>
559 <td>
560 <?php
561 do_action('acf/create_field', array(
562 'type' => 'radio',
563 'name' => 'fields['.$key.'][return_format]',
564 'value' => $field['return_format'],
565 'layout' => 'horizontal',
566 'choices' => array(
567 'object' => __("Post Objects",'acf'),
568 'id' => __("Post IDs",'acf')
569 )
570 ));
571 ?>
572 </td>
573 </tr>
574 <tr class="field_option field_option_<?php echo $this->name; ?>">
575 <td class="label">
576 <label for=""><?php _e("Post Type",'acf'); ?></label>
577 </td>
578 <td>
579 <?php
580
581 $choices = array(
582 'all' => __("All",'acf')
583 );
584 $choices = apply_filters('acf/get_post_types', $choices);
585
586
587 do_action('acf/create_field', array(
588 'type' => 'select',
589 'name' => 'fields['.$key.'][post_type]',
590 'value' => $field['post_type'],
591 'choices' => $choices,
592 'multiple' => 1,
593 ));
594
595 ?>
596 </td>
597 </tr>
598 <tr class="field_option field_option_<?php echo $this->name; ?>">
599 <td class="label">
600 <label><?php _e("Filter from Taxonomy",'acf'); ?></label>
601 </td>
602 <td>
603 <?php
604 $choices = array(
605 '' => array(
606 'all' => __("All",'acf')
607 )
608 );
609 $simple_value = false;
610 $choices = apply_filters('acf/get_taxonomies_for_select', $choices, $simple_value);
611
612
613 do_action('acf/create_field', array(
614 'type' => 'select',
615 'name' => 'fields['.$key.'][taxonomy]',
616 'value' => $field['taxonomy'],
617 'choices' => $choices,
618 'multiple' => 1,
619 ));
620 ?>
621 </td>
622 </tr>
623 <tr class="field_option field_option_<?php echo $this->name; ?>">
624 <td class="label">
625 <label><?php _e("Filters",'acf'); ?></label>
626 </td>
627 <td>
628 <?php
629 do_action('acf/create_field', array(
630 'type' => 'checkbox',
631 'name' => 'fields['.$key.'][filters]',
632 'value' => $field['filters'],
633 'choices' => array(
634 'search' => __("Search",'acf'),
635 'post_type' => __("Post Type Select",'acf'),
636 )
637 ));
638 ?>
639 </td>
640 </tr>
641 <tr class="field_option field_option_<?php echo $this->name; ?>">
642 <td class="label">
643 <label><?php _e("Elements",'acf'); ?></label>
644 <p><?php _e("Selected elements will be displayed in each result",'acf') ?></p>
645 </td>
646 <td>
647 <?php
648 do_action('acf/create_field', array(
649 'type' => 'checkbox',
650 'name' => 'fields['.$key.'][result_elements]',
651 'value' => $field['result_elements'],
652 'choices' => array(
653 'featured_image' => __("Featured Image",'acf'),
654 'post_title' => __("Post Title",'acf'),
655 'post_type' => __("Post Type",'acf'),
656 ),
657 'disabled' => array(
658 'post_title'
659 )
660 ));
661 ?>
662 </td>
663 </tr>
664 <tr class="field_option field_option_<?php echo $this->name; ?>">
665 <td class="label">
666 <label><?php _e("Maximum posts",'acf'); ?></label>
667 </td>
668 <td>
669 <?php
670 do_action('acf/create_field', array(
671 'type' => 'number',
672 'name' => 'fields['.$key.'][max]',
673 'value' => $field['max'],
674 ));
675 ?>
676 </td>
677 </tr>
678 <?php
679
680 }
681
682
683 /*
684 * format_value()
685 *
686 * This filter is appied to the $value after it is loaded from the db and before it is passed to the create_field action
687 *
688 * @type filter
689 * @since 3.6
690 * @date 23/01/13
691 *
692 * @param $value - the value which was loaded from the database
693 * @param $post_id - the $post_id from which the value was loaded
694 * @param $field - the field array holding all the field options
695 *
696 * @return $value - the modified value
697 */
698
699 function format_value( $value, $post_id, $field )
700 {
701 // empty?
702 if( !empty($value) )
703 {
704 // Pre 3.3.3, the value is a string coma seperated
705 if( is_string($value) )
706 {
707 $value = explode(',', $value);
708 }
709
710
711 // convert to integers
712 if( is_array($value) )
713 {
714 $value = array_map('intval', $value);
715
716 // convert into post objects
717 $value = $this->get_posts( $value );
718 }
719
720 }
721
722
723 // return value
724 return $value;
725 }
726
727
728 /*
729 * format_value_for_api()
730 *
731 * This filter is appied to the $value after it is loaded from the db and before it is passed back to the api functions such as the_field
732 *
733 * @type filter
734 * @since 3.6
735 * @date 23/01/13
736 *
737 * @param $value - the value which was loaded from the database
738 * @param $post_id - the $post_id from which the value was loaded
739 * @param $field - the field array holding all the field options
740 *
741 * @return $value - the modified value
742 */
743
744 function format_value_for_api( $value, $post_id, $field )
745 {
746 // empty?
747 if( !$value )
748 {
749 return $value;
750 }
751
752
753 // Pre 3.3.3, the value is a string coma seperated
754 if( is_string($value) )
755 {
756 $value = explode(',', $value);
757 }
758
759
760 // empty?
761 if( !is_array($value) || empty($value) )
762 {
763 return $value;
764 }
765
766
767 // convert to integers
768 $value = array_map('intval', $value);
769
770
771 // return format
772 if( $field['return_format'] == 'object' )
773 {
774 $value = $this->get_posts( $value );
775 }
776
777
778 // return
779 return $value;
780
781 }
782
783
784 /*
785 * get_posts
786 *
787 * This function will take an array of post_id's ($value) and return an array of post_objects
788 *
789 * @type function
790 * @date 7/08/13
791 *
792 * @param $post_ids (array) the array of post ID's
793 * @return (array) an array of post objects
794 */
795
796 function get_posts( $post_ids )
797 {
798 // validate
799 if( empty($post_ids) )
800 {
801 return $post_ids;
802 }
803
804
805 // vars
806 $r = array();
807
808
809 // find posts (DISTINCT POSTS)
810 $posts = get_posts(array(
811 'numberposts' => -1,
812 'post__in' => $post_ids,
813 'post_type' => apply_filters('acf/get_post_types', array()),
814 'post_status' => 'any',
815 ));
816
817
818 $ordered_posts = array();
819 foreach( $posts as $p )
820 {
821 // create array to hold value data
822 $ordered_posts[ $p->ID ] = $p;
823 }
824
825
826 // override value array with attachments
827 foreach( $post_ids as $k => $v)
828 {
829 // check that post exists (my have been trashed)
830 if( isset($ordered_posts[ $v ]) )
831 {
832 $r[] = $ordered_posts[ $v ];
833 }
834 }
835
836
837 // return
838 return $r;
839 }
840
841
842 /*
843 * update_value()
844 *
845 * This filter is appied to the $value before it is updated in the db
846 *
847 * @type filter
848 * @since 3.6
849 * @date 23/01/13
850 *
851 * @param $value - the value which will be saved in the database
852 * @param $post_id - the $post_id of which the value will be saved
853 * @param $field - the field array holding all the field options
854 *
855 * @return $value - the modified value
856 */
857
858 function update_value( $value, $post_id, $field )
859 {
860 // validate
861 if( empty($value) )
862 {
863 return $value;
864 }
865
866
867 if( is_string($value) )
868 {
869 // string
870 $value = explode(',', $value);
871
872 }
873 elseif( is_object($value) && isset($value->ID) )
874 {
875 // object
876 $value = array( $value->ID );
877
878 }
879 elseif( is_array($value) )
880 {
881 // array
882 foreach( $value as $k => $v ){
883
884 // object?
885 if( is_object($v) && isset($v->ID) )
886 {
887 $value[ $k ] = $v->ID;
888 }
889 }
890
891 }
892
893
894 // save value as strings, so we can clearly search for them in SQL LIKE statements
895 $value = array_map('strval', $value);
896
897
898 return $value;
899 }
900
901 }
902
903 new acf_field_relationship();
904
905 ?>