├── admin ├── field-group.php └── views │ └── field-group-field-conditional-logic-advanced.php ├── assets ├── css │ └── acf-input.css └── js │ ├── acf-field-group.js │ └── acf-input.js ├── composer.json ├── index.php ├── lib └── AcfConditionalLogicAdvanced.php ├── readme.txt ├── screenshot-1.png └── screenshot-2.png /admin/field-group.php: -------------------------------------------------------------------------------- 1 | 0, 54 | 'group_id' => 0, 55 | 'rule_id' => 0, 56 | 'value' => null, 57 | 'param' => null, 58 | 'disabled' => false 59 | )); 60 | 61 | 62 | // vars 63 | $choices = array(); 64 | 65 | 66 | switch( $options['param'] ) { 67 | 68 | case "post_template" : 69 | 70 | // vars 71 | $choices = array( 72 | 'default' => apply_filters( 'default_page_template_title', __('Default Template', 'acf') ) 73 | ); 74 | 75 | // get templates (WP 4.7) 76 | if( acf_version_compare('wp', '>=', '4.7') ) { 77 | $templates = acf_get_post_templates(); 78 | $choices = array_merge($choices, $templates); 79 | } 80 | 81 | // break 82 | break; 83 | 84 | 85 | case "post_format" : 86 | 87 | $choices = get_post_format_strings(); 88 | 89 | break; 90 | 91 | 92 | case "post_category" : 93 | $taxonomies = array_map(function($taxonomyName) { 94 | return get_taxonomy($taxonomyName); 95 | }, acf_get_taxonomies()); 96 | 97 | $applicableTaxonomies = array_filter($taxonomies, function($taxonomy) { 98 | if ($taxonomy->name == 'post_format') return; 99 | return true; 100 | }); 101 | 102 | $choices = acf_get_taxonomy_terms(array_map(function($taxonomy) { 103 | return $taxonomy->name; 104 | }, $applicableTaxonomies)); 105 | 106 | break; 107 | } 108 | 109 | 110 | // allow custom location rules 111 | $choices = apply_filters( 'acf/conditional_logic_advanced/rule_values/' . $options['param'], $choices ); 112 | 113 | 114 | // create field 115 | acf_render_field(array( 116 | 'type' => 'select', 117 | 'prefix' => "acf_fields[{$options['field_id']}][conditional_logic_advanced][{$options['group_id']}][{$options['rule_id']}]", 118 | 'name' => 'value', 119 | 'value' => $options['value'], 120 | 'choices' => $choices, 121 | 'disabled' => $options['disabled'], 122 | )); 123 | 124 | } 125 | 126 | 127 | /* 128 | * ajax_render_location_value 129 | * 130 | * This function can be accessed via an AJAX action and will return the result from the render_location_value function 131 | * 132 | * @type function (ajax) 133 | * @date 30/09/13 134 | * @since 5.0.0 135 | * 136 | * @param n/a 137 | * @return n/a 138 | */ 139 | 140 | function ajax_render_conditional_logic_advanced_value() { 141 | 142 | // validate 143 | if( !acf_verify_ajax() ) { 144 | 145 | die(); 146 | 147 | } 148 | 149 | 150 | // call function 151 | $this->render_conditional_logic_advanced_value( $_POST ); 152 | 153 | 154 | // die 155 | die(); 156 | 157 | } 158 | } 159 | 160 | endif; -------------------------------------------------------------------------------- /admin/views/field-group-field-conditional-logic-advanced.php: -------------------------------------------------------------------------------- 1 | 'post_template', 19 | 'operator' => '==', 20 | 'value' => 'default', 21 | ) 22 | 23 | ) 24 | 25 | ); 26 | 27 | } 28 | 29 | // vars 30 | $rule_types = apply_filters('acf/conditional_logic_advanced/rule_types', array( 31 | __("Post",'acf') => array( 32 | 'post_template' => __("Post Template",'acf'), 33 | 'post_format' => __("Post Format",'acf'), 34 | 'post_category' => __("Post Taxonomy",'acf'), 35 | ), 36 | )); 37 | 38 | 39 | // WP < 4.7 40 | if( acf_version_compare('wp', '<', '4.7') ) { 41 | 42 | unset( $rule_types[ __("Post",'acf') ]['post_template'] ); 43 | 44 | } 45 | 46 | $rule_operators = apply_filters( 'acf/conditional_logic_advanced/rule_operators', array( 47 | '==' => __("is equal to",'acf'), 48 | '!=' => __("is not equal to",'acf'), 49 | )); 50 | 51 | ?> 52 | 53 | 54 | 55 | 56 | 57 | 'true_false', 61 | 'name' => 'conditional_logic_advanced', 62 | 'prefix' => $field['prefix'], 63 | 'value' => $disabled ? 0 : 1, 64 | 'ui' => 1, 65 | 'class' => 'conditional-logic-advanced-toggle', 66 | )); 67 | 68 | ?> 69 |
style="display:none;"> 70 | 71 | $group ): 72 | 73 | // validate 74 | if( empty($group) ) { 75 | 76 | continue; 77 | 78 | } 79 | 80 | 81 | // $group_id must be completely different to $rule_id to avoid JS issues 82 | $group_id = "group_{$group_id}"; 83 | $h4 = ($group_id == "group_0") ? __("Show this field group if",'acf') : __("or",'acf'); 84 | 85 | ?> 86 | 87 |
88 | 89 |

90 | 91 | 92 | 93 | $rule ): 94 | 95 | // valid rule 96 | $rule = wp_parse_args( $rule, array( 97 | 'field' => '', 98 | 'operator' => '==', 99 | 'value' => '', 100 | )); 101 | 102 | 103 | // $group_id must be completely different to $rule_id to avoid JS issues 104 | $rule_id = "rule_{$rule_id}"; 105 | $prefix = "{$field['prefix']}[conditional_logic_advanced][{$group_id}][{$rule_id}]"; 106 | 107 | ?> 108 | 109 | 123 | 137 | 149 | 152 | 155 | 156 | 157 | 158 |
'select', 114 | 'prefix' => $prefix, 115 | 'name' => 'param', 116 | 'value' => $rule['param'], 117 | 'choices' => $rule_types, 118 | 'class' => 'conditional-logic-advanced-rule-param', 119 | 'disabled' => $disabled, 120 | )); 121 | 122 | ?> 'select', 128 | 'prefix' => $prefix, 129 | 'name' => 'operator', 130 | 'value' => $rule['operator'], 131 | 'choices' => $rule_operators, 132 | 'class' => 'conditional-logic-advanced-rule-operator', 133 | 'disabled' => $disabled, 134 | )); 135 | 136 | ?>acfAdminFieldGroupdConditionalLogicAdvanced->render_conditional_logic_advanced_value(array( 140 | 'field_id' => $field['ID'], 141 | 'group_id' => $group_id, 142 | 'rule_id' => $rule_id, 143 | 'value' => $rule['value'], 144 | 'param' => $rule['param'], 145 | 'class' => 'conditional-logic-advanced-rule-value', 146 | 'disabled' => $disabled, 147 | )); 148 | ?> 150 | 151 | 153 | 154 |
159 | 160 |
161 | 162 | 163 |

164 | 165 | 166 | 167 |
168 | 169 | 170 | -------------------------------------------------------------------------------- /assets/css/acf-input.css: -------------------------------------------------------------------------------- 1 | /*-------------------------------------------------------------------------- 2 | * 3 | * Conditional Logic Advanced 4 | * 5 | *-------------------------------------------------------------------------*/ 6 | /* Hide */ 7 | .hidden-by-conditional-logic-advanced { 8 | display: none !important; 9 | } 10 | /* Hide (appear empty) */ 11 | .hidden-by-conditional-logic-advanced.appear-empty { 12 | display: table-cell !important; 13 | } 14 | .hidden-by-conditional-logic-advanced.appear-empty .acf-input { 15 | display: none !important; 16 | } 17 | -------------------------------------------------------------------------------- /assets/js/acf-field-group.js: -------------------------------------------------------------------------------- 1 | (function($){ 2 | 3 | /* 4 | * locations 5 | * 6 | * This model will handle location rule events 7 | * 8 | * @type function 9 | * @date 19/08/2015 10 | * @since 5.2.3 11 | * 12 | * @param $post_id (int) 13 | * @return $post_id (int) 14 | */ 15 | 16 | acf.field_group.conditional_logic_advanced = acf.model.extend({ 17 | 18 | _checked: false, 19 | 20 | events: { 21 | 'change .conditional-logic-advanced-toggle': 'change_toggle', 22 | 'click .add-conditional-logic-advanced-rule': 'add_rule', 23 | 'click .add-conditional-logic-advanced-group': 'add_group', 24 | 'click .remove-conditional-logic-advanced-rule': 'remove_rule', 25 | 'change .conditional-logic-advanced-rule-param': 'change_rule' 26 | }, 27 | 28 | /* 29 | * change_toggle 30 | * 31 | * This function is triggered by changing the 'Conditional Logic' radio button 32 | * 33 | * @type function 34 | * @date 8/04/2014 35 | * @since 5.0.0 36 | * 37 | * @param $input 38 | * @return n/a 39 | */ 40 | 41 | change_toggle: function( e ){ 42 | 43 | // vars 44 | var $input = e.$el, 45 | checked = e.$el.prop('checked'), 46 | $td = $input.closest('.acf-input'); 47 | 48 | if( checked ) { 49 | 50 | $td.find('.rule-groups').show(); 51 | $td.find('.rule-groups').find('[name]').prop('disabled', false); 52 | 53 | } else { 54 | 55 | $td.find('.rule-groups').hide(); 56 | $td.find('.rule-groups').find('[name]').prop('disabled', true); 57 | 58 | } 59 | 60 | this._checked = checked; 61 | }, 62 | 63 | /* 64 | * add_rule 65 | * 66 | * This function will add a new rule below the specified $tr 67 | * 68 | * @type function 69 | * @date 8/04/2014 70 | * @since 5.0.0 71 | * 72 | * @param $tr 73 | * @return n/a 74 | */ 75 | 76 | add_rule: function( e ){ 77 | 78 | // vars 79 | var $tr = e.$el.closest('tr'); 80 | 81 | 82 | // duplicate 83 | $tr2 = acf.duplicate( $tr ); 84 | 85 | }, 86 | 87 | 88 | /* 89 | * remove_rule 90 | * 91 | * This function will remove the $tr and potentially the group 92 | * 93 | * @type function 94 | * @date 8/04/2014 95 | * @since 5.0.0 96 | * 97 | * @param $tr 98 | * @return n/a 99 | */ 100 | 101 | remove_rule: function( e ){ 102 | 103 | // vars 104 | var $tr = e.$el.closest('tr'); 105 | 106 | 107 | // save field 108 | $tr.find('select:first').trigger('change'); 109 | 110 | 111 | if( $tr.siblings('tr').length == 0 ) { 112 | 113 | // remove group 114 | $tr.closest('.rule-group').remove(); 115 | 116 | } 117 | 118 | 119 | // remove tr 120 | $tr.remove(); 121 | 122 | 123 | }, 124 | 125 | 126 | /* 127 | * add_group 128 | * 129 | * This function will add a new rule group to the given $groups container 130 | * 131 | * @type function 132 | * @date 8/04/2014 133 | * @since 5.0.0 134 | * 135 | * @param $tr 136 | * @return n/a 137 | */ 138 | 139 | add_group: function( e ){ 140 | 141 | // vars 142 | var $groups = e.$el.closest('.rule-groups'), 143 | $group = $groups.find('.rule-group:last'); 144 | 145 | 146 | // duplicate 147 | $group2 = acf.duplicate( $group ); 148 | 149 | 150 | // update h4 151 | $group2.find('h4').text( acf._e('or') ); 152 | 153 | 154 | // remove all tr's except the first one 155 | $group2.find('tr:not(:first)').remove(); 156 | 157 | }, 158 | 159 | 160 | /* 161 | * change_rule 162 | * 163 | * This function is triggered when changing a location rule trigger 164 | * 165 | * @type function 166 | * @date 8/04/2014 167 | * @since 5.0.0 168 | * 169 | * @param $select 170 | * @return n/a 171 | */ 172 | 173 | change_rule: function( e ){ 174 | 175 | // vars 176 | var $select = e.$el, 177 | $tr = $select.closest('tr'), 178 | rule_id = $tr.attr('data-id'), 179 | $group = $tr.closest('.rule-group'), 180 | group_id = $group.attr('data-id'); 181 | 182 | 183 | // add loading gif 184 | var $div = $('
'); 185 | 186 | $tr.find('td.value').html( $div ); 187 | 188 | 189 | // load location html 190 | $.ajax({ 191 | url: acf.get('ajaxurl'), 192 | data: acf.prepare_for_ajax({ 193 | 'field_id': $select.parents('.acf-field-object').eq(0).data('id'), 194 | 'action': 'acf/field_group/render_conditional_logic_advanced_value', 195 | 'rule_id': rule_id, 196 | 'group_id': group_id, 197 | 'param': $select.val(), 198 | 'value': '' 199 | }), 200 | type: 'post', 201 | dataType: 'html', 202 | success: function(html){ 203 | $select = $(html).prop('enabled', this._checked); 204 | $div.replaceWith($select); 205 | }.bind(this) 206 | }); 207 | 208 | } 209 | }); 210 | 211 | })(jQuery); -------------------------------------------------------------------------------- /assets/js/acf-input.js: -------------------------------------------------------------------------------- 1 | 2 | (function($){ 3 | 4 | acf.conditional_logic_advanced = acf.model.extend({ 5 | 6 | actions: { 7 | 'prepare 20': 'render', 8 | 'append 20': 'render' 9 | }, 10 | 11 | events: { 12 | 'change #page_template': '_change_template', 13 | 'change #post-formats-select input': '_change_format', 14 | 'change #post .categorychecklist input': '_change_term', 15 | 'change #post .categorychecklist select': '_change_term', 16 | 'change .acf-field input': 'change', 17 | 'change .acf-field textarea': 'change', 18 | 'change .acf-field select': 'change' 19 | }, 20 | o: { 21 | //'page_template': 0, 22 | //'post_format': 0, 23 | //'post_taxonomy': 0 24 | }, 25 | 26 | items: {}, 27 | triggers: {}, 28 | 29 | 30 | /* 31 | * add 32 | * 33 | * This function will add a set of conditional logic rules 34 | * 35 | * @type function 36 | * @date 22/05/2015 37 | * @since 5.2.3 38 | * 39 | * @param target (string) target field key 40 | * @param groups (array) rule groups 41 | * @return $post_id (int) 42 | */ 43 | 44 | add: function( target, groups ){ 45 | 46 | // debug 47 | //console.log( 'conditional_logic.add(%o, %o)', target, groups ); 48 | 49 | 50 | // populate triggers 51 | for( var i in groups ) { 52 | 53 | // vars 54 | var group = groups[i]; 55 | 56 | for( var k in group ) { 57 | 58 | // vars 59 | var rule = group[k], 60 | trigger = (rule.param) ? rule.param : rule.field, 61 | triggers = this.triggers[ trigger ] || {}; 62 | 63 | 64 | // append trigger (sub field will simply override) 65 | triggers[ target ] = target; 66 | 67 | 68 | // update 69 | this.triggers[ trigger ] = triggers; 70 | 71 | } 72 | 73 | } 74 | 75 | 76 | // append items 77 | this.items[ target ] = groups; 78 | }, 79 | 80 | update: function( k, v ){ 81 | 82 | this.o[ k ] = v; 83 | 84 | //console.log('update', k, v); 85 | 86 | return this; 87 | 88 | }, 89 | 90 | 91 | _update_template: function(){ 92 | 93 | // vars 94 | var page_template = $('#page_template').val(); 95 | 96 | 97 | // update & fetch 98 | return this.update('page_template', page_template); 99 | 100 | }, 101 | 102 | _change_template: function() { 103 | this._update_template().refresh(); 104 | }, 105 | 106 | _update_format: function(){ 107 | 108 | // vars 109 | var post_format = $('#post-formats-select input:checked').val(); 110 | 111 | 112 | // default 113 | if( post_format == '0' ) { 114 | 115 | post_format = 'standard'; 116 | 117 | } 118 | 119 | 120 | // update & fetch 121 | return this.update('post_format', post_format); 122 | 123 | }, 124 | 125 | _change_format: function() { 126 | this._update_format().refresh(); 127 | }, 128 | 129 | _update_term: function(){ 130 | 131 | // reference 132 | var self = this; 133 | 134 | 135 | // bail early if within media popup 136 | if( $('#post .categorychecklist input, #post .categorychecklist select').closest('.media-frame').exists() ) { 137 | 138 | return; 139 | 140 | } 141 | 142 | // vars 143 | var post_taxonomy = this.o['post_taxonomy'] || []; 144 | 145 | 146 | // loop over term lists 147 | $('#post .categorychecklist').each(function(){ 148 | var values = []; 149 | 150 | // vars 151 | var $el = $(this), 152 | $checkbox = $el.find('input[type="checkbox"]').not(':disabled'), 153 | $radio = $el.find('input[type="radio"]').not(':disabled'), 154 | $select = $el.find('select').not(':disabled'), 155 | $hidden = $el.find('input[type="hidden"]').not(':disabled'), 156 | taxonomy = /^taxonomy-(.*)$/.exec($el.parents('.categorydiv[id^=taxonomy-]').attr('id'))[1] 157 | termIds = []; 158 | 159 | 160 | // bail early if in attachment 161 | if( $el.closest('.media-frame').exists() ) { 162 | 163 | return; 164 | 165 | } 166 | 167 | 168 | // checkbox 169 | if( $checkbox.exists() ) { 170 | 171 | $checkbox.filter(':checked').each(function(){ 172 | 173 | termIds.push( parseInt($(this).val()) ); 174 | 175 | }); 176 | 177 | } else if( $radio.exists() ) { 178 | 179 | $radio.filter(':checked').each(function(){ 180 | 181 | termIds.push( parseInt($(this).val()) ); 182 | 183 | }); 184 | 185 | } else if( $select.exists() ) { 186 | 187 | $select.find('option:selected').each(function(){ 188 | 189 | termIds.push( parseInt($(this).val()) ); 190 | 191 | }); 192 | 193 | } else if( $hidden.exists() ) { 194 | 195 | $hidden.each(function(){ 196 | 197 | // ignor blank values 198 | if( ! $(this).val() ) { 199 | 200 | return; 201 | 202 | } 203 | 204 | termIds.push( parseInt($(this).val()) ); 205 | 206 | }); 207 | 208 | } 209 | 210 | // remove all entries for this taxonomy before adding 211 | post_taxonomy = post_taxonomy.filter(function(value) { 212 | return value.taxonomy != taxonomy; 213 | }); 214 | 215 | termIds 216 | // filter duplicates 217 | .filter(function(value, index, values) { 218 | return values.indexOf(value) === index; 219 | }) 220 | // add taxonomy entries 221 | .forEach(function(termId) { 222 | post_taxonomy.push({ 223 | id: termId, 224 | taxonomy: taxonomy 225 | }); 226 | }); 227 | }); 228 | 229 | 230 | // update screen 231 | return this.update( 'post_taxonomy', post_taxonomy ); 232 | 233 | }, 234 | 235 | _change_term: function() { 236 | this._update_term().refresh(); 237 | }, 238 | 239 | _change_tag: function() { 240 | this._update_tag().refresh(); 241 | }, 242 | 243 | _update_tag: function(e) { 244 | var tagDelimiter = ( window.tagsSuggestL10n && window.tagsSuggestL10n.tagDelimiter ) || ','; 245 | var post_taxonomy = this.o['post_taxonomy'] || []; 246 | 247 | $('[id^="tagsdiv-"] .jaxtag textarea') 248 | .toArray() 249 | .forEach(function(el) { 250 | var taxonomy = $(el).parents('.tagsdiv').attr('id'), 251 | value = $(el).val(); 252 | elTags = $(el).find('button').toArray(); 253 | 254 | // remove all entries for this taxonomy before adding 255 | post_taxonomy = post_taxonomy.filter(function(value) { 256 | return value.taxonomy != taxonomy; 257 | }); 258 | 259 | value 260 | .split(tagDelimiter) 261 | .forEach(function(tagName) { 262 | tagName = $.trim(tagName); 263 | 264 | if (!tagName) return; 265 | 266 | post_taxonomy.push({ 267 | name: tagName, 268 | taxonomy: taxonomy 269 | }); 270 | }); 271 | }) 272 | 273 | // update screen 274 | return this.update( 'post_taxonomy', post_taxonomy ); 275 | }, 276 | 277 | 278 | /* 279 | * render 280 | * 281 | * This function will render all fields 282 | * 283 | * @type function 284 | * @date 22/05/2015 285 | * @since 5.2.3 286 | * 287 | * @param $post_id (int) 288 | * @return $post_id (int) 289 | */ 290 | 291 | render: function( $el ) { 292 | // debug 293 | //console.log('conditional_logic.render(%o)', $el); 294 | 295 | // override wordpress core tag parsing, so that we can detect when tags change 296 | $(document).ready(function($) { 297 | if (!window.tagBox) return; 298 | 299 | var tagBox = window.tagBox, 300 | parseTagsOriginal = tagBox.parseTags, 301 | flushTagsOriginal = tagBox.flushTags; 302 | 303 | tagBox.parseTags = function() { 304 | var result = parseTagsOriginal.apply(tagBox, arguments); 305 | this._change_tag(); 306 | return result; 307 | }.bind(this); 308 | 309 | tagBox.flushTags = function() { 310 | var result = flushTagsOriginal.apply(tagBox, arguments); 311 | this._change_tag(); 312 | return result; 313 | }.bind(this); 314 | 315 | }.bind(this)); 316 | 317 | this._update_format(); 318 | this._update_template(); 319 | this._update_term(); 320 | this._update_tag(); 321 | 322 | this.refresh(); 323 | }, 324 | 325 | /* 326 | * change 327 | * 328 | * This function is called when an input is changed and will render any fields which are considered targets of this trigger 329 | * 330 | * @type function 331 | * @date 22/05/2015 332 | * @since 5.2.3 333 | * 334 | * @param $post_id (int) 335 | * @return $post_id (int) 336 | */ 337 | 338 | change: function( e ){ 339 | 340 | // debug 341 | //console.log( 'conditional_logic.change(%o)', $input ); 342 | 343 | 344 | // vars 345 | var $input = e.$el, 346 | $field = acf.get_field_wrap( $input ), 347 | key = $field.data('key'); 348 | 349 | 350 | // bail early if this field does not trigger any actions 351 | if( typeof this.triggers[key] === 'undefined' ) { 352 | 353 | return false; 354 | 355 | } 356 | 357 | 358 | // vars 359 | $parent = $field.parent(); 360 | 361 | 362 | // update visibility 363 | for( var i in this.triggers[ key ] ) { 364 | 365 | // get the target key 366 | var target_key = this.triggers[ key ][ i ]; 367 | 368 | 369 | // get targets 370 | var $targets = acf.get_fields(target_key, $parent, true); 371 | 372 | 373 | // render 374 | this.render_fields( $targets ); 375 | 376 | } 377 | 378 | 379 | // action for 3rd party customization 380 | //acf.do_action('refresh', $parent); 381 | 382 | }, 383 | 384 | refresh: function() { 385 | // get targets 386 | var $targets = acf.get_fields( '', '', true ); 387 | 388 | 389 | // render fields 390 | this.render_fields( $targets ); 391 | 392 | 393 | // action for 3rd party customization 394 | //acf.do_action('refresh', $el); 395 | 396 | }, 397 | 398 | 399 | /* 400 | * render_fields 401 | * 402 | * This function will render a selection of fields 403 | * 404 | * @type function 405 | * @date 22/05/2015 406 | * @since 5.2.3 407 | * 408 | * @param $post_id (int) 409 | * @return $post_id (int) 410 | */ 411 | 412 | render_fields: function( $targets ) { 413 | 414 | // reference 415 | var self = this; 416 | 417 | 418 | // loop over targets and render them 419 | $targets.each(function(){ 420 | 421 | self.render_field( $(this) ); 422 | 423 | }); 424 | 425 | }, 426 | 427 | 428 | /* 429 | * render_field 430 | * 431 | * This function will render a field 432 | * 433 | * @type function 434 | * @date 22/05/2015 435 | * @since 5.2.3 436 | * 437 | * @param $post_id (int) 438 | * @return $post_id (int) 439 | */ 440 | 441 | render_field : function( $target ){ 442 | 443 | // vars 444 | var key = $target.data('key'); 445 | 446 | 447 | // bail early if this field does not contain any conditional logic 448 | if( typeof this.items[ key ] === 'undefined' ) { 449 | 450 | return false; 451 | 452 | } 453 | 454 | 455 | // vars 456 | var visibility = false; 457 | 458 | 459 | // debug 460 | //console.log( 'conditional_logic.render_field(%o)', $field ); 461 | 462 | 463 | // get conditional logic 464 | var groups = this.items[ key ]; 465 | 466 | 467 | // calculate visibility 468 | for( var i = 0; i < groups.length; i++ ) { 469 | 470 | // vars 471 | var group = groups[i], 472 | match_group = true; 473 | 474 | for( var k = 0; k < group.length; k++ ) { 475 | 476 | // vars 477 | var rule = group[k]; 478 | 479 | // break if rule did not validate 480 | if( !this.calculate(rule, $target) ) { 481 | 482 | match_group = false; 483 | break; 484 | 485 | } 486 | 487 | } 488 | 489 | 490 | // set visibility if rule group did validate 491 | if( match_group ) { 492 | 493 | visibility = true; 494 | break; 495 | 496 | } 497 | 498 | } 499 | 500 | 501 | // hide / show field 502 | if( visibility ) { 503 | 504 | this.show_field( $target ); 505 | 506 | } else { 507 | 508 | this.hide_field( $target ); 509 | 510 | } 511 | 512 | }, 513 | 514 | 515 | /* 516 | * show_field 517 | * 518 | * This function will show a field 519 | * 520 | * @type function 521 | * @date 22/05/2015 522 | * @since 5.2.3 523 | * 524 | * @param $post_id (int) 525 | * @return $post_id (int) 526 | */ 527 | 528 | show_field: function( $field ){ 529 | 530 | // debug 531 | //console.log('show_field(%o)', $field); 532 | 533 | 534 | // vars 535 | var key = $field.data('key'); 536 | 537 | 538 | // remove class 539 | $field.removeClass( 'hidden-by-conditional-logic-advanced' ); 540 | 541 | 542 | // enable 543 | acf.enable_form( $field, 'condition_'+key ); 544 | 545 | 546 | // action for 3rd party customization 547 | acf.do_action('show_field', $field, 'conditional_logic_advanced' ); 548 | 549 | }, 550 | 551 | 552 | /* 553 | * hide_field 554 | * 555 | * This function will hide a field 556 | * 557 | * @type function 558 | * @date 22/05/2015 559 | * @since 5.2.3 560 | * 561 | * @param $post_id (int) 562 | * @return $post_id (int) 563 | */ 564 | 565 | hide_field : function( $field ){ 566 | 567 | // debug 568 | //console.log('hide_field(%o)', $field); 569 | 570 | 571 | // vars 572 | var key = $field.data('key'); 573 | 574 | 575 | // add class 576 | $field.addClass( 'hidden-by-conditional-logic-advanced' ); 577 | 578 | 579 | // disable 580 | acf.disable_form( $field, 'condition_'+key ); 581 | 582 | 583 | // action for 3rd party customization 584 | acf.do_action('hide_field', $field, 'conditional_logic_advanced' ); 585 | 586 | }, 587 | 588 | 589 | /* 590 | * get_trigger 591 | * 592 | * This function will return the relevant $trigger for a $target 593 | * 594 | * @type function 595 | * @date 22/05/2015 596 | * @since 5.2.3 597 | * 598 | * @param $post_id (int) 599 | * @return $post_id (int) 600 | */ 601 | 602 | get_trigger: function( $target, key ){ 603 | 604 | // vars 605 | var selector = acf.get_selector( key ); 606 | 607 | 608 | // find sibling $trigger 609 | var $trigger = $target.siblings( selector ); 610 | 611 | 612 | // parent trigger 613 | if( !$trigger.exists() ) { 614 | 615 | // vars 616 | var parent = acf.get_selector(); 617 | 618 | 619 | // loop through parent fields and review their siblings too 620 | $target.parents( parent ).each(function(){ 621 | 622 | // find sibling $trigger 623 | $trigger = $(this).siblings( selector ); 624 | 625 | 626 | // bail early if $trigger is found 627 | if( $trigger.exists() ) { 628 | 629 | return false; 630 | 631 | } 632 | 633 | }); 634 | 635 | } 636 | 637 | 638 | // bail early if no $trigger is found 639 | if( !$trigger.exists() ) { 640 | 641 | return false; 642 | 643 | } 644 | 645 | 646 | // return 647 | return $trigger; 648 | 649 | }, 650 | 651 | 652 | /* 653 | * calculate 654 | * 655 | * This function will calculate if a rule matches based on the $trigger 656 | * 657 | * @type function 658 | * @date 22/05/2015 659 | * @since 5.2.3 660 | * 661 | * @param $post_id (int) 662 | * @return $post_id (int) 663 | */ 664 | 665 | calculate : function( rule, $target ){ 666 | 667 | // debug 668 | //console.log( 'calculate(%o, %o, %o)', rule, $trigger, $target); 669 | 670 | // vars 671 | var match = false; 672 | 673 | switch(rule['param']) { 674 | case 'post_template': 675 | match = this.o['page_template'] == rule['value']; 676 | break; 677 | 678 | case 'post_category': 679 | match = this.o['post_taxonomy'].some(function(value) { 680 | if (rule['value'].taxonomy != value.taxonomy) return; 681 | if (value.id && rule['value'].id == value.id) return true; 682 | if (value.name && rule['value'].name == value.name) return true; 683 | }); 684 | break; 685 | 686 | case 'post_format': 687 | match = this.o['post_format'] == rule['value']; 688 | break; 689 | 690 | default: 691 | if (rule['field']) { 692 | var $trigger = this.get_trigger( $target, rule.field ), 693 | type = $trigger.data('type'); 694 | 695 | // input with :checked 696 | if( type == 'true_false' || type == 'checkbox' || type == 'radio' ) { 697 | 698 | match = this.calculate_checkbox( rule, $trigger ); 699 | 700 | 701 | } else if( type == 'select' ) { 702 | 703 | match = this.calculate_select( rule, $trigger ); 704 | 705 | } 706 | } else { 707 | console.error('Unknown rule param: ' + rule['param']); 708 | } 709 | break; 710 | } 711 | 712 | // reverse if 'not equal to' 713 | if( rule.operator === "!=" ) { 714 | 715 | match = !match; 716 | 717 | } 718 | 719 | 720 | // return 721 | return match; 722 | 723 | }, 724 | 725 | calculate_checkbox: function( rule, $trigger ){ 726 | 727 | // look for selected input 728 | var match = $trigger.find('input[value="' + rule.value + '"]:checked').exists(); 729 | 730 | 731 | // override for "allow null" 732 | if( rule.value === '' && !$trigger.find('input:checked').exists() ) { 733 | 734 | match = true; 735 | 736 | } 737 | 738 | 739 | // return 740 | return match; 741 | 742 | }, 743 | 744 | 745 | calculate_select: function( rule, $trigger ){ 746 | 747 | // vars 748 | var $select = $trigger.find('select'), 749 | val = $select.val(); 750 | 751 | 752 | // check for no value 753 | if( !val && !$.isNumeric(val) ) { 754 | 755 | val = ''; 756 | 757 | } 758 | 759 | 760 | // convert to array 761 | if( !$.isArray(val) ) { 762 | 763 | val = [ val ]; 764 | 765 | } 766 | 767 | 768 | // calc 769 | match = ($.inArray(rule.value, val) > -1); 770 | 771 | 772 | // return 773 | return match; 774 | 775 | } 776 | }); 777 | 778 | })(jQuery); 779 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "andrejpavlovic/acf-conditional-logic-advanced", 3 | "description": "Adds an Advanced Conditional Logic field setting to ACF that can show/hide individual fields based on post template, format, and/or category.", 4 | "version": "1.1.3", 5 | "type": "wordpress-plugin", 6 | "license": "GPL-2.0" 7 | } 8 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | plugin_basename( __FILE__ ), 20 | 'path' => plugin_dir_path( __FILE__ ), 21 | 'url' => plugin_dir_url( __FILE__ ), 22 | ]); 23 | $acfCondLogAdv->initialize(); 24 | -------------------------------------------------------------------------------- /lib/AcfConditionalLogicAdvanced.php: -------------------------------------------------------------------------------- 1 | settings = $settings; 8 | 9 | // admin 10 | if( is_admin() ) { 11 | require $this->settings['path'] . 'admin/field-group.php'; 12 | $this->acfAdminFieldGroupdConditionalLogicAdvanced = new acf_admin_field_group_conditional_logic_advanced(); 13 | } 14 | 15 | add_action('init', array($this, 'register_assets'), 5); 16 | 17 | add_action('acf/input/admin_enqueue_scripts', [$this, 'input_admin_enqueue_scripts']); 18 | add_action('acf/field_group/admin_enqueue_scripts', [$this, 'field_group_admin_enqueue_scripts']); 19 | 20 | add_filter('acf/update_field', function($field) { 21 | // clean up conditional logic keys 22 | if( !empty($field['conditional_logic_advanced']) ) { 23 | 24 | // extract groups 25 | $groups = acf_extract_var( $field, 'conditional_logic_advanced' ); 26 | 27 | 28 | // clean array 29 | $groups = array_filter($groups); 30 | $groups = array_values($groups); 31 | 32 | 33 | // clean rules 34 | foreach( array_keys($groups) as $i ) { 35 | 36 | $groups[ $i ] = array_filter($groups[ $i ]); 37 | $groups[ $i ] = array_values($groups[ $i ]); 38 | 39 | } 40 | 41 | 42 | // reset conditional logic 43 | $field['conditional_logic_advanced'] = $groups; 44 | 45 | } 46 | 47 | return $field; 48 | }); 49 | 50 | add_action('acf/render_field', [$this, 'acf_render_field']); 51 | } 52 | 53 | function initialize() { 54 | add_action('acf/render_field_settings', [$this, 'render_field_settings']); 55 | } 56 | 57 | function render_field_settings($field) { 58 | if ($field['type'] == 'clone') return; 59 | 60 | $args = [ 61 | 'field' => $field, 62 | ]; 63 | require $this->settings['path'] . 'admin/views/field-group-field-conditional-logic-advanced.php'; 64 | } 65 | 66 | function acf_render_field($field) { 67 | if (empty($field['conditional_logic_advanced'])) return; 68 | 69 | $groups = $field['conditional_logic_advanced']; 70 | 71 | // convert taxonomy term from slug to id 72 | foreach($groups as $groupId => $group) { 73 | foreach ($group as $ruleId => $rule) { 74 | if (!empty($rule['field'])) continue; 75 | 76 | if ($rule['param'] != 'post_category') continue; 77 | $param = explode(':', $rule['value']); 78 | 79 | $taxonomyTerm = get_term_by('slug', $param[1], $param[0]); 80 | 81 | $groups[$groupId][$ruleId]['value'] = array( 82 | 'id' => $taxonomyTerm->term_id, 83 | 'slug' => $taxonomyTerm->slug, 84 | 'taxonomy' => $taxonomyTerm->taxonomy, 85 | 'name' => $taxonomyTerm->name, 86 | ); 87 | } 88 | } 89 | ?> 90 | 93 | settings['url'] . 'assets/js/acf-input.js', array('acf-input') ); 99 | wp_register_script('acf-field-group-conditional-logic-advanced', $this->settings['url'] . 'assets/js/acf-field-group.js', array('acf-input-conditional-logic-advanced') ); 100 | 101 | // styles 102 | wp_register_style('acf-input-conditional-logic-advanced', $this->settings['url'] . 'assets/css/acf-input.css', array('acf-input') ); 103 | } 104 | 105 | function input_admin_enqueue_scripts() { 106 | wp_enqueue_script('acf-input-conditional-logic-advanced'); 107 | wp_enqueue_style('acf-input-conditional-logic-advanced'); 108 | } 109 | 110 | function field_group_admin_enqueue_scripts() { 111 | wp_enqueue_script('acf-field-group-conditional-logic-advanced'); 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === ACF Conditional Logic Advanced === 2 | Contributors: andrej.pavlovic 3 | Tags: acf, advanced custom fields, acf-field, conditional logic 4 | Requires at least: 3.6.0 5 | Tested up to: 4.9.9 6 | License: GPLv2 7 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 8 | 9 | Adds an Advanced Conditional Logic field setting to ACF that can show/hide individual fields based on post template, format, and/or category. 10 | 11 | = Links = 12 | * [Website](https://github.com/andrejpavlovic/acf-conditional-logic-advanced) 13 | * [Support](https://github.com/andrejpavlovic/acf-conditional-logic-advanced/issues) 14 | * [Advanced Custom Fields](https://www.advancedcustomfields.com/) 15 | 16 | == Screenshots == 17 | 18 | 1. Post Category options 19 | 2. Advanced Conditional Logic field setting options: Template, Format, Category 20 | 21 | == Changelog == 22 | 23 | = 1.1.3 = 24 | * Small fix to prevent acf-input category check from throwing errors when editing menus 25 | 26 | = 1.1.2 = 27 | * Small fix for Yes/No when editing Field in Field Group 28 | * Tested with ACF 5.7.7 29 | 30 | = 1.1.1 = 31 | 32 | * Don't display Conditional Logic Advanced row when field type is "Clone". 33 | 34 | = 1.1.0 = 35 | * Added support for custom taxonomies. 36 | * Added support for non-hierarchical taxonomies. (e.g. Tags) 37 | * Reorganized the Post Template dropdown options. 38 | 39 | = 1.0.3 = 40 | * composer.json - fixed version 41 | 42 | = 1.0.2 = 43 | * composer.json - fixed name 44 | 45 | = 1.0.1 = 46 | * Added composer.json 47 | 48 | = 1.0.0 = 49 | * Initial release. 50 | -------------------------------------------------------------------------------- /screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrejpavlovic/acf-conditional-logic-advanced/63ca1022265e0d257575ab3a8f31f91fe14a654f/screenshot-1.png -------------------------------------------------------------------------------- /screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/andrejpavlovic/acf-conditional-logic-advanced/63ca1022265e0d257575ab3a8f31f91fe14a654f/screenshot-2.png --------------------------------------------------------------------------------