PluginProbe ʕ •ᴥ•ʔ
Advanced Custom Fields: Extended / 0.9.2.7
Advanced Custom Fields: Extended v0.9.2.7
0.9.2.7 0.9.2.6 0.9.2.5 0.8.6 0.8.6.1 0.8.6.3 0.8.6.5 0.8.6.6 0.8.6.7 0.8.6.8 0.8.6.9 0.8.7 0.8.7.1 0.8.7.2 0.8.7.3 0.8.7.4 0.8.7.5 0.8.7.6 0.8.8 0.8.8.1 0.8.8.10 0.8.8.11 0.8.8.2 0.8.8.3 0.8.8.4 0.8.8.5 0.8.8.6 0.8.8.7 0.8.8.8 0.8.8.9 0.8.9 0.8.9.1 0.8.9.2 0.8.9.3 0.8.9.4 0.8.9.5 0.9 0.9.0.1 0.9.0.2 0.9.0.3 0.9.0.4 0.9.0.5 0.9.0.6 0.9.0.7 0.9.0.8 0.9.0.9 0.9.1 0.9.1.1 0.9.2 0.9.2.1 0.9.2.2 0.9.2.3 0.9.2.4 trunk 0.5 0.5.1 0.5.2 0.5.2.1 0.5.2.3 0.5.5 0.5.5.1 0.5.8 0.5.8.1 0.6 0.6.0.1 0.6.0.2 0.6.1 0.6.3 0.6.5 0.6.7 0.6.7.2 0.7 0.7.0.3 0.7.5 0.7.5.5 0.7.8 0.7.9 0.7.9.3 0.7.9.4 0.7.9.9.8 0.7.9.9.9 0.8 0.8.1 0.8.2 0.8.3 0.8.3.1 0.8.4 0.8.4.1 0.8.4.5 0.8.4.6 0.8.5 0.8.5.5
acf-extended / includes / fields-settings / bidirectional.php
acf-extended / includes / fields-settings Last commit date
bidirectional.php 3 months ago choices-label.php 3 months ago data.php 3 months ago instructions.php 3 months ago permissions.php 3 months ago settings.php 3 months ago validation.php 3 months ago
bidirectional.php
820 lines
1 <?php
2
3 if(!defined('ABSPATH')){
4 exit;
5 }
6
7 if(!class_exists('acfe_bidirectional')):
8
9 class acfe_bidirectional{
10
11 // vars
12 var $allowed_types = array('relationship', 'post_object', 'user', 'taxonomy');
13
14 /**
15 * construct
16 */
17 function __construct(){
18
19 foreach($this->allowed_types as $allowed_type){
20
21 add_action("acf/render_field_settings/type={$allowed_type}", array($this, 'field_settings_render'));
22 add_filter("acf/update_field/type={$allowed_type}", array($this, 'field_settings_update'));
23 add_action("acf/delete_field/type={$allowed_type}", array($this, 'field_settings_delete'));
24 add_filter("acf/update_value/type={$allowed_type}", array($this, 'update_value'), 11, 3);
25
26 }
27
28 add_action('wp_ajax_acfe/fields_settings/bidirectional/query', array($this, 'ajax_query'));
29 add_action('wp_ajax_nopriv_acfe/fields_settings/bidirectional/query', array($this, 'ajax_query'));
30
31 add_filter('acf/prepare_field/name=acfe_bidirectional_related', array($this, 'field_settings_default_value'));
32
33 }
34
35
36 /**
37 * field_settings_render
38 *
39 * @param $field
40 */
41 function field_settings_render($field){
42
43 // Settings
44 acf_render_field_setting($field, array(
45 'label' => __('Bidirectional'),
46 'key' => 'acfe_bidirectional',
47 'name' => 'acfe_bidirectional',
48 'instructions' => __('ACF Extended: Set the field as bidirectional', 'acfe'),
49 'type' => 'group',
50 'required' => false,
51 'conditional_logic' => false,
52 'wrapper' => array(
53 'width' => '',
54 'class' => '',
55 'id' => '',
56 ),
57 'layout' => 'block',
58 'sub_fields' => array(
59 array(
60 'label' => false,
61 'key' => 'acfe_bidirectional_enabled',
62 'name' => 'acfe_bidirectional_enabled',
63 'type' => 'true_false',
64 'instructions' => '',
65 'required' => false,
66 'wrapper' => array(
67 'width' => '15',
68 'class' => 'acfe_width_auto',
69 'id' => '',
70 ),
71 'message' => '',
72 'default_value' => false,
73 'ui' => true,
74 'ui_on_text' => '',
75 'ui_off_text' => '',
76 'conditional_logic' => false,
77 ),
78 array(
79 'label' => false,
80 'key' => 'acfe_bidirectional_related',
81 'name' => 'acfe_bidirectional_related',
82 'type' => 'select',
83 'instructions' => '',
84 'required' => false,
85 'wrapper' => array(
86 'width' => 50,
87 'class' => '',
88 'id' => '',
89 ),
90 'choices' => array(),
91 'default_value' => array(),
92 'allow_null' => 1,
93 'multiple' => 1,
94 'ui' => 1,
95 'ajax' => 1,
96 'ajax_action' => 'acfe/fields_settings/bidirectional/query',
97 'return_format' => 'value',
98 'placeholder' => '',
99 'conditional_logic' => array(
100 array(
101 array(
102 'field' => 'acfe_bidirectional_enabled',
103 'operator' => '==',
104 'value' => '1',
105 ),
106 ),
107 ),
108 ),
109 ),
110 ));
111
112 }
113
114
115 /**
116 * ajax_query
117 */
118 function ajax_query(){
119
120 // validate
121 if(!acf_verify_ajax()){
122 die();
123 }
124
125 $options = acf_parse_args($_POST, array(
126 'post_id' => 0,
127 's' => '',
128 'field_key' => '',
129 'paged' => 1
130 ));
131
132 $response = $this->ajax_results($options);
133
134 acf_send_ajax_results($response);
135
136 }
137
138
139 /**
140 * ajax_results
141 *
142 * @param $options
143 *
144 * @return array[]|false
145 */
146 function ajax_results($options = array()){
147
148 // disable Filters
149 acf_disable_filters();
150
151 // get field groups
152 $r_field_groups = acf_get_field_groups();
153
154 if(empty($r_field_groups)){
155 return false;
156 }
157
158 // vars
159 $hidden = acfe_get_setting('reserved_field_groups', array());
160 $choices = array();
161
162 foreach($r_field_groups as $r_field_group){
163
164 // bypass ACFE native groups
165 if(in_array($r_field_group['key'], $hidden)){
166 continue;
167 }
168
169 // get related fields
170 $r_fields = acf_get_fields($r_field_group['key']);
171 if(empty($r_fields)){
172 continue;
173 }
174
175 // filter & find possible related fields
176 foreach($r_fields as $r_field){
177 $this->get_related_settings($r_field, $r_field_group, $choices);
178 }
179
180 }
181
182 // vars
183 $results = array();
184 $s = null;
185
186 if(!empty($choices)){
187
188 // search
189 if($options['s'] !== ''){
190
191 // strip slashes (search may be integer)
192 $s = strval($options['s']);
193 $s = wp_unslash($s);
194
195 }
196
197 foreach($choices as $field_group_title => $childs){
198
199 $field_group_title = strval($field_group_title);
200
201 $childrens = array();
202 foreach($childs as $child_key => $child_label){
203
204 $child_label = strval($child_label);
205
206 // if searching, but doesn't exist
207 if(is_string($s) && stripos($child_label, $s) === false && stripos($field_group_title, $s) === false){
208 continue(2);
209 }
210
211 $childrens[] = array(
212 'id' => $child_key,
213 'text' => $child_label,
214 );
215
216 }
217
218 $results[] = array(
219 'text' => $field_group_title,
220 'children' => $childrens
221 );
222
223 }
224
225 }
226
227 return array(
228 'results' => $results
229 );
230
231 }
232
233 /**
234 * get_related_settings
235 *
236 * @param $r_field
237 * @param $r_field_group
238 * @param $choices
239 *
240 * @return false|void
241 */
242 function get_related_settings($r_field, $r_field_group, &$choices){
243
244 if(in_array($r_field['type'], array('repeater', 'flexible_content'))){
245 return;
246 }
247
248 // recursive search for sub_fields (groups & clones)
249 if(isset($r_field['sub_fields']) && !empty($r_field['sub_fields'])){
250
251 foreach($r_field['sub_fields'] as $r_sub_field){
252
253 // recursive call
254 $this->get_related_settings($r_sub_field, $r_field_group, $choices);
255
256 }
257
258 return;
259
260 }
261
262 // allow only specific fields
263 if(!in_array($r_field['type'], $this->allowed_types)){
264 return false;
265 }
266
267 $choices[ $r_field_group['title'] ][ $r_field['key'] ] = (!empty($r_field['label']) ? $r_field['label'] : $r_field['name']) . ' (' . $r_field['key'] . ')';
268
269 }
270
271
272 /**
273 * field_settings_default_value
274 *
275 * @param $field
276 *
277 * @return mixed
278 */
279 function field_settings_default_value($field){
280
281 if(!isset($field['value']) || empty($field['value'])){
282 return $field;
283 }
284
285 $values = acfe_as_array($field['value']);
286 $r_fields = array();
287 $r_field_update = false;
288
289 foreach($values as $i => $value){
290
291 $r_field = acf_get_field($value);
292
293 if($r_field){
294 $r_fields[] = $r_field;
295
296 }else{
297
298 unset($values[$i]);
299 $r_field_update = true;
300
301 }
302
303 }
304
305 if($r_field_update){
306
307 $field['value'] = $values;
308 acf_update_field($field);
309
310 }
311
312 foreach($r_fields as $r_field){
313 $field['choices'][ $r_field['key'] ] = (!empty($r_field['label']) ? $r_field['label'] : $r_field['name']) . ' (' . $r_field['key'] . ')';
314 }
315
316 return $field;
317
318 }
319
320
321 /**
322 * field_settings_update
323 *
324 * @param $field
325 *
326 * @return mixed
327 */
328 function field_settings_update($field){
329
330 // bypass
331 if(acf_is_filter_enabled('acfe/bidirectional_setting')){
332 return $field;
333 }
334
335 // previous setting values
336 $_field = acf_get_field($field['key']);
337
338 // turning off - Remove related field
339 if($this->has_field_bidirectional($_field) && !$this->has_field_bidirectional($field)){
340
341 // get related bidirectional related
342 $r_fields = acfe_as_array($_field['acfe_bidirectional']['acfe_bidirectional_related']);
343
344 foreach($r_fields as $r_field_key){
345
346 $r_field = acf_get_field($r_field_key);
347
348 if(!$this->has_field_bidirectional($r_field)){
349 continue;
350 }
351
352 $r_field_related = acfe_as_array($r_field['acfe_bidirectional']['acfe_bidirectional_related']);
353
354 if(in_array($field['key'], $r_field_related)){
355
356 foreach($r_field_related as $i => $r_field_r){
357
358 if($r_field_r !== $field['key']){
359 continue;
360 }
361
362 unset($r_field_related[$i]);
363
364 }
365
366 $r_field['acfe_bidirectional']['acfe_bidirectional_related'] = $r_field_related;
367
368 if(empty($r_field_related)){
369
370 $r_field['acfe_bidirectional']['acfe_bidirectional_enabled'] = false;
371 $r_field['acfe_bidirectional']['acfe_bidirectional_related'] = false;
372
373 }
374
375 acf_enable_filter('acfe/bidirectional_setting');
376
377 // Update related bidirectional
378 acf_update_field($r_field);
379
380 // Update field group (json/php sync)
381 $field_group = acfe_get_field_group_from_field($r_field);
382 acf_update_field_group($field_group);
383
384 acf_disable_filter('acfe/bidirectional_setting');
385
386 }
387
388 }
389
390 }
391
392 // turning on (or already on) - add related field
393 elseif(($this->has_field_bidirectional($_field) && $this->has_field_bidirectional($field)) || (!$this->has_field_bidirectional($_field) && $this->has_field_bidirectional($field))){
394
395 // get related bidirectional related
396 $r_fields = acfe_as_array($field['acfe_bidirectional']['acfe_bidirectional_related']);
397
398 foreach($r_fields as $r_field_key){
399
400 $r_field = acf_get_field($r_field_key);
401
402 // reset related bidirectional related
403 $r_field['acfe_bidirectional']['acfe_bidirectional_enabled'] = true;
404
405 $r_field_related = array();
406
407 if(isset($r_field['acfe_bidirectional']['acfe_bidirectional_related'])){
408 $r_field_related = acfe_as_array($r_field['acfe_bidirectional']['acfe_bidirectional_related']);
409 }
410
411 if(!in_array($field['key'], $r_field_related)){
412
413 $r_field_related[] = $field['key'];
414
415 $r_field['acfe_bidirectional']['acfe_bidirectional_related'] = $r_field_related;
416
417 }
418
419 acf_enable_filter('acfe/bidirectional_setting');
420
421 // update related bidirectional
422 acf_update_field($r_field);
423
424 // update field group (json/php sync)
425 $field_group = acfe_get_field_group_from_field($r_field);
426 acf_update_field_group($field_group);
427
428 acf_disable_filter('acfe/bidirectional_setting');
429
430 }
431
432 }
433
434 // return
435 return $field;
436
437 }
438
439
440 /**
441 * field_settings_delete
442 *
443 * @param $field
444 */
445 function field_settings_delete($field){
446
447 if(!$this->has_field_bidirectional($field)){
448 return;
449 }
450
451 // get related bidirectional related
452 $r_fields = acfe_as_array($field['acfe_bidirectional']['acfe_bidirectional_related']);
453
454 foreach($r_fields as $r_field_key){
455
456 $r_field = acf_get_field($r_field_key);
457
458 if(!$r_field){
459 continue;
460 }
461
462 $r_field_related = acfe_as_array($r_field['acfe_bidirectional']['acfe_bidirectional_related']);
463
464 if(in_array($field['key'], $r_field_related)){
465
466 foreach($r_field_related as $i => $r_field_r){
467
468 if($r_field_r !== $field['key']){
469 continue;
470 }
471
472 unset($r_field_related[$i]);
473
474 }
475
476 $r_field['acfe_bidirectional']['acfe_bidirectional_related'] = $r_field_related;
477
478 if(empty($r_field_related)){
479
480 $r_field['acfe_bidirectional']['acfe_bidirectional_enabled'] = false;
481 $r_field['acfe_bidirectional']['acfe_bidirectional_related'] = false;
482
483 }
484
485 // update related bidirectional
486 acf_update_field($r_field);
487
488 // update field group (json/php sync)
489 $field_group = acfe_get_field_group_from_field($r_field);
490 acf_update_field_group($field_group);
491
492 }
493
494 }
495
496 }
497
498
499 /**
500 * update_value
501 *
502 * @param $value
503 * @param $post_id
504 * @param $field
505 *
506 * @return mixed
507 */
508 function update_value($value, $post_id, $field){
509
510 // bail early if updating a relation
511 if(acf_is_filter_enabled('acfe/bidirectional')){
512 return $value;
513 }
514
515 // bail early if no bidirectional setting
516 if(!$this->get_field_bidirectional($field)){
517 return $value;
518 }
519
520 // bail early if local meta
521 if(acfe_is_local_post_id($post_id)){
522 return $value;
523 }
524
525 // bail early if previewing a post
526 if(acf_maybe_get_POST('wp-preview') === 'dopreview'){
527 return $value;
528 }
529
530 // decode current post_id (ie: user_1)
531 $request = acf_decode_post_id($post_id);
532
533 // values
534 $old_values = acfe_as_array(acf_get_metadata($post_id, $field['name']));
535 $new_values = acfe_as_array($value);
536
537 // bail early if no difference
538 // if($old_values === $new_values)
539 // return $value;
540
541 // values have been removed
542 if(!empty($old_values)){
543 foreach($old_values as $r_id){
544
545 if(in_array($r_id, $new_values)){
546 continue;
547 }
548
549 $this->relationship('remove', $r_id, $field, $request['id']);
550
551 }
552 }
553
554 // Values have been added
555 if(!empty($new_values)){
556 foreach($new_values as $r_id){
557
558 if(in_array($r_id, $old_values)){
559 continue;
560 }
561
562 $this->relationship('add', $r_id, $field, $request['id']);
563
564 }
565 }
566
567 $force_update = false;
568 $force_update = apply_filters("acfe/bidirectional/force_update", $force_update, $field, $post_id);
569 $force_update = apply_filters("acfe/bidirectional/force_update/type={$field['type']}", $force_update, $field, $post_id);
570 $force_update = apply_filters("acfe/bidirectional/force_update/name={$field['name']}", $force_update, $field, $post_id);
571 $force_update = apply_filters("acfe/bidirectional/force_update/key={$field['key']}", $force_update, $field, $post_id);
572
573 if($force_update){
574
575 // force new values to be saved
576 if(!empty($new_values)){
577 foreach($new_values as $r_id){
578
579 $this->relationship('add', $r_id, $field, $request['id']);
580
581 }
582 }
583
584 }
585
586 return $value;
587
588 }
589
590
591 /**
592 * relationship
593 *
594 * establish relationship
595 *
596 * @param $type = add|remove
597 * @param $r_id = the post_id to add the relationship to
598 * @param $p_field = the parent field
599 * @param $p_value = the relationship to add
600 */
601 function relationship($type, $r_id, $p_field, $p_value){
602
603 // get Related Field Configuration
604 $r_fields = acfe_as_array($p_field['acfe_bidirectional']['acfe_bidirectional_related']);
605
606 foreach($r_fields as $r_field_key){
607
608 $r_field = acf_get_field($r_field_key);
609
610 // get if bidirectional is active
611 if(!$this->get_field_bidirectional($r_field)) continue;
612
613 // get Related Data Type ({post_id}, user_{id} ...)
614 $r_mtype = '';
615 if($p_field['type'] === 'user'){
616 $r_mtype = 'user_';
617 }elseif($p_field['type'] === 'taxonomy'){
618 $r_mtype = 'term_';
619 }
620
621 // get Related Field Ancestors
622 $r_field_ancestors = acf_get_field_ancestors($r_field);
623
624 // ancestors - complex field (group|clone)
625 if(!empty($r_field_ancestors)){
626
627 // get ancestors
628 $r_field_ancestors = array_reverse($r_field_ancestors);
629 $r_field_ancestors_fields = array_map('acf_get_field', $r_field_ancestors);
630
631 // get top ancestor
632 $r_ref_field = $r_field_ancestors_fields[0];
633 $r_ref_values = acfe_as_array(acf_get_value($r_mtype.$r_id, $r_ref_field));
634
635 // get values
636 $r_values = acfe_as_array($this->get_value_from_ancestor($r_ref_values, $r_field));
637
638 // unset top ancestor for update (not needed)
639 unset($r_field_ancestors_fields[0]);
640
641 // add related field to get
642 $r_values_query = array($r_field['key']);
643
644 // if > 1 ancestors, return ancestors keys only
645 if(!empty($r_field_ancestors_fields)){
646
647 $r_field_ancestors_keys = array_map(function($field){
648 return $field['key'];
649 }, $r_field_ancestors_fields);
650
651 // add ancestors to get
652 $r_values_query = array_merge($r_field_ancestors_keys, $r_values_query);
653
654 }
655
656 }
657
658 // no Ancestors - simple field
659 else{
660
661 // reference field
662 $r_ref_field = $r_field;
663
664 // values
665 $r_values = acfe_as_array(acf_get_value($r_mtype.$r_id, $r_field));
666
667 }
668
669 // convert strings to integers
670 $r_values = acf_parse_types($r_values);
671
672 // add Value
673 if($type === 'add'){
674
675 if(!in_array($p_value, $r_values)){
676 $r_values[] = $p_value;
677 }
678
679 }
680
681 // remove Value
682 elseif($type === 'remove'){
683
684 $r_new_values = array();
685 foreach($r_values as $r_value){
686
687 if($r_value === $p_value) continue;
688
689 $r_new_values[] = $r_value;
690
691 }
692
693 $r_values = $r_new_values;
694
695 }
696
697 /**
698 * post object & user 'allow multiple' disabled
699 * value must not be inside array
700 */
701 if(($r_ref_field['type'] === 'post_object' || $r_ref_field['type'] === 'user') && empty($r_ref_field['multiple']) && isset($r_values[0])){
702
703 // Get latest value
704 $r_values = end($r_values);
705
706 }
707
708 // remove potential empty serialized array in meta value 'a:0:{}'
709 if(empty($r_values)){
710 $r_values = false;
711 }
712
713 /**
714 * construct a value array in case of ancestors. ie:
715 *
716 * $related_values = Array(
717 * [field_aaa] => Array(
718 * [field_bbb] => Array(
719 * [0] => xxxx
720 * )
721 * )
722 * )
723 */
724 if(!empty($r_field_ancestors)){
725
726 for($i = count($r_values_query)-1; $i>=0; $i--){
727 $r_values = array($r_values_query[$i] => $r_values);
728 }
729
730 }
731
732 // filter acf_update_value (to avoid infinite loop)
733 acf_enable_filter('acfe/bidirectional');
734
735 // update Related Field
736 acf_update_value($r_values, $r_mtype.$r_id, $r_ref_field);
737
738 // remove acf_update_value filter
739 acf_disable_filter('acfe/bidirectional');
740
741 }
742
743 }
744
745
746 /**
747 * get_value_from_ancestor
748 *
749 * @param $r_ref_values
750 * @param $r_field
751 *
752 * @return false|mixed|void
753 */
754 function get_value_from_ancestor($r_ref_values, $r_field){
755
756 foreach($r_ref_values as $r_ref_key => $r_ref_value){
757
758 if($r_ref_key != $r_field['key']){
759
760 if(is_array($r_ref_value)){
761 return $this->get_value_from_ancestor($r_ref_value, $r_field);
762 }
763
764 return false;
765
766 }
767
768 return $r_ref_value;
769
770 }
771
772 }
773
774
775 /**
776 * is_field_bidirectional
777 *
778 * @param $field
779 *
780 * @return bool
781 */
782 function is_field_bidirectional($field){
783 return isset($field['acfe_bidirectional']['acfe_bidirectional_enabled']) && !empty($field['acfe_bidirectional']['acfe_bidirectional_enabled']);
784 }
785
786
787 /**
788 * has_field_bidirectional
789 *
790 * @param $field
791 *
792 * @return bool
793 */
794 function has_field_bidirectional($field){
795 return isset($field['acfe_bidirectional']['acfe_bidirectional_related']) && !empty($field['acfe_bidirectional']['acfe_bidirectional_related']);
796 }
797
798
799 /**
800 * get_field_bidirectional
801 *
802 * @param $field
803 *
804 * @return false|mixed
805 */
806 function get_field_bidirectional($field){
807
808 if(!$this->is_field_bidirectional($field) || !$this->has_field_bidirectional($field)){
809 return false;
810 }
811
812 return $field['acfe_bidirectional']['acfe_bidirectional_related'];
813
814 }
815
816 }
817
818 new acfe_bidirectional();
819
820 endif;