diff --git a/acf.php b/acf.php
index 8b36b43..1a2358a 100644
--- a/acf.php
+++ b/acf.php
@@ -3,7 +3,7 @@
Plugin Name: Advanced Custom Fields PRO
Plugin URI: https://www.advancedcustomfields.com/
Description: Customize WordPress with powerful, professional and intuitive fields.
-Version: 5.7.10
+Version: 5.7.13
Author: Elliot Condon
Author URI: http://www.elliotcondon.com/
Copyright: Elliot Condon
@@ -18,7 +18,7 @@ if( ! class_exists('ACF') ) :
class ACF {
/** @var string The plugin version number */
- var $version = '5.7.10';
+ var $version = '5.7.13';
/** @var array The plugin settings array */
var $settings = array();
@@ -118,27 +118,31 @@ class ACF {
$this->define( 'ACF', true );
$this->define( 'ACF_VERSION', $version );
$this->define( 'ACF_PATH', $path );
- //$this->define( 'ACF_DEV', true );
// api
include_once( ACF_PATH . 'includes/api/api-helpers.php');
acf_include('includes/api/api-input.php');
- acf_include('includes/api/api-value.php');
- acf_include('includes/api/api-field.php');
- acf_include('includes/api/api-field-group.php');
acf_include('includes/api/api-template.php');
acf_include('includes/api/api-term.php');
// Include models.
acf_include('includes/class-acf-data.php');
- // Include functions.
- acf_include('includes/acf-helper-functions.php');
+ // Include core functions.
acf_include('includes/acf-data-functions.php');
- acf_include('includes/acf-form-functions.php');
- acf_include('includes/acf-user-functions.php');
+ acf_include('includes/acf-helper-functions.php');
+ acf_include('includes/acf-hook-functions.php');
+ // Include functions.
+ acf_include('includes/acf-deprecated-functions.php');
+ acf_include('includes/acf-field-functions.php');
+ acf_include('includes/acf-field-group-functions.php');
+ acf_include('includes/acf-form-functions.php');
+ acf_include('includes/acf-meta-functions.php');
+ acf_include('includes/acf-post-functions.php');
+ acf_include('includes/acf-user-functions.php');
+ acf_include('includes/acf-value-functions.php');
// fields
acf_include('includes/fields.php');
@@ -152,12 +156,11 @@ class ACF {
// core
acf_include('includes/assets.php');
- acf_include('includes/cache.php');
acf_include('includes/compatibility.php');
acf_include('includes/deprecated.php');
acf_include('includes/json.php');
acf_include('includes/l10n.php');
- acf_include('includes/local.php');
+ acf_include('includes/local-fields.php');
acf_include('includes/loop.php');
acf_include('includes/media.php');
acf_include('includes/revisions.php');
diff --git a/assets/js/acf-input.js b/assets/js/acf-input.js
index 998ffc6..48f7798 100644
--- a/assets/js/acf-input.js
+++ b/assets/js/acf-input.js
@@ -1991,7 +1991,7 @@
// option
} else {
- itemsHtml += '';
+ itemsHtml += '';
}
});
@@ -2097,7 +2097,7 @@
* @return bool
*/
acf.isGutenberg = function(){
- return ( window.wp && wp.blocks );
+ return ( window.wp && wp.data && wp.data.select && wp.data.select( 'core/editor' ) );
};
/**
@@ -5934,6 +5934,10 @@
// render
this.renderVal( val );
+
+ // action
+ var latLng = this.newLatLng( val.lat, val.lng );
+ acf.doAction('google_map_change', latLng, this.map, this);
},
renderVal: function( val ){
@@ -5965,9 +5969,6 @@
// show marker
this.map.marker.setVisible( true );
- // action
- acf.doAction('google_map_change', latLng, this.map, this);
-
// center
this.center();
@@ -11037,16 +11038,20 @@
copyEvents( $submitdiv.children('.hndle'), $postbox.children('.hndle') );
}
+ // Initalize it (modifies HTML).
+ postbox = acf.newPostbox( result );
+
// Trigger action.
acf.doAction('append', $postbox);
-
- // Initalize it.
- postbox = acf.newPostbox( result );
+ acf.doAction('append_postbox', postbox);
}
// show postbox
postbox.showEnable();
+ // Do action.
+ acf.doAction('show_postbox', postbox);
+
// append
visible.push( result.id );
});
@@ -11055,6 +11060,9 @@
acf.getPostboxes().map(function( postbox ){
if( visible.indexOf( postbox.get('id') ) === -1 ) {
postbox.hideDisable();
+
+ // Do action.
+ acf.doAction('hide_postbox', postbox);
}
});
@@ -11099,6 +11107,9 @@
acf.screen.getPostType = this.getPostType;
acf.screen.getPostFormat = this.getPostFormat;
acf.screen.getPostCoreTerms = this.getPostCoreTerms;
+
+ // Add actions.
+ this.addAction( 'append_postbox', acf.screen.refreshAvailableMetaBoxesPerLocation );
},
onChange: function(){
@@ -11121,7 +11132,7 @@
// Filter out attributes that have not changed.
attributes = attributes.filter(this.proxy(function( attr ){
- return ( edits[attr] && edits[attr] !== this.get(attr) );
+ return ( edits[attr] !== undefined && edits[attr] !== this.get(attr) );
}));
// Trigger change if has attributes.
@@ -11175,8 +11186,62 @@
// return
return terms;
- },
+ }
});
+
+ /**
+ * acf.screen.refreshAvailableMetaBoxesPerLocation
+ *
+ * Refreshes the WP data state based on metaboxes found in the DOM.
+ *
+ * @date 6/3/19
+ * @since 5.7.13
+ *
+ * @param void
+ * @return void
+ */
+ acf.screen.refreshAvailableMetaBoxesPerLocation = function() {
+
+ // Extract vars.
+ var select = wp.data.select( 'core/edit-post' );
+ var dispatch = wp.data.dispatch( 'core/edit-post' );
+
+ // Load current metabox locations and data.
+ var data = {};
+ select.getActiveMetaBoxLocations().map(function( location ){
+ data[ location ] = select.getMetaBoxesPerLocation( location );
+ });
+
+ // Generate flat array of existing ids.
+ var ids = [];
+ for( var k in data ) {
+ ids = ids.concat( data[k].map(function(m){ return m.id; }) );
+ }
+
+ // Append ACF metaboxes.
+ acf.getPostboxes().map(function( postbox ){
+
+ // Ignore if already exists in data.
+ if( ids.indexOf( postbox.get('id') ) !== -1 ) {
+ return;
+ }
+
+ // Get metabox location looking at parent form.
+ var location = postbox.$el.closest('form').attr('class').replace('metabox-location-', '');
+
+ // Ensure location exists.
+ data[ location ] = data[ location ] || [];
+
+ // Append.
+ data[ location ].push({
+ id: postbox.get('id'),
+ title: postbox.get('title')
+ });
+ });
+
+ // Update state.
+ dispatch.setAvailableMetaBoxesPerLocation(data);
+ };
})(jQuery);
@@ -13074,7 +13139,7 @@
'click button[type="submit"]': 'onClickSubmit',
//'click #editor .editor-post-publish-button': 'onClickSubmitGutenberg',
'click #save-post': 'onClickSave',
- 'mousedown #post-preview': 'onClickPreview', // use mousedown to hook in before WP click event
+ 'submit form#post': 'onSubmitPost',
'submit form': 'onSubmit',
},
@@ -13237,27 +13302,6 @@
this.set('ignore', true);
},
- /**
- * onClickPreview
- *
- * Set ignore to true when previewing a post.
- *
- * @date 4/9/18
- * @since 5.7.5
- *
- * @param object e The event object.
- * @param jQuery $el The input element.
- * @return void
- */
- onClickPreview: function( e, $el ) {
- this.set('ignore', true);
-
- // if post has previously been published but prevented by an error, WP core has
- // added a custom 'submit.edit-post' event which causes the input buttons to become disabled.
- // remove this event to prevent UX issues.
- $('form#post').off('submit.edit-post');
- },
-
/**
* onClickSubmitGutenberg
*
@@ -13291,6 +13335,31 @@
}
},
+ /**
+ * onSubmitPost
+ *
+ * Callback when the 'post' form is submit.
+ *
+ * @date 5/3/19
+ * @since 5.7.13
+ *
+ * @param object e The event object.
+ * @param jQuery $el The input element.
+ * @return void
+ */
+ onSubmitPost: function( e, $el ) {
+
+ // Check if is preview.
+ if( $('input#wp-preview').val() === 'dopreview' ) {
+
+ // Ignore validation.
+ this.set('ignore', true);
+
+ // Unlock form to fix conflict with core "submit.edit-post" event causing all submit buttons to be disabled.
+ acf.unlockForm( $el )
+ }
+ },
+
/**
* onSubmit
*
@@ -13305,27 +13374,51 @@
*/
onSubmit: function( e, $el ){
- // bail early if is disabled
- if( !this.active ) {
- return;
+ // Allow form to submit if...
+ if(
+ // Validation has been disabled.
+ !this.active
+
+ // Or this event is to be ignored.
+ || this.get('ignore')
+
+ // Or this event has already been prevented.
+ || e.isDefaultPrevented()
+ ) {
+ // Return early and call reset function.
+ return this.allowSubmit();
}
- // bail early if is ignore
- if( this.get('ignore') ) {
- this.set('ignore', false);
- return;
- }
-
- // validate
+ // Validate form.
var valid = acf.validateForm({
form: $el,
event: this.get('originalEvent')
});
- // if not valid, stop event and allow validation to continue
+ // If not valid, stop event to prevent form submit.
if( !valid ) {
e.preventDefault();
}
+ },
+
+ /**
+ * allowSubmit
+ *
+ * Resets data during onSubmit when the form is allowed to submit.
+ *
+ * @date 5/3/19
+ * @since 5.7.13
+ *
+ * @param void
+ * @return void
+ */
+ allowSubmit: function(){
+
+ // Reset "ignore" state.
+ this.set('ignore', false);
+
+ // Reset "originalEvent" object.
+ this.set('originalEvent', false)
}
});
diff --git a/assets/js/acf-input.min.js b/assets/js/acf-input.min.js
index 3ef6113..d74dac4 100644
--- a/assets/js/acf-input.min.js
+++ b/assets/js/acf-input.min.js
@@ -1,4 +1,4 @@
-!function(r,s){var c={};(window.acf=c).data={},c.get=function(t){return this.data[t]||null},c.has=function(t){return null!==this.get(t)},c.set=function(t,e){return this.data[t]=e,this};var i=0;c.uniqueId=function(t){var e=++i+"";return t?t+e:e},c.uniqueArray=function(t){function e(t,e,i){return i.indexOf(t)===e}return t.filter(e)};var a="";c.uniqid=function(t,e){var i;void 0===t&&(t="");var n=function(t,e){return e<(t=parseInt(t,10).toString(16)).length?t.slice(t.length-e):e>t.length?Array(e-t.length+1).join("0")+t:t};return a||(a=Math.floor(123456789*Math.random())),a++,i=t,i+=n(parseInt((new Date).getTime()/1e3,10),8),i+=n(a,5),e&&(i+=(10*Math.random()).toFixed(8).toString()),i},c.strReplace=function(t,e,i){return i.split(t).join(e)},c.strCamelCase=function(t){return t=(t=t.replace(/[_-]/g," ")).replace(/(?:^\w|\b\w|\s+)/g,function(t,e){return 0==+t?"":0==e?t.toLowerCase():t.toUpperCase()})},c.strPascalCase=function(t){var e=c.strCamelCase(t);return e.charAt(0).toUpperCase()+e.slice(1)},c.strSlugify=function(t){return c.strReplace("_","-",t.toLowerCase())},c.strSanitize=function(t){var e={"À":"A","Á":"A","Â":"A","Ã":"A","Ä":"A","Å":"A","Æ":"AE","Ç":"C","È":"E","É":"E","Ê":"E","Ë":"E","Ì":"I","Í":"I","Î":"I","Ï":"I","Ð":"D","Ñ":"N","Ò":"O","Ó":"O","Ô":"O","Õ":"O","Ö":"O","Ø":"O","Ù":"U","Ú":"U","Û":"U","Ü":"U","Ý":"Y","ß":"s","à":"a","á":"a","â":"a","ã":"a","ä":"a","å":"a","æ":"ae","ç":"c","è":"e","é":"e","ê":"e","ë":"e","ì":"i","í":"i","î":"i","ï":"i","ñ":"n","ò":"o","ó":"o","ô":"o","õ":"o","ö":"o","ø":"o","ù":"u","ú":"u","û":"u","ü":"u","ý":"y","ÿ":"y","Ā":"A","ā":"a","Ă":"A","ă":"a","Ą":"A","ą":"a","Ć":"C","ć":"c","Ĉ":"C","ĉ":"c","Ċ":"C","ċ":"c","Č":"C","č":"c","Ď":"D","ď":"d","Đ":"D","đ":"d","Ē":"E","ē":"e","Ĕ":"E","ĕ":"e","Ė":"E","ė":"e","Ę":"E","ę":"e","Ě":"E","ě":"e","Ĝ":"G","ĝ":"g","Ğ":"G","ğ":"g","Ġ":"G","ġ":"g","Ģ":"G","ģ":"g","Ĥ":"H","ĥ":"h","Ħ":"H","ħ":"h","Ĩ":"I","ĩ":"i","Ī":"I","ī":"i","Ĭ":"I","ĭ":"i","Į":"I","į":"i","İ":"I","ı":"i","IJ":"IJ","ij":"ij","Ĵ":"J","ĵ":"j","Ķ":"K","ķ":"k","Ĺ":"L","ĺ":"l","Ļ":"L","ļ":"l","Ľ":"L","ľ":"l","Ŀ":"L","ŀ":"l","Ł":"l","ł":"l","Ń":"N","ń":"n","Ņ":"N","ņ":"n","Ň":"N","ň":"n","ʼn":"n","Ō":"O","ō":"o","Ŏ":"O","ŏ":"o","Ő":"O","ő":"o","Œ":"OE","œ":"oe","Ŕ":"R","ŕ":"r","Ŗ":"R","ŗ":"r","Ř":"R","ř":"r","Ś":"S","ś":"s","Ŝ":"S","ŝ":"s","Ş":"S","ş":"s","Š":"S","š":"s","Ţ":"T","ţ":"t","Ť":"T","ť":"t","Ŧ":"T","ŧ":"t","Ũ":"U","ũ":"u","Ū":"U","ū":"u","Ŭ":"U","ŭ":"u","Ů":"U","ů":"u","Ű":"U","ű":"u","Ų":"U","ų":"u","Ŵ":"W","ŵ":"w","Ŷ":"Y","ŷ":"y","Ÿ":"Y","Ź":"Z","ź":"z","Ż":"Z","ż":"z","Ž":"Z","ž":"z","ſ":"s","ƒ":"f","Ơ":"O","ơ":"o","Ư":"U","ư":"u","Ǎ":"A","ǎ":"a","Ǐ":"I","ǐ":"i","Ǒ":"O","ǒ":"o","Ǔ":"U","ǔ":"u","Ǖ":"U","ǖ":"u","Ǘ":"U","ǘ":"u","Ǚ":"U","ǚ":"u","Ǜ":"U","ǜ":"u","Ǻ":"A","ǻ":"a","Ǽ":"AE","ǽ":"ae","Ǿ":"O","ǿ":"o"," ":"_","'":"","?":"","/":"","\\":"",".":"",",":"","`":"",">":"","<":"",'"':"","[":"","]":"","|":"","{":"","}":"","(":"",")":""},i=/\W/g,n=function(t){return e[t]!==s?e[t]:t};return t=(t=t.replace(i,n)).toLowerCase()},c.strMatch=function(t,e){for(var i=0,n=Math.min(t.length,e.length),a=0;a
"+this.get("text")+"
"),this.get("dismiss")&&(this.$el.append(''),this.$el.addClass("-dismiss"));var t=this.get("timeout");t&&this.away(t)},update:function(t){e.extend(this.data,t),this.initialize(),this.removeEvents(),this.addEvents()},show:function(){var t=this.get("target");t&&t.prepend(this.$el)},hide:function(){this.$el.remove()},away:function(t){this.setTimeout(function(){acf.remove(this.$el)},t)},type:function(t){var e=this.get("type");e&&this.$el.removeClass("-"+e),this.$el.addClass("-"+t),"error"==t&&this.$el.addClass("acf-error-message")},html:function(t){this.$el.html(t)},text:function(t){this.$("p").html(t)},onClickClose:function(t,e){t.preventDefault(),this.get("close").apply(this,arguments),this.remove()}});acf.newNotice=function(t){return"object"!=typeof t&&(t={text:t}),new i(t)};var n=new acf.Model({wait:"prepare",priority:1,initialize:function(){var t=e(".acf-admin-notice");t.length&&e("h1:first").after(t)}})}(jQuery),function(e,t){acf.getPostbox=function(t){return"string"==typeof t&&(t=e("#"+t)),acf.getInstance(t)},acf.getPostboxes=function(){return acf.getInstances(e(".acf-postbox"))},acf.newPostbox=function(t){return new acf.models.Postbox(t)},acf.models.Postbox=acf.Model.extend({data:{id:"",key:"",style:"default",label:"top",edit:""},setup:function(t){t.editLink&&(t.edit=t.editLink),e.extend(this.data,t),this.$el=this.$postbox()},$postbox:function(){return e("#"+this.get("id"))},$hide:function(){return e("#"+this.get("id")+"-hide")},$hideLabel:function(){return this.$hide().parent()},$hndle:function(){return this.$("> .hndle")},$inside:function(){return this.$("> .inside")},isVisible:function(){return this.$el.hasClass("acf-hidden")},initialize:function(){this.$el.addClass("acf-postbox"),this.$el.removeClass("hide-if-js");var t=this.get("style");"default"!==t&&this.$el.addClass(t),this.$inside().addClass("acf-fields").addClass("-"+this.get("label"));var e=this.get("edit");e&&this.$hndle().append(''),this.show()},show:function(){this.$hideLabel().show(),this.$hide().prop("checked",!0),this.$el.show().removeClass("acf-hidden")},enable:function(){acf.enable(this.$el,"postbox")},showEnable:function(){this.show(),this.enable()},hide:function(){this.$hideLabel().hide(),this.$el.hide().addClass("acf-hidden")},disable:function(){acf.disable(this.$el,"postbox")},hideDisable:function(){this.hide(),this.disable()},html:function(t){this.$inside().html(t),acf.doAction("append",this.$el)}})}(jQuery),function(f,e){acf.newTooltip=function(t){return"object"!=typeof t&&(t={text:t}),t.confirmRemove!==e?(t.textConfirm=acf.__("Remove"),t.textCancel=acf.__("Cancel"),new n(t)):t.confirm!==e?new n(t):new i(t)};var i=acf.Model.extend({data:{text:"",timeout:0,target:null},tmpl:function(){return''},setup:function(t){f.extend(this.data,t),this.$el=f(this.tmpl())},initialize:function(){this.render(),this.show(),this.position();var t=this.get("timeout");t&&setTimeout(f.proxy(this.fade,this),t)},update:function(t){f.extend(this.data,t),this.initialize()},render:function(){this.html(this.get("text"))},show:function(){f("body").append(this.$el)},hide:function(){this.$el.remove()},fade:function(){this.$el.addClass("acf-fade-up"),this.setTimeout(function(){this.remove()},250)},html:function(t){this.$el.html(t)},position:function(){var t=this.$el,e=this.get("target");if(e){t.removeClass("right left bottom top").css({top:0,left:0});var i=10,n=e.outerWidth(),a=e.outerHeight(),r=e.offset().top,s=e.offset().left,o=t.outerWidth(),c=t.outerHeight(),l=t.offset().top,u=r-c-l,d=s+n/2-o/2;d<10?(t.addClass("right"),d=s+n,u=r+a/2-c/2-l):d+o+10>f(window).width()?(t.addClass("left"),d=s-o,u=r+a/2-c/2-l):u-f(window).scrollTop()<10?(t.addClass("bottom"),u=r+a-l):t.addClass("top"),t.css({top:u,left:d})}}}),n=i.extend({data:{text:"",textConfirm:"",textCancel:"",target:null,targetConfirm:!0,confirm:function(){},cancel:function(){},context:!1},events:{'click [data-event="cancel"]':"onCancel",'click [data-event="confirm"]':"onConfirm"},addEvents:function(){acf.Model.prototype.addEvents.apply(this);var t=f(document),e=this.get("target");this.setTimeout(function(){this.on(t,"click","onCancel")}),this.get("targetConfirm")&&this.on(e,"click","onConfirm")},removeEvents:function(){acf.Model.prototype.removeEvents.apply(this);var t=f(document),e=this.get("target");this.off(t,"click"),this.off(e,"click")},render:function(){var t,e,i,n=[this.get("text")||acf.__("Are you sure?"),''+(this.get("textConfirm")||acf.__("Yes"))+"",''+(this.get("textCancel")||acf.__("No"))+""].join(" ");this.html(n),this.$el.addClass("-confirm")},onCancel:function(t,e){t.preventDefault(),t.stopImmediatePropagation();var i=this.get("cancel"),n=this.get("context")||this;i.apply(n,arguments),this.remove()},onConfirm:function(t,e){t.preventDefault(),t.stopImmediatePropagation();var i=this.get("confirm"),n=this.get("context")||this;i.apply(n,arguments),this.remove()}});acf.models.Tooltip=i,acf.models.TooltipConfirm=n;var t=new acf.Model({tooltip:!1,events:{"mouseenter .acf-js-tooltip":"showTitle","mouseup .acf-js-tooltip":"hideTitle","mouseleave .acf-js-tooltip":"hideTitle"},showTitle:function(t,e){var i=e.attr("title");i&&(e.attr("title",""),this.tooltip?this.tooltip.update({text:i,target:e}):this.tooltip=acf.newTooltip({text:i,target:e}))},hideTitle:function(t,e){this.tooltip.hide(),e.attr("title",this.tooltip.get("text"))}})}(jQuery),function(e,i){var r=[];acf.Field=acf.Model.extend({type:"",eventScope:".acf-field",wait:"ready",setup:function(t){this.$el=t,this.inherit(t),this.inherit(this.$control())},val:function(t){return t!==i?this.setValue(t):this.prop("disabled")?null:this.getValue()},getValue:function(){return this.$input().val()},setValue:function(t){return acf.val(this.$input(),t)},__:function(t){return acf._e(this.type,t)},$control:function(){return!1},$input:function(){return this.$("[name]:first")},$inputWrap:function(){return this.$(".acf-input:first")},$labelWrap:function(){return this.$(".acf-label:first")},getInputName:function(){return this.$input().attr("name")||""},parent:function(){var t=this.parents();return!!t.length&&t[0]},parents:function(){var t=this.$el.parents(".acf-field"),e;return acf.getFields(t)},show:function(t,e){var i=acf.show(this.$el,t);return i&&(this.prop("hidden",!1),acf.doAction("show_field",this,e)),i},hide:function(t,e){var i=acf.hide(this.$el,t);return i&&(this.prop("hidden",!0),acf.doAction("hide_field",this,e)),i},enable:function(t,e){var i=acf.enable(this.$el,t);return i&&(this.prop("disabled",!1),acf.doAction("enable_field",this,e)),i},disable:function(t,e){var i=acf.disable(this.$el,t);return i&&(this.prop("disabled",!0),acf.doAction("disable_field",this,e)),i},showEnable:function(t,e){return this.enable.apply(this,arguments),this.show.apply(this,arguments)},hideDisable:function(t,e){return this.disable.apply(this,arguments),this.hide.apply(this,arguments)},showNotice:function(t){"object"!=typeof t&&(t={text:t}),this.notice&&this.notice.remove(),t.target=this.$inputWrap(),this.notice=acf.newNotice(t)},removeNotice:function(t){this.notice&&(this.notice.away(t||0),this.notice=!1)},showError:function(t){this.$el.addClass("acf-error"),t!==i&&this.showNotice({text:t,type:"error",dismiss:!1}),acf.doAction("invalid_field",this),this.$el.one("focus change","input, select, textarea",e.proxy(this.removeError,this))},removeError:function(){this.$el.removeClass("acf-error"),this.removeNotice(250),acf.doAction("valid_field",this)},trigger:function(t,e,i){return"invalidField"==t&&(i=!0),acf.Model.prototype.trigger.apply(this,[t,e,i])}}),acf.newField=function(t){var e=t.data("type"),i=s(e),n,a=new(acf.models[i]||acf.Field)(t);return acf.doAction("new_field",a),a};var s=function(t){return acf.strPascalCase(t||"")+"Field"};acf.registerFieldType=function(t){var e,i=t.prototype.type,n=s(i);acf.models[n]=t,r.push(i)},acf.getFieldType=function(t){var e=s(t);return acf.models[e]||!1},acf.getFieldTypes=function(n){n=acf.parseArgs(n,{category:""});var a=[];return r.map(function(t){var e=acf.getFieldType(t),i=e.prototype;n.category&&i.category!==n.category||a.push(e)}),a}}(jQuery),function(n,t){acf.findFields=function(t){var e=".acf-field",i=!1;return(t=acf.parseArgs(t,{key:"",name:"",type:"",is:"",parent:!1,sibling:!1,limit:!1,visible:!1,suppressFilters:!1})).suppressFilters||(t=acf.applyFilters("find_fields_args",t)),t.key&&(e+='[data-key="'+t.key+'"]'),t.type&&(e+='[data-type="'+t.type+'"]'),t.name&&(e+='[data-name="'+t.name+'"]'),t.is&&(e+=t.is),t.visible&&(e+=":visible"),i=t.parent?t.parent.find(e):t.sibling?t.sibling.siblings(e):n(e),t.suppressFilters||(i=i.not(".acf-clone .acf-field"),i=acf.applyFilters("find_fields",i)),t.limit&&(i=i.slice(0,t.limit)),i},acf.findField=function(t,e){return acf.findFields({key:t,limit:1,parent:e,suppressFilters:!0})},acf.getField=function(t){t instanceof jQuery||(t=acf.findField(t));var e=t.data("acf");return e||(e=acf.newField(t)),e},acf.getFields=function(t){t instanceof jQuery||(t=acf.findFields(t));var e=[];return t.each(function(){var t=acf.getField(n(this));e.push(t)}),e},acf.findClosestField=function(t){return t.closest(".acf-field")},acf.getClosestField=function(t){var e=acf.findClosestField(t);return this.getField(e)};var e=function(t){var e=t,r=t+"_fields",a=t+"_field",i=function(t){var e,i=acf.arrayArgs(arguments).slice(1),n=acf.getFields({parent:t});if(n.length){var a=[r,n].concat(i);acf.doAction.apply(null,a)}},n=function(t){var e,n=acf.arrayArgs(arguments).slice(1);t.map(function(t,e){var i=[a,t].concat(n);acf.doAction.apply(null,i)})};acf.addAction(e,i),acf.addAction(r,n),s(t)},s=function(e){var r=e+"_field",s=e+"Field",t=function(i){var n=acf.arrayArgs(arguments),a=n.slice(1),t;["type","name","key"].map(function(t){var e="/"+t+"="+i.get(t);n=[r+e,i].concat(a),acf.doAction.apply(null,n)}),-1"+this.get("text")+"
"),this.get("dismiss")&&(this.$el.append(''),this.$el.addClass("-dismiss"));var t=this.get("timeout");t&&this.away(t)},update:function(t){e.extend(this.data,t),this.initialize(),this.removeEvents(),this.addEvents()},show:function(){var t=this.get("target");t&&t.prepend(this.$el)},hide:function(){this.$el.remove()},away:function(t){this.setTimeout(function(){acf.remove(this.$el)},t)},type:function(t){var e=this.get("type");e&&this.$el.removeClass("-"+e),this.$el.addClass("-"+t),"error"==t&&this.$el.addClass("acf-error-message")},html:function(t){this.$el.html(t)},text:function(t){this.$("p").html(t)},onClickClose:function(t,e){t.preventDefault(),this.get("close").apply(this,arguments),this.remove()}});acf.newNotice=function(t){return"object"!=typeof t&&(t={text:t}),new i(t)};var n=new acf.Model({wait:"prepare",priority:1,initialize:function(){var t=e(".acf-admin-notice");t.length&&e("h1:first").after(t)}})}(jQuery),function(e,t){acf.getPostbox=function(t){return"string"==typeof t&&(t=e("#"+t)),acf.getInstance(t)},acf.getPostboxes=function(){return acf.getInstances(e(".acf-postbox"))},acf.newPostbox=function(t){return new acf.models.Postbox(t)},acf.models.Postbox=acf.Model.extend({data:{id:"",key:"",style:"default",label:"top",edit:""},setup:function(t){t.editLink&&(t.edit=t.editLink),e.extend(this.data,t),this.$el=this.$postbox()},$postbox:function(){return e("#"+this.get("id"))},$hide:function(){return e("#"+this.get("id")+"-hide")},$hideLabel:function(){return this.$hide().parent()},$hndle:function(){return this.$("> .hndle")},$inside:function(){return this.$("> .inside")},isVisible:function(){return this.$el.hasClass("acf-hidden")},initialize:function(){this.$el.addClass("acf-postbox"),this.$el.removeClass("hide-if-js");var t=this.get("style");"default"!==t&&this.$el.addClass(t),this.$inside().addClass("acf-fields").addClass("-"+this.get("label"));var e=this.get("edit");e&&this.$hndle().append(''),this.show()},show:function(){this.$hideLabel().show(),this.$hide().prop("checked",!0),this.$el.show().removeClass("acf-hidden")},enable:function(){acf.enable(this.$el,"postbox")},showEnable:function(){this.show(),this.enable()},hide:function(){this.$hideLabel().hide(),this.$el.hide().addClass("acf-hidden")},disable:function(){acf.disable(this.$el,"postbox")},hideDisable:function(){this.hide(),this.disable()},html:function(t){this.$inside().html(t),acf.doAction("append",this.$el)}})}(jQuery),function(f,e){acf.newTooltip=function(t){return"object"!=typeof t&&(t={text:t}),t.confirmRemove!==e?(t.textConfirm=acf.__("Remove"),t.textCancel=acf.__("Cancel"),new n(t)):t.confirm!==e?new n(t):new i(t)};var i=acf.Model.extend({data:{text:"",timeout:0,target:null},tmpl:function(){return''},setup:function(t){f.extend(this.data,t),this.$el=f(this.tmpl())},initialize:function(){this.render(),this.show(),this.position();var t=this.get("timeout");t&&setTimeout(f.proxy(this.fade,this),t)},update:function(t){f.extend(this.data,t),this.initialize()},render:function(){this.html(this.get("text"))},show:function(){f("body").append(this.$el)},hide:function(){this.$el.remove()},fade:function(){this.$el.addClass("acf-fade-up"),this.setTimeout(function(){this.remove()},250)},html:function(t){this.$el.html(t)},position:function(){var t=this.$el,e=this.get("target");if(e){t.removeClass("right left bottom top").css({top:0,left:0});var i=10,n=e.outerWidth(),a=e.outerHeight(),r=e.offset().top,s=e.offset().left,o=t.outerWidth(),c=t.outerHeight(),l=t.offset().top,u=r-c-l,d=s+n/2-o/2;d<10?(t.addClass("right"),d=s+n,u=r+a/2-c/2-l):d+o+10>f(window).width()?(t.addClass("left"),d=s-o,u=r+a/2-c/2-l):u-f(window).scrollTop()<10?(t.addClass("bottom"),u=r+a-l):t.addClass("top"),t.css({top:u,left:d})}}}),n=i.extend({data:{text:"",textConfirm:"",textCancel:"",target:null,targetConfirm:!0,confirm:function(){},cancel:function(){},context:!1},events:{'click [data-event="cancel"]':"onCancel",'click [data-event="confirm"]':"onConfirm"},addEvents:function(){acf.Model.prototype.addEvents.apply(this);var t=f(document),e=this.get("target");this.setTimeout(function(){this.on(t,"click","onCancel")}),this.get("targetConfirm")&&this.on(e,"click","onConfirm")},removeEvents:function(){acf.Model.prototype.removeEvents.apply(this);var t=f(document),e=this.get("target");this.off(t,"click"),this.off(e,"click")},render:function(){var t,e,i,n=[this.get("text")||acf.__("Are you sure?"),''+(this.get("textConfirm")||acf.__("Yes"))+"",''+(this.get("textCancel")||acf.__("No"))+""].join(" ");this.html(n),this.$el.addClass("-confirm")},onCancel:function(t,e){t.preventDefault(),t.stopImmediatePropagation();var i=this.get("cancel"),n=this.get("context")||this;i.apply(n,arguments),this.remove()},onConfirm:function(t,e){t.preventDefault(),t.stopImmediatePropagation();var i=this.get("confirm"),n=this.get("context")||this;i.apply(n,arguments),this.remove()}});acf.models.Tooltip=i,acf.models.TooltipConfirm=n;var t=new acf.Model({tooltip:!1,events:{"mouseenter .acf-js-tooltip":"showTitle","mouseup .acf-js-tooltip":"hideTitle","mouseleave .acf-js-tooltip":"hideTitle"},showTitle:function(t,e){var i=e.attr("title");i&&(e.attr("title",""),this.tooltip?this.tooltip.update({text:i,target:e}):this.tooltip=acf.newTooltip({text:i,target:e}))},hideTitle:function(t,e){this.tooltip.hide(),e.attr("title",this.tooltip.get("text"))}})}(jQuery),function(e,i){var r=[];acf.Field=acf.Model.extend({type:"",eventScope:".acf-field",wait:"ready",setup:function(t){this.$el=t,this.inherit(t),this.inherit(this.$control())},val:function(t){return t!==i?this.setValue(t):this.prop("disabled")?null:this.getValue()},getValue:function(){return this.$input().val()},setValue:function(t){return acf.val(this.$input(),t)},__:function(t){return acf._e(this.type,t)},$control:function(){return!1},$input:function(){return this.$("[name]:first")},$inputWrap:function(){return this.$(".acf-input:first")},$labelWrap:function(){return this.$(".acf-label:first")},getInputName:function(){return this.$input().attr("name")||""},parent:function(){var t=this.parents();return!!t.length&&t[0]},parents:function(){var t=this.$el.parents(".acf-field"),e;return acf.getFields(t)},show:function(t,e){var i=acf.show(this.$el,t);return i&&(this.prop("hidden",!1),acf.doAction("show_field",this,e)),i},hide:function(t,e){var i=acf.hide(this.$el,t);return i&&(this.prop("hidden",!0),acf.doAction("hide_field",this,e)),i},enable:function(t,e){var i=acf.enable(this.$el,t);return i&&(this.prop("disabled",!1),acf.doAction("enable_field",this,e)),i},disable:function(t,e){var i=acf.disable(this.$el,t);return i&&(this.prop("disabled",!0),acf.doAction("disable_field",this,e)),i},showEnable:function(t,e){return this.enable.apply(this,arguments),this.show.apply(this,arguments)},hideDisable:function(t,e){return this.disable.apply(this,arguments),this.hide.apply(this,arguments)},showNotice:function(t){"object"!=typeof t&&(t={text:t}),this.notice&&this.notice.remove(),t.target=this.$inputWrap(),this.notice=acf.newNotice(t)},removeNotice:function(t){this.notice&&(this.notice.away(t||0),this.notice=!1)},showError:function(t){this.$el.addClass("acf-error"),t!==i&&this.showNotice({text:t,type:"error",dismiss:!1}),acf.doAction("invalid_field",this),this.$el.one("focus change","input, select, textarea",e.proxy(this.removeError,this))},removeError:function(){this.$el.removeClass("acf-error"),this.removeNotice(250),acf.doAction("valid_field",this)},trigger:function(t,e,i){return"invalidField"==t&&(i=!0),acf.Model.prototype.trigger.apply(this,[t,e,i])}}),acf.newField=function(t){var e=t.data("type"),i=s(e),n,a=new(acf.models[i]||acf.Field)(t);return acf.doAction("new_field",a),a};var s=function(t){return acf.strPascalCase(t||"")+"Field"};acf.registerFieldType=function(t){var e,i=t.prototype.type,n=s(i);acf.models[n]=t,r.push(i)},acf.getFieldType=function(t){var e=s(t);return acf.models[e]||!1},acf.getFieldTypes=function(n){n=acf.parseArgs(n,{category:""});var a=[];return r.map(function(t){var e=acf.getFieldType(t),i=e.prototype;n.category&&i.category!==n.category||a.push(e)}),a}}(jQuery),function(n,t){acf.findFields=function(t){var e=".acf-field",i=!1;return(t=acf.parseArgs(t,{key:"",name:"",type:"",is:"",parent:!1,sibling:!1,limit:!1,visible:!1,suppressFilters:!1})).suppressFilters||(t=acf.applyFilters("find_fields_args",t)),t.key&&(e+='[data-key="'+t.key+'"]'),t.type&&(e+='[data-type="'+t.type+'"]'),t.name&&(e+='[data-name="'+t.name+'"]'),t.is&&(e+=t.is),t.visible&&(e+=":visible"),i=t.parent?t.parent.find(e):t.sibling?t.sibling.siblings(e):n(e),t.suppressFilters||(i=i.not(".acf-clone .acf-field"),i=acf.applyFilters("find_fields",i)),t.limit&&(i=i.slice(0,t.limit)),i},acf.findField=function(t,e){return acf.findFields({key:t,limit:1,parent:e,suppressFilters:!0})},acf.getField=function(t){t instanceof jQuery||(t=acf.findField(t));var e=t.data("acf");return e||(e=acf.newField(t)),e},acf.getFields=function(t){t instanceof jQuery||(t=acf.findFields(t));var e=[];return t.each(function(){var t=acf.getField(n(this));e.push(t)}),e},acf.findClosestField=function(t){return t.closest(".acf-field")},acf.getClosestField=function(t){var e=acf.findClosestField(t);return this.getField(e)};var e=function(t){var e=t,r=t+"_fields",a=t+"_field",i=function(t){var e,i=acf.arrayArgs(arguments).slice(1),n=acf.getFields({parent:t});if(n.length){var a=[r,n].concat(i);acf.doAction.apply(null,a)}},n=function(t){var e,n=acf.arrayArgs(arguments).slice(1);t.map(function(t,e){var i=[a,t].concat(n);acf.doAction.apply(null,i)})};acf.addAction(e,i),acf.addAction(r,n),s(t)},s=function(e){var r=e+"_field",s=e+"Field",t=function(i){var n=acf.arrayArgs(arguments),a=n.slice(1),t;["type","name","key"].map(function(t){var e="/"+t+"="+i.get(t);n=[r+e,i].concat(a),acf.doAction.apply(null,n)}),-1' . acf_esc_html($field['instructions']) . '
'; + } +} + +/** + * acf_render_field_setting + * + * Renders a field setting used in the admin edit screen. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param array $field The field array. + * @param array $setting The settings field array. + * @param bool $global Whether this setting is a global or field type specific one. + * @return void + */ +function acf_render_field_setting( $field, $setting, $global = false ) { + + // Validate field. + $setting = acf_validate_field( $setting ); + + // Add custom attributes to setting wrapper. + $setting['wrapper']['data-key'] = $setting['name']; + $setting['wrapper']['class'] .= ' acf-field-setting-' . $setting['name']; + if( !$global ) { + $setting['wrapper']['data-setting'] = $field['type']; + } + + // Copy across prefix. + $setting['prefix'] = $field['prefix']; + + // Find setting value from field. + if( $setting['value'] === null ) { + + // Name. + if( isset($field[ $setting['name'] ]) ) { + $setting['value'] = $field[ $setting['name'] ]; + + // Default value. + } elseif( isset($setting['default_value']) ) { + $setting['value'] = $setting['default_value']; + } + } + + // Add append attribute used by JS to join settings. + if( isset($setting['_append']) ) { + $setting['wrapper']['data-append'] = $setting['_append']; + } + + // Render setting. + acf_render_field_wrap( $setting, 'tr', 'label' ); +} + +/** + * acf_update_field + * + * Updates a field in the database. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param array $field The field array. + * @param array $specific An array of specific field attributes to update. + * @return void + */ +function acf_update_field( $field, $specific = array() ) { + + // Validate field. + $field = acf_validate_field( $field ); + + // May have been posted. Remove slashes. + $field = wp_unslash( $field ); + + // Parse types (converts string '0' to int 0). + $field = acf_parse_types( $field ); + + // Clean up conditional logic keys. + if( $field['conditional_logic'] ) { + + // Remove empty values and convert to associated array. + $field['conditional_logic'] = array_filter( $field['conditional_logic'] ); + $field['conditional_logic'] = array_values( $field['conditional_logic'] ); + $field['conditional_logic'] = array_map( 'array_filter', $field['conditional_logic'] ); + $field['conditional_logic'] = array_map( 'array_values', $field['conditional_logic'] ); + } + + // Parent may be provided as a field key. + if( $field['parent'] && !is_numeric($field['parent']) ) { + $parent = acf_get_field_post( $field['parent'] ); + $field['parent'] = $parent ? $parent->ID : 0; + } + + /** + * Filters the $field array before it is updated. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field The field array. + */ + $field = apply_filters( "acf/update_field", $field ); + + // Make a backup of field data and remove some args. + $_field = $field; + acf_extract_vars( $_field, array( 'ID', 'key', 'label', 'name', 'prefix', 'value', 'menu_order', 'id', 'class', 'parent', '_name', '_prepare', '_valid' ) ); + + // Create array of data to save. + $save = array( + 'ID' => $field['ID'], + 'post_status' => 'publish', + 'post_type' => 'acf-field', + 'post_title' => $field['label'], + 'post_name' => $field['key'], + 'post_excerpt' => $field['name'], + 'post_content' => maybe_serialize( $_field ), + 'post_parent' => $field['parent'], + 'menu_order' => $field['menu_order'], + ); + + // Reduce save data if specific key list is provided. + if( $specific ) { + $specific[] = 'ID'; + $save = acf_get_sub_array( $save, $specific ); + } + + // Unhook wp_targeted_link_rel() filter from WP 5.1 corrupting serialized data. + remove_filter( 'content_save_pre', 'wp_targeted_link_rel' ); + + // Slash data. + // WP expects all data to be slashed and will unslash it (fixes '\' character issues). + $save = wp_slash( $save ); + + // Update or Insert. + if( $field['ID'] ) { + wp_update_post( $save ); + } else { + $field['ID'] = wp_insert_post( $save ); + } + + // Flush field cache. + acf_flush_field_cache( $field ); + + /** + * Fires after a field has been updated, and the field cache has been cleaned. + * + * @date 24/1/19 + * @since 5.7.10 + * + * @param array $field The field array. + */ + do_action( "acf/updated_field", $field ); + + // Return field. + return $field; +} + +// Register variation. +acf_add_filter_variations( 'acf/update_field', array('type', 'name', 'key'), 0 ); + +/** + * _acf_apply_unique_field_slug + * + * Allows full control over 'acf-field' slugs. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param string $slug The post slug. + * @param int $post_ID Post ID. + * @param string $post_status The post status. + * @param string $post_type Post type. + * @param int $post_parent Post parent ID + * @param string $original_slug The original post slug. + */ +function _acf_apply_unique_field_slug( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) { + + // Check post type and reset to original value. + if( $post_type === 'acf-field' ) { + return $original_slug; + } + + // Return slug. + return $slug; +} + +// Hook into filter. +add_filter( 'wp_unique_post_slug', '_acf_apply_unique_field_slug', 999, 6 ); + +/** + * acf_flush_field_cache + * + * Deletes all caches for this field. + * + * @date 22/1/19 + * @since 5.7.10 + * + * @param array $field The field array. + * @return void + */ +function acf_flush_field_cache( $field ) { + + // Delete stored data. + acf_get_store( 'fields' )->remove( $field['key'] ); + + // Flush cached post_id for this field's name and key. + wp_cache_delete( acf_cache_key("acf_get_field_post:name:{$field['name']}"), 'acf' ); + wp_cache_delete( acf_cache_key("acf_get_field_post:key:{$field['key']}"), 'acf' ); + + // Flush cached array of post_ids for this field's parent. + wp_cache_delete( acf_cache_key("acf_get_field_posts:{$field['parent']}"), 'acf' ); +} + +/** + * acf_delete_field + * + * Deletes a field from the database. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param (int|string) $id The field ID, key or name. + * @return bool True if field was deleted. + */ +function acf_delete_field( $id = 0 ) { + + // Get the field. + $field = acf_get_field( $id ); + + // Bail early if field was not found. + if( !$field || !$field['ID'] ) { + return false; + } + + // Delete post. + wp_delete_post( $field['ID'], true ); + + // Flush field cache. + acf_flush_field_cache( $field ); + + /** + * Fires immediately after a field has been deleted. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field The field array. + */ + do_action( "acf/delete_field", $field ); + + // Return true. + return true; +} + +// Register variation. +acf_add_action_variations( 'acf/delete_field', array('type', 'name', 'key'), 0 ); + +/** + * acf_trash_field + * + * Trashes a field from the database. + * + * @date 2/10/13 + * @since 5.0.0 + * + * @param (int|string) $id The field ID, key or name. + * @return bool True if field was trashed. + */ +function acf_trash_field( $id = 0 ) { + + // Get the field. + $field = acf_get_field( $id ); + + // Bail early if field was not found. + if( !$field || !$field['ID'] ) { + return false; + } + + // Trash post. + wp_trash_post( $field['ID'], true ); + + /** + * Fires immediately after a field has been trashed. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field The field array. + */ + do_action( 'acf/trash_field', $field ); + + // Return true. + return true; +} + +/** + * acf_untrash_field + * + * Restores a field from the trash. + * + * @date 2/10/13 + * @since 5.0.0 + * + * @param (int|string) $id The field ID, key or name. + * @return bool True if field was trashed. + */ +function acf_untrash_field( $id = 0 ) { + + // Get the field. + $field = acf_get_field( $id ); + + // Bail early if field was not found. + if( !$field || !$field['ID'] ) { + return false; + } + + // Untrash post. + wp_untrash_post( $field['ID'], true ); + + // Flush field cache. + acf_flush_field_cache( $field ); + + /** + * Fires immediately after a field has been trashed. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field The field array. + */ + do_action( 'acf/untrash_field', $field ); + + // Return true. + return true; +} + +/** + * acf_prefix_fields + * + * Changes the prefix for an array of fields by reference. + * + * @date 5/9/17 + * @since 5.6.0 + * + * @param array $fields An array of fields. + * @param string $prefix The new prefix. + * @return void + */ +function acf_prefix_fields( &$fields, $prefix = 'acf' ) { + + // Loopover fields. + foreach( $fields as &$field ) { + + // Replace 'acf' with $prefix. + $field['prefix'] = $prefix . substr($field['prefix'], 3); + } +} + +/** + * acf_get_sub_field + * + * Searches a field for sub fields matching the given selector. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param (int|string) $id The field ID, key or name. + * @param array $field The parent field array. + * @return (array|false) + */ +function acf_get_sub_field( $id, $field ) { + + // Vars. + $sub_field = false; + + // Search sub fields. + if( isset($field['sub_fields']) ) { + $sub_field = acf_search_fields( $id, $field['sub_fields'] ); + } + + /** + * Filters the $sub_field found. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $sub_field The found sub field array. + * @param string $selector The selector used to search. + * @param array $field The parent field array. + */ + $sub_field = apply_filters( "acf/get_sub_field", $sub_field, $id, $field ); + + // return + return $sub_field; + +} + +// Register variation. +acf_add_filter_variations( 'acf/get_sub_field', array('type'), 2 ); + +/** + * acf_search_fields + * + * Searches an array of fields for one that matches the given identifier. + * + * @date 12/2/19 + * @since 5.7.11 + * + * @param (int|string) $id The field ID, key or name. + * @param array $haystack The array of fields. + * @return (int|false) + */ +function acf_search_fields( $id, $fields ) { + + // Loop over searchable keys in order of priority. + // Important to search "name" on all fields before "_name" backup. + foreach( array( 'key', 'name', '_name', '__name' ) as $key ) { + + // Loop over fields and compare. + foreach( $fields as $field ) { + if( isset($field[$key]) && $field[$key] === $id ) { + return $field; + } + } + } + + // Return not found. + return false; +} + +/** + * acf_is_field + * + * Returns true if the given params match a field. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param array $field The field array. + * @param mixed $id An optional identifier to search for. + * @return bool + */ +function acf_is_field( $field = false, $id = '' ) { + return ( + is_array($field) + && isset($field['key']) + && isset($field['name']) + ); +} + +/** + * acf_get_field_ancestors + * + * Returns an array of ancestor field ID's or keys. + * + * @date 22/06/2016 + * @since 5.3.8 + * + * @param array $field The field array. + * @return array + */ +function acf_get_field_ancestors( $field ) { + + // Vars. + $ancestors = array(); + + // Loop over parents. + while( $field = acf_get_field($field['parent']) ) { + $ancestors[] = $field['ID'] ? $field['ID'] : $field['key']; + } + + // return + return $ancestors; +} + +/** + * acf_duplicate_fields + * + * Duplicate an array of fields. + * + * @date 16/06/2014 + * @since 5.0.0 + * + * @param array $fields An array of fields. + * @param int $parent_id The new parent ID. + * @return array + */ +function acf_duplicate_fields( $fields = array(), $parent_id = 0 ) { + + // Vars. + $duplicates = array(); + + // Loop over fields and pre-generate new field keys (needed for conditional logic). + $keys = array(); + foreach( $fields as $field ) { + + // Delay for a microsecond to ensure a unique ID. + usleep(1); + $keys[ $field['key'] ] = uniqid('field_'); + } + + // Store these keys for later use. + acf_set_data( 'duplicates', $keys ); + + // Duplicate fields. + foreach( $fields as $field ) { + $duplicates[] = acf_duplicate_field( $field['ID'], $parent_id ); + } + + // Return. + return $duplicates; +} + +/** + * acf_duplicate_field + * + * Duplicates a field. + * + * @date 16/06/2014 + * @since 5.0.0 + * + * @param (int|string) $id The field ID, key or name. + * @param int $parent_id The new parent ID. + * @return bool True if field was duplicated. + */ +function acf_duplicate_field( $id = 0, $parent_id = 0 ){ + + // Get the field. + $field = acf_get_field( $id ); + + // Bail early if field was not found. + if( !$field ) { + return false; + } + + // Remove ID to avoid update. + $field['ID'] = 0; + + // Generate key. + $keys = acf_get_data( 'duplicates' ); + $field['key'] = isset($keys[ $field['key'] ]) ? $keys[ $field['key'] ] : uniqid('field_'); + + // Set parent. + if( $parent_id ) { + $field['parent'] = $parent_id; + } + + // Update conditional logic references because field keys have changed. + if( $field['conditional_logic'] ) { + + // Loop over groups + foreach( $field['conditional_logic'] as $group_i => $group ) { + + // Loop over rules + foreach( $group as $rule_i => $rule ) { + $field['conditional_logic'][ $group_i ][ $rule_i ]['field'] = isset($keys[ $rule['field'] ]) ? $keys[ $rule['field'] ] : $rule['field']; + } + } + } + + /** + * Filters the $field array after it has been duplicated. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field The field array. + */ + $field = apply_filters( "acf/duplicate_field", $field); + + // Update and return. + return acf_update_field( $field ); +} + +// Register variation. +acf_add_filter_variations( 'acf/duplicate_field', array('type'), 0 ); + +/** + * acf_prepare_fields_for_export + * + * Returns a modified array of fields ready for export. + * + * @date 11/03/2014 + * @since 5.0.0 + * + * @param array $fields An array of fields. + * @return array + */ +function acf_prepare_fields_for_export( $fields = array() ) { + + // Map function and return. + return array_map( 'acf_prepare_field_for_export', $fields ); +} + +/** + * acf_prepare_field_for_export + * + * Returns a modified field ready for export. + * + * @date 11/03/2014 + * @since 5.0.0 + * + * @param array $field The field array. + * @return array + */ +function acf_prepare_field_for_export( $field ) { + + // Remove args. + acf_extract_vars( $field, array( 'ID', 'prefix', 'value', 'menu_order', 'id', 'class', 'parent', '_name', '_prepare', '_valid' ) ); + + /** + * Filters the $field array before being returned to the export tool. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field The field array. + */ + $field = apply_filters( "acf/prepare_field_for_export", $field ); + + // Return field. + return $field; +} + +// Register variation. +acf_add_filter_variations( 'acf/prepare_field_for_export', array('type'), 0 ); + +/** + * acf_prepare_field_for_import + * + * Returns a modified array of fields ready for import. + * + * @date 11/03/2014 + * @since 5.0.0 + * + * @param array $fields An array of fields. + * @return array + */ +function acf_prepare_fields_for_import( $fields = array() ) { + + // Ensure array indexes are clean. + $fields = array_values($fields); + + // Loop through fields allowing for growth. + $i = 0; + while( $i < count($fields) ) { + + // Prepare for import. + $field = acf_prepare_field_for_import( $fields[ $i ] ); + + // Allow multiple fields to be returned (parent + children). + if( is_array($field) && !isset($field['key']) ) { + + // Replace this field ($i) with all returned fields. + array_splice( $fields, $i, 1, $field ); + } + + // Iterate. + $i++; + } + + /** + * Filters the $fields array before being returned to the import tool. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field The field array. + */ + $fields = apply_filters( 'acf/prepare_fields_for_import', $fields ); + + // Return. + return $fields; +} + +/** + * acf_prepare_field_for_import + * + * Returns a modified field ready for import. + * Allows parent fields to modify themselves and also return sub fields. + * + * @date 11/03/2014 + * @since 5.0.0 + * + * @param array $field The field array. + * @return array + */ +function acf_prepare_field_for_import( $field ) { + + /** + * Filters the $field array before being returned to the import tool. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field The field array. + */ + $field = apply_filters( "acf/prepare_field_for_import", $field ); + + // Return field. + return $field; +} + +// Register variation. +acf_add_filter_variations( 'acf/prepare_field_for_import', array('type'), 0 ); \ No newline at end of file diff --git a/includes/acf-field-group-functions.php b/includes/acf-field-group-functions.php new file mode 100644 index 0000000..8d0512c --- /dev/null +++ b/includes/acf-field-group-functions.php @@ -0,0 +1,1071 @@ +prop( 'multisite', true ); + +/** + * acf_get_field_group + * + * Retrieves a field group for the given identifier. + * + * @date 30/09/13 + * @since 5.0.0 + * + * @param (int|string) $id The field group ID, key or name. + * @return (array|false) The field group array. + */ +function acf_get_field_group( $id = 0 ) { + + // Allow WP_Post to be passed. + if( is_object($id) ) { + $id = $id->ID; + } + + // Check store. + $store = acf_get_store( 'field-groups' ); + if( $store->has( $id ) ) { + return $store->get( $id ); + } + + // Check local fields first. + if( acf_is_local_field_group($id) ) { + $field_group = acf_get_local_field_group( $id ); + + // Then check database. + } else { + $field_group = acf_get_raw_field_group( $id ); + } + + // Bail early if no field group. + if( !$field_group ) { + return false; + } + + // Validate field group. + $field_group = acf_validate_field_group( $field_group ); + + /** + * Filters the $field_group array after it has been loaded. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array The field_group array. + */ + $field_group = apply_filters( 'acf/get_field_group', $field_group ); + $field_group = apply_filters( 'acf/load_field_group', $field_group ); + + // Store field group using aliasses to also find via key, ID and name. + $store->set( $field_group['key'], $field_group ); + $store->alias( $field_group['key'], $field_group['ID'] ); + + // Return. + return $field_group; +} + +/** + * acf_get_raw_field_group + * + * Retrieves raw field group data for the given identifier. + * + * @date 18/1/19 + * @since 5.7.10 + * + * @param (int|string) $id The field ID, key or name. + * @return (array|false) The field group array. + */ +function acf_get_raw_field_group( $id = 0 ) { + + // Get raw field group from database. + $post = acf_get_field_group_post( $id ); + if( !$post ) { + return false; + } + + // Bail early if incorrect post type. + if( $post->post_type !== 'acf-field-group' ) { + return false; + } + + // Unserialize post_content. + $field_group = (array) maybe_unserialize( $post->post_content ); + + // update attributes + $field_group['ID'] = $post->ID; + $field_group['title'] = $post->post_title; + $field_group['key'] = $post->post_name; + $field_group['menu_order'] = $post->menu_order; + $field_group['active'] = in_array($post->post_status, array('publish', 'auto-draft')); + + // Return field. + return $field_group; +} + +/** + * acf_get_field_group_post + * + * Retrieves the field group's WP_Post object. + * + * @date 18/1/19 + * @since 5.7.10 + * + * @param (int|string) $id The field group's ID, key or name. + * @return (array|false) The field group's array. + */ +function acf_get_field_group_post( $id = 0 ) { + + // Get post if numeric. + if( is_numeric($id) ) { + return get_post( $id ); + + // Search posts if is string. + } elseif( is_string($id) ) { + + // Try cache. + $cache_key = acf_cache_key( "acf_get_field_group_post:key:$id" ); + $post_id = wp_cache_get( $cache_key, 'acf' ); + if( $post_id === false ) { + + // Query posts. + $posts = get_posts(array( + 'posts_per_page' => 1, + 'post_type' => 'acf-field-group', + 'post_status' => array('publish', 'acf-disabled', 'trash'), + 'orderby' => 'menu_order title', + 'order' => 'ASC', + 'suppress_filters' => false, + 'cache_results' => true, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'acf_group_key' => $id + )); + + // Update $post_id with a non false value. + $post_id = $posts ? $posts[0]->ID : 0; + + // Update cache. + wp_cache_set( $cache_key, $post_id, 'acf' ); + } + + // Check $post_id and return the post when possible. + if( $post_id ) { + return get_post( $post_id ); + } + } + + // Return false by default. + return false; +} + +/** + * acf_is_field_group_key + * + * Returns true if the given identifier is a field group key. + * + * @date 6/12/2013 + * @since 5.0.0 + * + * @param string $id The identifier. + * @return bool + */ +function acf_is_field_group_key( $id = '' ) { + + // Check if $id is a string starting with "group_". + if( is_string($id) && substr($id, 0, 6) === 'group_' ) { + return true; + } + + /** + * Filters whether the $id is a field group key. + * + * @date 23/1/19 + * @since 5.7.10 + * + * @param bool $bool The result. + * @param string $id The identifier. + */ + return apply_filters( 'acf/is_field_group_key', false, $id ); +} + +/** + * acf_validate_field_group + * + * Ensures the given field group is valid. + * + * @date 18/1/19 + * @since 5.7.10 + * + * @param array $field The field group array. + * @return array + */ +function acf_validate_field_group( $field_group = array() ) { + + // Bail early if already valid. + if( is_array($field_group) && !empty($field_group['_valid']) ) { + return $field_group; + } + + // Apply defaults. + $field_group = wp_parse_args($field_group, array( + 'ID' => 0, + 'key' => '', + 'title' => '', + 'fields' => array(), + 'location' => array(), + 'menu_order' => 0, + 'position' => 'normal', + 'style' => 'default', + 'label_placement' => 'top', + 'instruction_placement' => 'label', + 'hide_on_screen' => array(), + 'active' => true, + 'description' => '', + )); + + // Field group is now valid. + $field_group['_valid'] = 1; + + /** + * Filters the $field_group array to validate settings. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field_group The field group array. + */ + $field_group = apply_filters( 'acf/validate_field_group', $field_group ); + + // return + return $field_group; +} + +/** + * acf_get_valid_field_group + * + * Ensures the given field group is valid. + * + * @date 28/09/13 + * @since 5.0.0 + * + * @param array $field_group The field group array. + * @return array + */ +function acf_get_valid_field_group( $field_group = false ) { + return acf_validate_field_group( $field_group ); +} + +/** + * acf_translate_field_group + * + * Translates a field group's settings. + * + * @date 8/03/2016 + * @since 5.3.2 + * + * @param array $field_group The field group array. + * @return array + */ +function acf_translate_field_group( $field_group = array() ) { + + // Get settings. + $l10n = acf_get_setting('l10n'); + $l10n_textdomain = acf_get_setting('l10n_textdomain'); + + // Translate field settings if textdomain is set. + if( $l10n && $l10n_textdomain ) { + + $field_group['title'] = acf_translate( $field_group['title'] ); + + /** + * Filters the $field group array to translate strings. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field_group The field group array. + */ + $field_group = apply_filters( "acf/translate_field_group", $field_group ); + } + + // Return field. + return $field_group; +} + +// Translate field groups passing through validation. +add_action('acf/validate_field_group', 'acf_translate_field_group'); + + +/** + * acf_get_field_groups + * + * Returns and array of field_groups for the given $filter. + * + * @date 30/09/13 + * @since 5.0.0 + * + * @param array $filter An array of args to filter results by. + * @return array + */ +function acf_get_field_groups( $filter = array() ) { + + // Vars. + $field_groups = array(); + + // Check database. + $raw_field_groups = acf_get_raw_field_groups(); + if( $raw_field_groups ) { + foreach( $raw_field_groups as $raw_field_group ) { + $field_groups[] = acf_get_field_group( $raw_field_group['ID'] ); + } + } + + + /** + * Filters the $field_groups array. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field_groups The array of field_groups. + */ + $field_groups = apply_filters( 'acf/load_field_groups', $field_groups ); + $field_groups = apply_filters( 'acf/get_field_groups', $field_groups ); + + // Filter results. + if( $filter ) { + return acf_filter_field_groups( $field_groups, $filter ); + } + + // Return field groups. + return $field_groups; +} + +/** + * acf_get_raw_field_groups + * + * Returns and array of raw field_group data. + * + * @date 18/1/19 + * @since 5.7.10 + * + * @param void + * @return array + */ +function acf_get_raw_field_groups() { + + // Try cache. + $cache_key = acf_cache_key( "acf_get_field_group_posts" ); + $post_ids = wp_cache_get( $cache_key, 'acf' ); + if( $post_ids === false ) { + + // Query posts. + $posts = get_posts(array( + 'posts_per_page' => -1, + 'post_type' => 'acf-field-group', + 'orderby' => 'menu_order title', + 'order' => 'ASC', + 'suppress_filters' => false, // Allow WPML to modify the query + 'cache_results' => true, + 'update_post_meta_cache' => false, + 'update_post_term_cache' => false, + 'post_status' => array('publish', 'acf-disabled'), + )); + + // Update $post_ids with a non false value. + $post_ids = array(); + foreach( $posts as $post ) { + $post_ids[] = $post->ID; + } + + // Update cache. + wp_cache_set( $cache_key, $post_ids, 'acf' ); + } + + // Loop over ids and populate array of field groups. + $field_groups = array(); + foreach( $post_ids as $post_id ) { + $field_groups[] = acf_get_raw_field_group( $post_id ); + } + + // Return field groups. + return $field_groups; +} + +/** + * acf_filter_field_groups + * + * Returns a filtered aray of field groups based on the given $args. + * + * @date 29/11/2013 + * @since 5.0.0 + * + * @param array $field_groups An array of field groups. + * @param array $args An array of location args. + * @return array + */ +function acf_filter_field_groups( $field_groups, $args = array() ) { + + // Loop over field groups and check visibility. + $filtered = array(); + if( $field_groups ) { + foreach( $field_groups as $field_group ) { + if( acf_get_field_group_visibility( $field_group, $args ) ) { + $filtered[] = $field_group; + } + } + } + + // Return filtered. + return $filtered; +} + +/** + * acf_get_field_group_visibility + * + * Returns true if the given field group's location rules match the given $args. + * + * @date 7/10/13 + * @since 5.0.0 + * + * @param array $field_groups An array of field groups. + * @param array $args An array of location args. + * @return bool + */ +function acf_get_field_group_visibility( $field_group, $args = array() ) { + + // Check if active. + if( !$field_group['active'] ) { + return false; + } + + // Check if location rules exist + if( $field_group['location'] ) { + + // Get the current screen. + $screen = acf_get_location_screen( $args ); + + // Loop through location groups. + foreach( $field_group['location'] as $group ) { + + // ignore group if no rules. + if( empty($group) ) { + continue; + } + + // Loop over rules and determine if all rules match. + $match_group = true; + foreach( $group as $rule ) { + if( !acf_match_location_rule( $rule, $screen ) ) { + $match_group = false; + break; + } + } + + // If this group matches, show the field group. + if( $match_group ) { + return true; + } + } + } + + // Return default. + return false; +} + +/** + * acf_update_field_group + * + * Updates a field group in the database. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param array $field_group The field group array. + * @return array + */ +function acf_update_field_group( $field_group ) { + + // Validate field group. + $field_group = acf_get_valid_field_group( $field_group ); + + // May have been posted. Remove slashes. + $field_group = wp_unslash( $field_group ); + + // Parse types (converts string '0' to int 0). + $field_group = acf_parse_types( $field_group ); + + // Clean up location keys. + if( $field_group['location'] ) { + + // Remove empty values and convert to associated array. + $field_group['location'] = array_filter( $field_group['location'] ); + $field_group['location'] = array_values( $field_group['location'] ); + $field_group['location'] = array_map( 'array_filter', $field_group['location'] ); + $field_group['location'] = array_map( 'array_values', $field_group['location'] ); + } + + // Make a backup of field group data and remove some args. + $_field_group = $field_group; + acf_extract_vars( $_field_group, array( 'ID', 'key', 'title', 'menu_order', 'fields', 'active', '_valid' ) ); + + // Create array of data to save. + $save = array( + 'ID' => $field_group['ID'], + 'post_status' => $field_group['active'] ? 'publish' : 'acf-disabled', + 'post_type' => 'acf-field-group', + 'post_title' => $field_group['title'], + 'post_name' => $field_group['key'], + 'post_excerpt' => sanitize_title( $field_group['title'] ), + 'post_content' => maybe_serialize( $_field_group ), + 'menu_order' => $field_group['menu_order'], + ); + + // Unhook wp_targeted_link_rel() filter from WP 5.1 corrupting serialized data. + remove_filter( 'content_save_pre', 'wp_targeted_link_rel' ); + + // Slash data. + // WP expects all data to be slashed and will unslash it (fixes '\' character issues). + $save = wp_slash( $save ); + + // Update or Insert. + if( $field_group['ID'] ) { + wp_update_post( $save ); + } else { + $field_group['ID'] = wp_insert_post( $save ); + } + + // Flush field group cache. + acf_flush_field_group_cache( $field_group ); + + /** + * Fires immediately after a field group has been updated. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field_group The field group array. + */ + do_action( 'acf/update_field_group', $field_group ); + + // Return field group. + return $field_group; +} + +/** + * _acf_apply_unique_field_group_slug + * + * Allows full control over 'acf-field-group' slugs. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param string $slug The post slug. + * @param int $post_ID Post ID. + * @param string $post_status The post status. + * @param string $post_type Post type. + * @param int $post_parent Post parent ID + * @param string $original_slug The original post slug. + */ +function _acf_apply_unique_field_group_slug( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) { + + // Check post type and reset to original value. + if( $post_type === 'acf-field-group' ) { + return $original_slug; + } + + // Return slug. + return $slug; +} + +// Hook into filter. +add_filter( 'wp_unique_post_slug', '_acf_apply_unique_field_group_slug', 999, 6 ); + +/** + * acf_flush_field_group_cache + * + * Deletes all caches for this field group. + * + * @date 22/1/19 + * @since 5.7.10 + * + * @param array $field_group The field group array. + * @return void + */ +function acf_flush_field_group_cache( $field_group ) { + + // Delete stored data. + acf_get_store( 'field-groups' )->remove( $field_group['key'] ); + + // Flush cached post_id for this field group's key. + wp_cache_delete( acf_cache_key( "acf_get_field_group_post:key:{$field_group['key']}" ), 'acf' ); + + // Flush cached array of post_ids for collection of field groups. + wp_cache_delete( acf_cache_key( "acf_get_field_group_posts" ), 'acf' ); +} + +/** + * acf_delete_field_group + * + * Deletes a field group from the database. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param (int|string) $id The field group ID, key or name. + * @return bool True if field group was deleted. + */ +function acf_delete_field_group( $id = 0 ) { + + // Disable filters to ensure ACF loads data from DB. + acf_disable_filters(); + + // Get the field_group. + $field_group = acf_get_field_group( $id ); + + // Bail early if field group was not found. + if( !$field_group || !$field_group['ID'] ) { + return false; + } + + // Delete fields. + $fields = acf_get_fields( $field_group ); + if( $fields ) { + foreach( $fields as $field ) { + acf_delete_field( $field['ID'] ); + } + } + + // Delete post. + wp_delete_post( $field_group['ID'], true ); + + // Flush field group cache. + acf_flush_field_group_cache( $field_group ); + + /** + * Fires immediately after a field group has been deleted. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field_group The field group array. + */ + do_action( 'acf/delete_field_group', $field_group ); + + // Return true. + return true; +} + +/** + * acf_trash_field_group + * + * Trashes a field group from the database. + * + * @date 2/10/13 + * @since 5.0.0 + * + * @param (int|string) $id The field group ID, key or name. + * @return bool True if field group was trashed. + */ +function acf_trash_field_group( $id = 0 ) { + + // Disable filters to ensure ACF loads data from DB. + acf_disable_filters(); + + // Get the field_group. + $field_group = acf_get_field_group( $id ); + + // Bail early if field_group was not found. + if( !$field_group || !$field_group['ID'] ) { + return false; + } + + // Trash fields. + $fields = acf_get_fields( $field_group ); + if( $fields ) { + foreach( $fields as $field ) { + acf_trash_field( $field['ID'] ); + } + } + + // Trash post. + wp_trash_post( $field_group['ID'], true ); + + // Flush field group cache. + acf_flush_field_group_cache( $field_group ); + + /** + * Fires immediately after a field_group has been trashed. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field_group The field_group array. + */ + do_action( 'acf/trash_field_group', $field_group ); + + // Return true. + return true; +} + +/** + * acf_untrash_field_group + * + * Restores a field_group from the trash. + * + * @date 2/10/13 + * @since 5.0.0 + * + * @param (int|string) $id The field_group ID, key or name. + * @return bool True if field_group was trashed. + */ +function acf_untrash_field_group( $id = 0 ) { + + // Disable filters to ensure ACF loads data from DB. + acf_disable_filters(); + + // Get the field_group. + $field_group = acf_get_field_group( $id ); + + // Bail early if field_group was not found. + if( !$field_group || !$field_group['ID'] ) { + return false; + } + + // Untrash fields. + $fields = acf_get_fields( $field_group ); + if( $fields ) { + foreach( $fields as $field ) { + acf_untrash_field( $field['ID'] ); + } + } + + // Untrash post. + wp_untrash_post( $field_group['ID'], true ); + + // Flush field group cache. + acf_flush_field_group_cache( $field_group ); + + /** + * Fires immediately after a field_group has been trashed. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field_group The field_group array. + */ + do_action( 'acf/untrash_field_group', $field_group ); + + // Return true. + return true; +} + +/** + * acf_is_field_group + * + * Returns true if the given params match a field group. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param array $field_group The field group array. + * @param mixed $id An optional identifier to search for. + * @return bool + */ +function acf_is_field_group( $field_group = false ) { + return ( + is_array($field_group) + && isset($field_group['key']) + && isset($field_group['title']) + ); +} + +/** + * acf_duplicate_field_group + * + * Duplicates a field group. + * + * @date 16/06/2014 + * @since 5.0.0 + * + * @param (int|string) $id The field_group ID, key or name. + * @param int $new_post_id Optional post ID to override. + * @return array The new field group. + */ +function acf_duplicate_field_group( $id = 0, $new_post_id = 0 ){ + + // Disable filters to ensure ACF loads data from DB. + acf_disable_filters(); + + // Get the field_group. + $field_group = acf_get_field_group( $id ); + + // Bail early if field_group was not found. + if( !$field_group || !$field_group['ID'] ) { + return false; + } + + // Get fields. + $fields = acf_get_fields( $field_group ); + + // Update attributes. + $field_group['ID'] = $new_post_id; + $field_group['key'] = uniqid('group_'); + + // Add (copy) to title when apropriate. + if( !$new_post_id ) { + $field_group['title'] .= ' (' . __("copy", 'acf') . ')'; + } + + // When importing a new field group, insert a temporary post and set the field group's ID. + // This allows fields to be updated before the field group (field group ID is needed for field parent setting). + if( !$field_group['ID'] ) { + $field_group['ID'] = wp_insert_post( array( 'post_title' => $field_group['key'] ) ); + } + + // Duplicate fields. + $duplicates = acf_duplicate_fields( $fields, $field_group['ID'] ); + + // Save field group. + $field_group = acf_update_field_group( $field_group ); + + /** + * Fires immediately after a field_group has been duplicated. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field_group The field_group array. + */ + do_action( 'acf/duplicate_field_group', $field_group ); + + // return + return $field_group; +} + +/** + * acf_get_field_group_style + * + * Returns the CSS styles generated from field group settings. + * + * @date 20/10/13 + * @since 5.0.0 + * + * @param array $field_group The field group array. + * @return string. + */ +function acf_get_field_group_style( $field_group ) { + + // Vars. + $style = ''; + $elements = array( + 'permalink' => '#edit-slug-box', + 'the_content' => '#postdivrich', + 'excerpt' => '#postexcerpt', + 'custom_fields' => '#postcustom', + 'discussion' => '#commentstatusdiv', + 'comments' => '#commentsdiv', + 'slug' => '#slugdiv', + 'author' => '#authordiv', + 'format' => '#formatdiv', + 'page_attributes' => '#pageparentdiv', + 'featured_image' => '#postimagediv', + 'revisions' => '#revisionsdiv', + 'categories' => '#categorydiv', + 'tags' => '#tagsdiv-post_tag', + 'send-trackbacks' => '#trackbacksdiv' + ); + + // Loop over field group settings and generate list of selectors to hide. + if( is_array($field_group['hide_on_screen']) ) { + $hide = array(); + foreach( $field_group['hide_on_screen'] as $k ) { + if( isset($elements[ $k ]) ) { + $id = $elements[ $k ]; + $hide[] = $id; + $hide[] = '#screen-meta label[for=' . substr($id, 1) . '-hide]'; + } + } + $style = implode(', ', $hide) . ' {display: none;}'; + } + + /** + * Filters the generated CSS styles. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param string $style The CSS styles. + * @param array $field_group The field group array. + */ + return apply_filters('acf/get_field_group_style', $style, $field_group); +} + +/** + * acf_get_field_group_edit_link + * + * Checks if the current user can edit the field group and returns the edit url. + * + * @date 23/9/18 + * @since 5.7.7 + * + * @param int $post_id The field group ID. + * @return string + */ +function acf_get_field_group_edit_link( $post_id ) { + if( $post_id && acf_current_user_can_admin() ) { + return admin_url('post.php?post=' . $post_id . '&action=edit'); + } + return ''; +} + +/** + * acf_prepare_field_group_for_export + * + * Returns a modified field group ready for export. + * + * @date 11/03/2014 + * @since 5.0.0 + * + * @param array $field_group The field group array. + * @return array + */ +function acf_prepare_field_group_for_export( $field_group = array() ) { + + // Remove args. + acf_extract_vars( $field_group, array( 'ID', 'local', '_valid' ) ); + + // Prepare fields. + $field_group['fields'] = acf_prepare_fields_for_export( $field_group['fields'] ); + + /** + * Filters the $field_group array before being returned to the export tool. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field_group The $field group array. + */ + return apply_filters( 'acf/prepare_field_group_for_export', $field_group ); +} + +/** + * acf_import_field_group + * + * Imports a field group into the databse. + * + * @date 11/03/2014 + * @since 5.0.0 + * + * @param array $field_group The field group array. + * @return array The new field group. + */ +function acf_import_field_group( $field_group ) { + + // Disable filters to ensure data is not modified by local, clone, etc. + $filters = acf_disable_filters(); + + // Validate field group. + $field_group = acf_get_valid_field_group( $field_group ); + + // Stores a map of field "key" => "ID". + $ids = array(); + + // Stores a map of field "parent_key" => "child_count". + $count = array(); + + // Prepare fields for import. + $fields = acf_prepare_fields_for_import( $field_group['fields'] ); + + // If the field group has an ID, review and delete stale fields in the databse. + if( $field_group['ID'] ) { + + // Load database fields. + $db_fields = acf_get_fields( $field_group ); + $db_fields = acf_prepare_fields_for_import( $db_fields ); + + // Generate map of "index" => "key" data. + $keys = wp_list_pluck( $fields, 'key' ); + + // Loop over db fields and delete those who don't exist in $new_fields. + foreach( $db_fields as $field ) { + + // Add field data to $ids map. + $ids[ $field['key'] ] = $field['ID']; + + // Delete field if not in $keys. + if( !in_array($field['key'], $keys) ) { + acf_delete_field( $field['ID'] ); + } + } + } + + // When importing a new field group, insert a temporary post and set the field group's ID. + // This allows fields to be updated before the field group (field group ID is needed for field parent setting). + if( !$field_group['ID'] ) { + $field_group['ID'] = wp_insert_post( array( 'post_title' => $field_group['key'] ) ); + } + + // Add field group data to $ids map. + $ids[ $field_group['key'] ] = $field_group['ID']; + + // Add count to map. + $count[ $field_group['ID'] ] = 0; + + // Loop over and add fields. + if( $fields ) { + foreach( $fields as $field ) { + + // Check $ids map for existing ID for this key. + if( isset($ids[ $field['key'] ]) ) { + $field['ID'] = $ids[ $field['key'] ]; + } + + // Add field group as parent. + if( empty($field['parent']) ) { + $field['parent'] = $field_group['ID']; + + // Check $ids map for existing parent + } elseif( isset($ids[ $field['parent'] ]) ) { + $field['parent'] = $ids[ $field['parent'] ]; + } + + // Add field menu_order. + if( !isset($count[ $field['parent'] ]) ) { + $count[ $field['parent'] ] = 1; + } else { + $count[ $field['parent'] ]++; + } + + // Only add menu order if doesn't already exist. + // Allows Flexible Content field to set custom order. + if( empty($field['menu_order']) ) { + $field['menu_order'] = ($count[ $field['parent'] ] - 1); + } + + // Save field. + $field = acf_update_field( $field ); + + // Add field data to $ids map. + $ids[ $field['key'] ] = $field['ID']; + } + } + + // Save field group. + $field_group = acf_update_field_group( $field_group ); + + // Enable filters again. + acf_enable_filters( $filters ); + + /** + * Fires immediately after a field_group has been imported. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $field_group The field_group array. + */ + do_action( 'acf/import_field_group', $field_group ); + + // return new field group. + return $field_group; +} diff --git a/includes/acf-form-functions.php b/includes/acf-form-functions.php index ca05607..e430b74 100644 --- a/includes/acf-form-functions.php +++ b/includes/acf-form-functions.php @@ -153,20 +153,9 @@ function acf_save_post( $post_id = 0, $values = null ) { */ function _acf_do_save_post( $post_id = 0 ) { - // Check $_POST data. + // Check and update $_POST data. if( $_POST['acf'] ) { - - // Loop over posted values. - foreach( $_POST['acf'] as $key => $value ) { - - // Get field. - $field = acf_get_field( $key ); - - // Update value. - if( $field ) { - acf_update_value( $value, $post_id, $field ); - } - } + acf_update_values( $_POST['acf'], $post_id ); } } diff --git a/includes/acf-helper-functions.php b/includes/acf-helper-functions.php index 034c890..e09c424 100644 --- a/includes/acf-helper-functions.php +++ b/includes/acf-helper-functions.php @@ -22,3 +22,204 @@ function acf_uniqid( $prefix = 'acf' ) { // Return id. return $prefix . '-' . $acf_uniqid++; } + +/** + * acf_merge_attributes + * + * Merges together two arrays but with extra functionality to append class names. + * + * @date 22/1/19 + * @since 5.7.10 + * + * @param array $array1 An array of attributes. + * @param array $array2 An array of attributes. + * @return array + */ +function acf_merge_attributes( $array1, $array2 ) { + + // Merge together attributes. + $array3 = array_merge( $array1, $array2 ); + + // Append together special attributes. + foreach( array('class', 'style') as $key ) { + if( isset($array1[$key]) && isset($array2[$key]) ) { + $array3[$key] = trim($array1[$key]) . ' ' . trim($array2[$key]); + } + } + + // Return + return $array3; +} + +/** + * acf_cache_key + * + * Returns a filtered cache key. + * + * @date 25/1/19 + * @since 5.7.11 + * + * @param string $key The cache key. + * @return string + */ +function acf_cache_key( $key = '' ) { + + /** + * Filters the cache key. + * + * @date 25/1/19 + * @since 5.7.11 + * + * @param string $key The cache key. + * @param string $original_key The original cache key. + */ + return apply_filters( "acf/get_cache_key", $key, $key ); +} + +/** + * acf_request_args + * + * Returns an array of $_REQUEST values using the provided defaults. + * + * @date 28/2/19 + * @since 5.7.13 + * + * @param array $args An array of args. + * @return array + */ +function acf_request_args( $args = array() ) { + foreach( $args as $k => $v ) { + $args[ $k ] = isset($_REQUEST[ $k ]) ? $_REQUEST[ $k ] : $args[ $k ]; + } + return $args; +} + +// Register store. +acf_register_store( 'filters' ); + +/** + * acf_enable_filter + * + * Enables a filter with the given name. + * + * @date 14/7/16 + * @since 5.4.0 + * + * @param string name The modifer name. + * @return void + */ +function acf_enable_filter( $name = '' ) { + acf_get_store( 'filters' )->set( $name, true ); +} + +/** + * acf_disable_filter + * + * Disables a filter with the given name. + * + * @date 14/7/16 + * @since 5.4.0 + * + * @param string name The modifer name. + * @return void + */ +function acf_disable_filter( $name = '' ) { + acf_get_store( 'filters' )->set( $name, false ); +} + +/** + * acf_is_filter_enabled + * + * Returns the state of a filter for the given name. + * + * @date 14/7/16 + * @since 5.4.0 + * + * @param string name The modifer name. + * @return array + */ +function acf_is_filter_enabled( $name = '' ) { + return acf_get_store( 'filters' )->get( $name ); +} + +/** + * acf_get_filters + * + * Returns an array of filters in their current state. + * + * @date 14/7/16 + * @since 5.4.0 + * + * @param void + * @return array + */ +function acf_get_filters() { + return acf_get_store( 'filters' )->get(); +} + +/** + * acf_set_filters + * + * Sets an array of filter states. + * + * @date 14/7/16 + * @since 5.4.0 + * + * @param array $filters An Array of modifers + * @return array + */ +function acf_set_filters( $filters = array() ) { + acf_get_store( 'filters' )->set( $filters ); +} + +/** + * acf_disable_filters + * + * Disables all filters and returns the previous state. + * + * @date 14/7/16 + * @since 5.4.0 + * + * @param void + * @return array + */ +function acf_disable_filters() { + + // Get state. + $prev_state = acf_get_filters(); + + // Set all modifers as false. + acf_set_filters( array_map('__return_false', $prev_state) ); + + // Return prev state. + return $prev_state; +} + +/** + * acf_enable_filters + * + * Enables all or an array of specific filters and returns the previous state. + * + * @date 14/7/16 + * @since 5.4.0 + * + * @param array $filters An Array of modifers + * @return array + */ +function acf_enable_filters( $filters = array() ) { + + // Get state. + $prev_state = acf_get_filters(); + + // Allow specific filters to be enabled. + if( $filters ) { + acf_set_filters( $filters ); + + // Set all modifers as true. + } else { + acf_set_filters( array_map('__return_true', $prev_state) ); + } + + // Return prev state. + return $prev_state; +} diff --git a/includes/acf-hook-functions.php b/includes/acf-hook-functions.php new file mode 100644 index 0000000..80fc42d --- /dev/null +++ b/includes/acf-hook-functions.php @@ -0,0 +1,220 @@ +set( $filter, array( + 'type' => 'filter', + 'variations' => $variations, + 'index' => $index, + )); + + // Add generic handler. + // Use a priotiry of 10, and accepted args of 10 (ignored by WP). + add_filter( $filter, '_acf_apply_hook_variations', 10, 10 ); +} + +/** + * acf_add_action_variations + * + * Registers variations for the given action. + * + * @date 26/1/19 + * @since 5.7.11 + * + * @param string $action The action name. + * @param array $variations An array variation keys. + * @param int $index The param index to find variation values. + * @return void + */ +function acf_add_action_variations( $action = '', $variations = array(), $index = 0 ) { + + // Store replacement data. + acf_get_store('hook-variations')->set( $action, array( + 'type' => 'action', + 'variations' => $variations, + 'index' => $index, + )); + + // Add generic handler. + // Use a priotiry of 10, and accepted args of 10 (ignored by WP). + add_action( $action, '_acf_apply_hook_variations', 10, 10 ); +} + +/** + * _acf_apply_hook_variations + * + * Applys hook variations during apply_filters() or do_action(). + * + * @date 25/1/19 + * @since 5.7.11 + * + * @param mixed + * @return mixed + */ +function _acf_apply_hook_variations() { + + // Get current filter. + $filter = current_filter(); + + // Get args provided. + $args = func_get_args(); + + // Get variation information. + $variations = acf_get_store('hook-variations')->get( $filter ); + extract( $variations ); + + // Find field in args using index. + $field = $args[ $index ]; + + // Loop over variations and apply filters. + foreach( $variations as $variation ) { + + // Get value from field. + // First look for "backup" value ("_name", "_key"). + if( isset($field[ "_$variation" ]) ) { + $value = $field[ "_$variation" ]; + } elseif( isset($field[ $variation ]) ) { + $value = $field[ $variation ]; + } else { + continue; + } + + // Apply filters. + if( $type === 'filter' ) { + $args[0] = apply_filters_ref_array( "$filter/$variation=$value", $args ); + + // Or do action. + } else { + do_action_ref_array( "$filter/$variation=$value", $args ); + } + } + + // Return first arg. + return $args[0]; +} + +// Register store. +acf_register_store( 'deprecated-hooks' ); + +/** + * acf_add_deprecated_filter + * + * Registers a deprecated filter to run during the replacement. + * + * @date 25/1/19 + * @since 5.7.11 + * + * @param string $deprecated The deprecated hook. + * @param string $version The version this hook was deprecated. + * @param string $replacement The replacement hook. + * @return void + */ +function acf_add_deprecated_filter( $deprecated, $version, $replacement ) { + + // Store replacement data. + acf_get_store('deprecated-hooks')->append(array( + 'type' => 'filter', + 'deprecated' => $deprecated, + 'replacement' => $replacement, + 'version' => $version + )); + + // Add generic handler. + // Use a priotiry of 10, and accepted args of 10 (ignored by WP). + add_filter( $replacement, '_acf_apply_deprecated_hook', 10, 10 ); +} + +/** + * acf_add_deprecated_action + * + * Registers a deprecated action to run during the replacement. + * + * @date 25/1/19 + * @since 5.7.11 + * + * @param string $deprecated The deprecated hook. + * @param string $version The version this hook was deprecated. + * @param string $replacement The replacement hook. + * @return void + */ +function acf_add_deprecated_action( $deprecated, $version, $replacement ) { + + // Store replacement data. + acf_get_store('deprecated-hooks')->append(array( + 'type' => 'action', + 'deprecated' => $deprecated, + 'replacement' => $replacement, + 'version' => $version + )); + + // Add generic handler. + // Use a priotiry of 10, and accepted args of 10 (ignored by WP). + add_filter( $replacement, '_acf_apply_deprecated_hook', 10, 10 ); +} + +/** + * _acf_apply_deprecated_hook + * + * Applys a deprecated filter during apply_filters() or do_action(). + * + * @date 25/1/19 + * @since 5.7.11 + * + * @param mixed + * @return mixed + */ +function _acf_apply_deprecated_hook() { + + // Get current hook. + $hook = current_filter(); + + // Get args provided. + $args = func_get_args(); + + // Get deprecated items for this hook. + $items = acf_get_store('deprecated-hooks')->query(array( 'replacement' => $hook )); + + // Loop over results. + foreach( $items as $item ) { + + // Extract data. + extract( $item ); + + // Check if anyone is hooked into this deprecated hook. + if( has_filter($deprecated) ) { + + // Log warning. + _deprecated_hook( $deprecated, $version, $hook ); + + // Apply filters. + if( $type === 'filter' ) { + $args[0] = apply_filters_ref_array( $deprecated, $args ); + + // Or do action. + } else { + do_action_ref_array( $deprecated, $args ); + } + } + } + + // Return first arg. + return $args[0]; +} + diff --git a/includes/acf-meta-functions.php b/includes/acf-meta-functions.php new file mode 100644 index 0000000..cfe7b00 --- /dev/null +++ b/includes/acf-meta-functions.php @@ -0,0 +1,439 @@ + 'post', + 'id' => 0 + ); + + // Check if is numeric. + if( is_numeric($post_id) ) { + $data['id'] = (int) $post_id; + + // Check if is string. + } elseif( is_string($post_id) ) { + + // Determine "{$type}_{$id}" from string. + $bits = explode( '_', $post_id ); + $id = array_pop( $bits ); + $type = implode( '_', $bits ); + + // Check if is meta type. + if( function_exists("get_{$type}_meta") && is_numeric($id) ) { + $data['type'] = $type; + $data['id'] = (int) $id; + + // Check if is taxonomy name. + } elseif( taxonomy_exists($type) && is_numeric($id) ) { + $data['type'] = 'term'; + $data['id'] = (int) $id; + + // Otherwise, default to option. + } else { + $data['type'] = 'option'; + $data['id'] = $post_id; + } + } + + /** + * Filters the $data array after it has been decoded. + * + * @date 12/02/2014 + * @since 5.0.0 + * + * @param array $data The type and id array. + */ + return apply_filters( "acf/decode_post_id", $data, $post_id ); +} + +/** + * acf_get_meta + * + * Returns an array of "ACF only" meta for the given post_id. + * + * @date 9/10/18 + * @since 5.8.0 + * + * @param mixed $post_id The post_id for this data. + * @return array + */ +function acf_get_meta( $post_id = 0 ) { + + // Allow filter to short-circuit load_value logic. + $pre = apply_filters( "acf/pre_load_meta", null, $post_id ); + if( $pre !== null ) { + return $pre; + } + + // Decode $post_id for $type and $id. + extract( acf_decode_post_id($post_id) ); + + // Use get_$type_meta() function when possible. + if( function_exists("get_{$type}_meta") ) { + $allmeta = call_user_func("get_{$type}_meta", $id, ''); + + // Default to wp_options. + } else { + $allmeta = acf_get_option_meta( $id ); + } + + // Loop over meta and check that a reference exists for each value. + $meta = array(); + foreach( $allmeta as $key => $value ) { + + // If a reference exists for this value, add it to the meta array. + if( isset($allmeta["_$key"]) ) { + $meta[ $key ] = $allmeta[ $key ][0]; + $meta[ "_$key" ] = $allmeta[ "_$key" ][0]; + } + } + + // Unserialized results (get_metadata does not unserialize if $key is empty). + $meta = array_map('maybe_unserialize', $meta); + + /** + * Filters the $meta array after it has been loaded. + * + * @date 25/1/19 + * @since 5.7.11 + * + * @param array $meta The arary of loaded meta. + * @param string $post_id The $post_id for this meta. + */ + return apply_filters( "acf/load_meta", $meta, $post_id ); +} + + +/** + * acf_get_option_meta + * + * Returns an array of meta for the given wp_option name prefix in the same format as get_post_meta(). + * + * @date 9/10/18 + * @since 5.8.0 + * + * @param string $prefix The wp_option name prefix. + * @return array + */ +function acf_get_option_meta( $prefix = '' ) { + + // Globals. + global $wpdb; + + // Vars. + $meta = array(); + $search = "{$prefix}_%"; + $_search = "_{$prefix}_%"; + + // Escape underscores for LIKE. + $search = str_replace('_', '\_', $search); + $_search = str_replace('_', '\_', $_search); + + // Query database for results. + $rows = $wpdb->get_results($wpdb->prepare( + "SELECT * + FROM $wpdb->options + WHERE option_name LIKE %s + OR option_name LIKE %s", + $search, + $_search + ), ARRAY_A); + + // Loop over results and append meta (removing the $prefix from the option name). + $len = strlen("{$prefix}_"); + foreach( $rows as $row ) { + $meta[ substr($row['option_name'], $len) ][] = $row['option_value']; + } + + // Return results. + return $meta; +} + +/** + * acf_get_metadata + * + * Retrieves specific metadata from the database. + * + * @date 16/10/2015 + * @since 5.2.3 + * + * @param (int|string) $post_id The post id. + * @param string $name The meta name. + * @param bool $hidden If the meta is hidden (starts with an underscore). + * @return mixed + */ +function acf_get_metadata( $post_id = 0, $name = '', $hidden = false ) { + + // Allow filter to short-circuit logic. + $pre = apply_filters( "acf/pre_load_metadata", null, $post_id, $name, $hidden ); + if( $pre !== null ) { + return $pre; + } + + // Decode $post_id for $type and $id. + extract( acf_decode_post_id($post_id) ); + + // Hidden meta uses an underscore prefix. + $prefix = $hidden ? '_' : ''; + + // Bail early if no $id (possible during new acf_form). + if( !$id ) { + return null; + } + + // Check option. + if( $type === 'option' ) { + return get_option( "{$prefix}{$id}_{$name}", null ); + + // Check meta. + } else { + $meta = get_metadata( $type, $id, "{$prefix}{$name}", false ); + return isset($meta[0]) ? $meta[0] : null; + } +} + +/** + * acf_update_metadata + * + * Updates metadata in the database. + * + * @date 16/10/2015 + * @since 5.2.3 + * + * @param (int|string) $post_id The post id. + * @param string $name The meta name. + * @param mixed $value The meta value. + * @param bool $hidden If the meta is hidden (starts with an underscore). + * @return (int|bool) Meta ID if the key didn't exist, true on successful update, false on failure. + */ +function acf_update_metadata( $post_id = 0, $name = '', $value = '', $hidden = false ) { + + // Allow filter to short-circuit logic. + $pre = apply_filters( "acf/pre_update_metadata", null, $post_id, $name, $value, $hidden ); + if( $pre !== null ) { + return $pre; + } + + // Decode $post_id for $type and $id. + extract( acf_decode_post_id($post_id) ); + + // Hidden meta uses an underscore prefix. + $prefix = $hidden ? '_' : ''; + + // Bail early if no $id (possible during new acf_form). + if( !$id ) { + return false; + } + + // Update option. + if( $type === 'option' ) { + + // Unslash value to match update_metadata() functionality. + $value = wp_unslash( $value ); + $autoload = (bool) acf_get_setting('autoload'); + return update_option( "{$prefix}{$id}_{$name}", $value, $autoload ); + + // Update meta. + } else { + return update_metadata( $type, $id, "{$prefix}{$name}", $value ); + } +} + +/** + * acf_delete_metadata + * + * Deletes metadata from the database. + * + * @date 16/10/2015 + * @since 5.2.3 + * + * @param (int|string) $post_id The post id. + * @param string $name The meta name. + * @param bool $hidden If the meta is hidden (starts with an underscore). + * @return bool + */ +function acf_delete_metadata( $post_id = 0, $name = '', $hidden = false ) { + + // Allow filter to short-circuit logic. + $pre = apply_filters( "acf/pre_delete_metadata", null, $post_id, $name, $hidden ); + if( $pre !== null ) { + return $pre; + } + + // Decode $post_id for $type and $id. + extract( acf_decode_post_id($post_id) ); + + // Hidden meta uses an underscore prefix. + $prefix = $hidden ? '_' : ''; + + // Bail early if no $id (possible during new acf_form). + if( !$id ) { + return false; + } + + // Update option. + if( $type === 'option' ) { + $autoload = (bool) acf_get_setting('autoload'); + return delete_option( "{$prefix}{$id}_{$name}" ); + + // Update meta. + } else { + return delete_metadata( $type, $id, "{$prefix}{$name}" ); + } +} + +/** + * acf_copy_postmeta + * + * Copies meta from one post to another. Useful for saving and restoring revisions. + * + * @date 25/06/2016 + * @since 5.3.8 + * + * @param (int|string) $from_post_id The post id to copy from. + * @param (int|string) $to_post_id The post id to paste to. + * @return void + */ +function acf_copy_metadata( $from_post_id = 0, $to_post_id = 0 ) { + + // Get all postmeta. + $meta = acf_get_meta( $from_post_id ); + + // Check meta. + if( $meta ) { + + // Slash data. WP expects all data to be slashed and will unslash it (fixes '\' character issues). + $meta = wp_slash( $meta ); + + // Loop over meta. + foreach( $meta as $name => $value ) { + acf_update_metadata( $to_post_id, $name, $value ); + } + } +} + +/** + * acf_copy_postmeta + * + * Copies meta from one post to another. Useful for saving and restoring revisions. + * + * @date 25/06/2016 + * @since 5.3.8 + * @deprecated 5.7.11 + * + * @param int $from_post_id The post id to copy from. + * @param int $to_post_id The post id to paste to. + * @return void + */ +function acf_copy_postmeta( $from_post_id = 0, $to_post_id = 0 ) { + return acf_copy_metadata( $from_post_id, $to_post_id ); +} + +/** + * acf_get_meta_field + * + * Returns a field using the provided $id and $post_id parameters. + * Looks for a reference to help loading the correct field via name. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param string $key The meta name (field name). + * @param (int|string) $post_id The post_id where this field's value is saved. + * @return (array|false) The field array. + */ +function acf_get_meta_field( $key = 0, $post_id = 0 ) { + + // Try reference. + $field_key = acf_get_reference( $key, $post_id ); + + if( $field_key ) { + $field = acf_get_field( $field_key ); + if( $field ) { + $field['name'] = $key; + return $field; + } + } + + // Return false. + return false; +} + +/** + * acf_get_metaref + * + * Retrieves reference metadata from the database. + * + * @date 16/10/2015 + * @since 5.2.3 + * + * @param (int|string) $post_id The post id. + * @param string type The reference type (fields|groups). + * @param string $name An optional specific name + * @return mixed + */ +function acf_get_metaref( $post_id = 0, $type = 'fields', $name = '' ) { + + // Load existing meta. + $meta = acf_get_metadata( $post_id, "_acf_$type" ); + + // Handle no meta. + if( !$meta ) { + return $name ? '' : array(); + } + + // Return specific reference. + if( $name ) { + return isset($meta[ $name ]) ? $meta[ $name ] : ''; + + // Or return all references. + } else { + return $meta; + } +} + +/** + * acf_update_metaref + * + * Updates reference metadata in the database. + * + * @date 16/10/2015 + * @since 5.2.3 + * + * @param (int|string) $post_id The post id. + * @param string type The reference type (fields|groups). + * @param array $references An array of references. + * @return (int|bool) Meta ID if the key didn't exist, true on successful update, false on failure. + */ +function acf_update_metaref( $post_id = 0, $type = 'fields', $references = array() ) { + + // Get current references. + $current = acf_get_metaref( $post_id, $type ); + + // Merge in new references. + $references = array_merge( $current, $references ); + + // Simplify groups + if( $type === 'groups' ) { + $references = array_values($references); + } + + // Remove duplicate references. + $references = array_unique($references); + + // Update metadata. + return acf_update_metadata( $post_id, "_acf_$type", $references ); +} diff --git a/includes/acf-post-functions.php b/includes/acf-post-functions.php new file mode 100644 index 0000000..06abc62 --- /dev/null +++ b/includes/acf-post-functions.php @@ -0,0 +1,34 @@ + templates data. + * + * @date 29/8/17 + * @since 5.6.2 + * + * @param void + * @return array + */ +function acf_get_post_templates() { + + // Defaults. + $post_templates = array( + 'page' => array() + ); + + // Loop over post types and append their templates. + if( method_exists('WP_Theme', 'get_page_templates') ) { + $post_types = acf_get_post_types(); + foreach( $post_types as $post_type ) { + $templates = wp_get_theme()->get_page_templates( null, $post_type ); + if( $templates ) { + $post_templates[ $post_type ] = $templates; + } + } + } + + // Return. + return $post_templates; +} \ No newline at end of file diff --git a/includes/acf-value-functions.php b/includes/acf-value-functions.php new file mode 100644 index 0000000..78576be --- /dev/null +++ b/includes/acf-value-functions.php @@ -0,0 +1,329 @@ +prop( 'multisite', true ); + +/** + * acf_get_reference + * + * Retrieves the field key for a given field name and post_id. + * + * @date 26/1/18 + * @since 5.6.5 + * + * @param string $field_name The name of the field. eg 'sub_heading'. + * @param mixed $post_id The post_id of which the value is saved against. + * @return string The field key. + */ +function acf_get_reference( $field_name, $post_id ) { + + // Allow filter to short-circuit load_value logic. + $reference = apply_filters( "acf/pre_load_reference", null, $field_name, $post_id ); + if( $reference !== null ) { + return $reference; + } + + // Get hidden meta for this field name. + $reference = acf_get_metadata( $post_id, $field_name, true ); + + /** + * Filters the reference value. + * + * @date 25/1/19 + * @since 5.7.11 + * + * @param string $reference The reference value. + * @param string $field_name The field name. + * @param (int|string) $post_id The post ID where meta is stored. + */ + return apply_filters( "acf/load_reference", $reference, $field_name, $post_id ); +} + +// Register deprecated. +acf_add_deprecated_filter( 'acf/get_field_reference', '5.6.5', 'acf/load_reference' ); + +/** + * acf_get_value + * + * Retrieves the value for a given field and post_id. + * + * @date 28/09/13 + * @since 5.0.0 + * + * @param (int|string) $post_id The post id. + * @param array $field The field array. + * @return mixed. + */ +function acf_get_value( $post_id = 0, $field ) { + + // Allow filter to short-circuit load_value logic. + $value = apply_filters( "acf/pre_load_value", null, $post_id, $field ); + if( $value !== null ) { + return $value; + } + + // Get field name. + $field_name = $field['name']; + + // Check store. + $store = acf_get_store( 'values' ); + if( $store->has( "$post_id:$field_name" ) ) { + return $store->get( "$post_id:$field_name" ); + } + + // Load value from database. + $value = acf_get_metadata( $post_id, $field_name ); + + // Use field's default_value if no meta was found. + if( $value === null && isset($field['default_value']) ) { + $value = $field['default_value']; + } + + /** + * Filters the $value after it has been loaded. + * + * @date 28/09/13 + * @since 5.0.0 + * + * @param mixed $value The value to preview. + * @param string $post_id The post ID for this value. + * @param array $field The field array. + */ + $value = apply_filters( "acf/load_value", $value, $post_id, $field ); + + // Update store. + $store->set( "$post_id:$field_name", $value ); + + // Return value. + return $value; +} + +// Register variation. +acf_add_filter_variations( 'acf/load_value', array('type', 'name', 'key'), 2 ); + +/** + * acf_format_value + * + * Returns a formatted version of the provided value. + * + * @date 28/09/13 + * @since 5.0.0 + * + * @param mixed $value The field value. + * @param (int|string) $post_id The post id. + * @param array $field The field array. + * @return mixed. + */ +function acf_format_value( $value, $post_id, $field ) { + + // Allow filter to short-circuit load_value logic. + $check = apply_filters( "acf/pre_format_value", null, $post_id, $field ); + if( $check !== null ) { + return $check; + } + + // Get field name. + $field_name = $field['name']; + + // Check store. + $store = acf_get_store( 'values' ); + if( $store->has( "$post_id:$field_name:formatted" ) ) { + return $store->get( "$post_id:$field_name:formatted" ); + } + + /** + * Filters the $value for use in a template function. + * + * @date 28/09/13 + * @since 5.0.0 + * + * @param mixed $value The value to preview. + * @param string $post_id The post ID for this value. + * @param array $field The field array. + */ + $value = apply_filters( "acf/format_value", $value, $post_id, $field ); + + // Update store. + $store->set( "$post_id:$field_name:formatted", $value ); + + // Return value. + return $value; +} + +// Register variation. +acf_add_filter_variations( 'acf/format_value', array('type', 'name', 'key'), 2 ); + +/** + * acf_update_value + * + * Updates the value for a given field and post_id. + * + * @date 28/09/13 + * @since 5.0.0 + * + * @param mixed $value The new value. + * @param (int|string) $post_id The post id. + * @param array $field The field array. + * @return bool. + */ +function acf_update_value( $value = null, $post_id = 0, $field ) { + + // Allow filter to short-circuit update_value logic. + $check = apply_filters( "acf/pre_update_value", null, $value, $post_id, $field ); + if( $check !== null ) { + return $check; + } + + /** + * Filters the $value before it is updated. + * + * @date 28/09/13 + * @since 5.0.0 + * + * @param mixed $value The value to update. + * @param string $post_id The post ID for this value. + * @param array $field The field array. + * @param mixed $original The original value before modification. + */ + $value = apply_filters( "acf/update_value", $value, $post_id, $field, $value ); + + // Allow null to delete value. + if( $value === null ) { + return acf_delete_value( $post_id, $field ); + } + + // Update meta. + $return = acf_update_metadata( $post_id, $field['name'], $value ); + + // Update reference. + acf_update_metadata( $post_id, $field['name'], $field['key'], true ); + + // Delete stored data. + acf_flush_value_cache( $post_id, $field['name'] ); + + // Return update status. + return $return; +} + +// Register variation. +acf_add_filter_variations( 'acf/update_value', array('type', 'name', 'key'), 2 ); + +/** + * acf_update_values + * + * Updates an array of values. + * + * @date 26/2/19 + * @since 5.7.13 + * + * @param array values The array of values. + * @param (int|string) $post_id The post id. + * @return void + */ +function acf_update_values( $values = array(), $post_id = 0 ) { + + // Loop over values. + foreach( $values as $key => $value ) { + + // Get field. + $field = acf_get_field( $key ); + + // Update value. + if( $field ) { + acf_update_value( $value, $post_id, $field ); + } + } +} + +/** + * acf_flush_value_cache + * + * Deletes all cached data for this value. + * + * @date 22/1/19 + * @since 5.7.10 + * + * @param (int|string) $post_id The post id. + * @param string $field_name The field name. + * @return void + */ +function acf_flush_value_cache( $post_id = 0, $field_name = '' ) { + + // Delete stored data. + acf_get_store( 'values' ) + ->remove( "$post_id:$field_name" ) + ->remove( "$post_id:$field_name:formatted" ); +} + +/** + * acf_delete_value + * + * Deletes the value for a given field and post_id. + * + * @date 28/09/13 + * @since 5.0.0 + * + * @param (int|string) $post_id The post id. + * @param array $field The field array. + * @return bool. + */ +function acf_delete_value( $post_id, $field ) { + + /** + * Fires before a value is deleted. + * + * @date 28/09/13 + * @since 5.0.0 + * + * @param string $post_id The post ID for this value. + * @param mixed $name The meta name. + * @param array $field The field array. + */ + do_action( "acf/delete_value", $post_id, $field['name'], $field ); + + // Delete meta. + $return = acf_delete_metadata( $post_id, $field['name'] ); + + // Delete reference. + acf_delete_metadata( $post_id, $field['name'], true ); + + // Delete stored data. + acf_flush_value_cache( $post_id, $field['name'] ); + + // Return delete status. + return $return; +} + +// Register variation. +acf_add_filter_variations( 'acf/delete_value', array('type', 'name', 'key'), 2 ); + +/** + * acf_preview_value + * + * Return a human friendly 'preview' for a given field value. + * + * @date 28/09/13 + * @since 5.0.0 + * + * @param mixed $value The new value. + * @param (int|string) $post_id The post id. + * @param array $field The field array. + * @return bool. + */ +function acf_preview_value( $value, $post_id, $field ) { + + /** + * Filters the $value before used in HTML. + * + * @date 24/10/16 + * @since 5.5.0 + * + * @param mixed $value The value to preview. + * @param string $post_id The post ID for this value. + * @param array $field The field array. + */ + return apply_filters( "acf/preview_value", $value, $post_id, $field ); +} + +// Register variation. +acf_add_filter_variations( 'acf/preview_value', array('type', 'name', 'key'), 2 ); diff --git a/includes/admin/admin-field-group.php b/includes/admin/admin-field-group.php index 9a6cc91..99e5048 100644 --- a/includes/admin/admin-field-group.php +++ b/includes/admin/admin-field-group.php @@ -213,7 +213,7 @@ class acf_admin_field_group { // set global var - $field_group = acf_get_field_group( $post ); + $field_group = acf_get_field_group( $post->ID ); // metaboxes @@ -557,7 +557,7 @@ class acf_admin_field_group { // get fields $view = array( - 'fields' => acf_get_fields_by_id( $field_group['ID'] ), + 'fields' => acf_get_fields( $field_group ), 'parent' => 0 ); diff --git a/includes/admin/admin-field-groups.php b/includes/admin/admin-field-groups.php index a6321b1..f9ac7d7 100644 --- a/includes/admin/admin-field-groups.php +++ b/includes/admin/admin-field-groups.php @@ -270,22 +270,22 @@ class acf_admin_field_groups { $modified = acf_maybe_get($group, 'modified', 0); $private = acf_maybe_get($group, 'private', false); + // Ignore if is private. + if( $private ) { + continue; - // ignore DB / PHP / private field groups - if( $local !== 'json' || $private ) { - - // do nothing - + // Ignore not local "json". + } elseif( $local !== 'json' ) { + continue; + + // Append to sync if not yet in database. } elseif( !$group['ID'] ) { - $this->sync[ $group['key'] ] = $group; - + + // Append to sync if "json" modified time is newer than database. } elseif( $modified && $modified > get_post_modified_time('U', true, $group['ID'], true) ) { - $this->sync[ $group['key'] ] = $group; - } - } @@ -334,21 +334,22 @@ class acf_admin_field_groups { // loop foreach( $sync_keys as $key ) { - // append fields - if( acf_have_local_fields($key) ) { - - $this->sync[ $key ]['fields'] = acf_get_local_fields( $key ); - + // Bail early if not found. + if( !isset($this->sync[ $key ]) ) { + continue; } + // Get field group. + $field_group = $this->sync[ $key ]; - // import - $field_group = acf_import_field_group( $this->sync[ $key ] ); + // Append fields. + $field_group['fields'] = acf_get_fields( $field_group ); + + // Import field group. + $field_group = acf_import_field_group( $field_group ); - - // append + // Append imported ID. $new_ids[] = $field_group['ID']; - } @@ -614,7 +615,6 @@ class acf_admin_field_groups { // vars $url_home = 'https://www.advancedcustomfields.com'; - $url_support = 'https://support.advancedcustomfields.com'; $icon = ''; ?> @@ -637,7 +637,7 @@ class acf_admin_field_groups {' . acf_esc_html($instructions) . '
'; - } - -} - - -/* depreciated since 5.6.5 */ -function acf_render_field_wrap_description( $field ) { - acf_render_field_instructions( $field ); -} - - -/* -* acf_render_field_setting -* -* This function will render a tr element containing a label and field cell, but also setting the tr data attribute for AJAX -* -* @type function -* @date 28/09/13 -* @since 5.0.0 -* -* @param $field (array) the origional field being edited -* @param $setting (array) the settings field to create -* @return n/a -*/ - -function acf_render_field_setting( $field, $setting, $global = false ) { - - // validate - $setting = acf_get_valid_field( $setting ); - - // custom key and class - $setting['wrapper']['data-key'] = $setting['name']; - $setting['wrapper']['class'] .= ' acf-field-setting-' . $setting['name']; - - // context - if( !$global ) { - $setting['wrapper']['data-setting'] = $field['type']; - } - - // copy across prefix - $setting['prefix'] = $field['prefix']; - - // attempt find value - if( $setting['value'] === null ) { - - // name - if( isset($field[ $setting['name'] ]) ) { - $setting['value'] = $field[ $setting['name'] ]; - - // default - } elseif( isset($setting['default_value']) ) { - $setting['value'] = $setting['default_value']; - } - } - - // append (used by JS to join settings) - if( isset($setting['_append']) ) { - $setting['wrapper']['data-append'] = $setting['_append']; - } - - // render - acf_render_field_wrap( $setting, 'tr', 'label' ); -} - - -/* -* acf_get_fields -* -* This function will return an array of fields for the given $parent -* -* @type function -* @date 30/09/13 -* @since 5.0.0 -* -* @param $parent (array) a field or field group -* @return (array) -*/ - -function acf_get_fields( $parent = false ) { - - // allow $parent to be a field group ID - if( !is_array($parent) ) { - - $parent = acf_get_field_group( $parent ); - - } - - - // bail early if no parent - if( !$parent ) return false; - - - // vars - $fields = array(); - - - // try JSON before DB to save query time - if( acf_have_local_fields( $parent['key'] ) ) { - - $fields = acf_get_local_fields( $parent['key'] ); - - } else { - - $fields = acf_get_fields_by_id( $parent['ID'] ); - - } - - - // filter - $fields = apply_filters('acf/load_fields', $fields, $parent); - $fields = apply_filters('acf/get_fields', $fields, $parent); - - - // return - return $fields; - -} - - -/* -* acf_get_fields_by_id -* -* This function will get all fields for the given parent -* -* @type function -* @date 27/02/2014 -* @since 5.0.0 -* -* @param $post_id (int) -* @return $fields (array) -*/ - -function acf_get_fields_by_id( $parent_id = 0 ) { - - // bail early if no ID - if( !$parent_id ) return false; - - - // vars - $fields = array(); - $post_ids = array(); - $cache_key = "get_fields/ID={$parent_id}"; - - - // check cache for child ids - if( acf_isset_cache($cache_key) ) { - - $post_ids = acf_get_cache($cache_key); - - // query DB for child ids - } else { - - // query - $posts = get_posts(array( - 'posts_per_page' => -1, - 'post_type' => 'acf-field', - 'orderby' => 'menu_order', - 'order' => 'ASC', - 'suppress_filters' => true, // DO NOT allow WPML to modify the query - 'post_parent' => $parent_id, - 'post_status' => 'publish, trash', // 'any' won't get trashed fields - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false - )); - - - // loop - if( $posts ) { - - foreach( $posts as $post ) { - - $post_ids[] = $post->ID; - - } - - } - - - // update cache - acf_set_cache($cache_key, $post_ids); - - } - - - // bail early if no children - if( empty($post_ids) ) return false; - - - // load fields - foreach( $post_ids as $post_id ) { - - $fields[] = acf_get_field( $post_id ); - - } - - - // return - return $fields; - -} - - -/* -* acf_get_field -* -* This function will return a field for the given selector. -* -* @type function -* @date 30/09/13 -* @since 5.0.0 -* -* @param $selector (mixed) identifyer of field. Can be an ID, key, name or post object -* @param $db_only (boolean) return $field in it's raw form without filters or cache -* @return $field (array) -*/ - -function acf_get_field( $selector = null, $db_only = false ) { - - // vars - $field = false; - $type = 'ID'; - - - // ID - if( is_numeric($selector) ) { - - // do nothing - - // object - } elseif( is_object($selector) ) { - - $selector = $selector->ID; - - // string - } elseif( is_string($selector) ) { - - $type = acf_is_field_key($selector) ? 'key' : 'name'; - - // other - } else { - - return false; - - } - - - // return early if cache is found - $cache_key = "get_field/{$type}={$selector}"; - - if( !$db_only && acf_isset_cache($cache_key) ) { - - return acf_get_cache($cache_key); - - } - - - // ID - if( $type == 'ID' ) { - - $field = _acf_get_field_by_id( $selector, $db_only ); - - // key - } elseif( $type == 'key' ) { - - $field = _acf_get_field_by_key( $selector, $db_only ); - - // name (rare case) - } else { - - $field = _acf_get_field_by_name( $selector, $db_only ); - - } - - - // bail early if no field - if( !$field ) return false; - - - // validate - $field = acf_get_valid_field( $field ); - - - // set prefix (acf fields save with prefix 'acf') - $field['prefix'] = 'acf'; - - - // bail early if db only value (no need to update cache) - if( $db_only ) return $field; - - - /** - * Filters the $field array after it has been loaded. - * - * @date 12/02/2014 - * @since 5.0.0 - * - * @param array $field The field array. - */ - $field = apply_filters( "acf/load_field/type={$field['type']}", $field ); - $field = apply_filters( "acf/load_field/name={$field['_name']}", $field ); - $field = apply_filters( "acf/load_field/key={$field['key']}", $field ); - $field = apply_filters( "acf/load_field", $field ); - - - // update cache - // - Use key instead of ID for best compatibility (not all fields exist in the DB) - $cache_key = acf_set_cache("get_field/key={$field['key']}", $field); - - - // update cache reference - // - allow cache to return if using an ID selector - acf_set_cache_reference("get_field/ID={$field['ID']}", $cache_key); - - - // return - return $field; - -} - - -/* -* _acf_get_field_by_id -* -* This function will get a field via its ID -* -* @type function -* @date 27/02/2014 -* @since 5.0.0 -* -* @param $post_id (int) -* @return $field (array) -*/ - -function _acf_get_field_by_id( $post_id = 0, $db_only = false ) { - - // get post - $post = get_post( $post_id ); - - - // bail early if no post, or is not a field - if( empty($post) || $post->post_type != 'acf-field' ) return false; - - - // unserialize - $field = maybe_unserialize( $post->post_content ); - - - // update attributes - $field['ID'] = $post->ID; - $field['key'] = $post->post_name; - $field['label'] = $post->post_title; - $field['name'] = $post->post_excerpt; - $field['menu_order'] = $post->menu_order; - $field['parent'] = $post->post_parent; - - - // override with JSON - if( !$db_only && acf_is_local_field($field['key']) ) { - - // load JSON field - $local = acf_get_local_field( $field['key'] ); - - - // override IDs - $local['ID'] = $field['ID']; - $local['parent'] = $field['parent']; - - - // return - return $local; - - } - - - // return - return $field; - -} - - -/* -* _acf_get_field_by_key -* -* This function will get a field via its key -* -* @type function -* @date 27/02/2014 -* @since 5.0.0 -* -* @param $key (string) -* @return $field (array) -*/ - -function _acf_get_field_by_key( $key = '', $db_only = false ) { - - // try JSON before DB to save query time - if( !$db_only && acf_is_local_field( $key ) ) { - - return acf_get_local_field( $key ); - - } - - - // vars - $post_id = acf_get_field_id( $key ); - - - // bail early if no post_id - if( !$post_id ) return false; - - - // return - return _acf_get_field_by_id( $post_id, $db_only ); - -} - - -/* -* _acf_get_field_by_name -* -* This function will get a field via its name -* -* @type function -* @date 27/02/2014 -* @since 5.0.0 -* -* @param $key (string) -* @return $field (array) -*/ - -function _acf_get_field_by_name( $name = '', $db_only = false ) { - - // try JSON before DB to save query time - if( !$db_only && acf_is_local_field( $name ) ) { - - return acf_get_local_field( $name ); - - } - - - // vars - $args = array( - 'posts_per_page' => 1, - 'post_type' => 'acf-field', - 'orderby' => 'menu_order title', - 'order' => 'ASC', - 'suppress_filters' => false, - 'acf_field_name' => $name, - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false - ); - - - // load posts - $posts = get_posts( $args ); - - - // bail early if no posts - if( empty($posts) ) return false; - - - // return - return _acf_get_field_by_id( $posts[0]->ID, $db_only ); - -} - - -/* -* acf_maybe_get_field -* -* This function will return a field for the given selector. -* It will also review the field_reference to ensure the correct field is returned which makes it useful for the template API -* -* @type function -* @date 4/08/2015 -* @since 5.2.3 -* -* @param $selector (mixed) identifyer of field. Can be an ID, key, name or post object -* @param $post_id (mixed) the post_id of which the value is saved against -* @param $strict (boolean) if true, return a field only when a field key is found. -* @return $field (array) -*/ - -function acf_maybe_get_field( $selector, $post_id = false, $strict = true ) { - - // init - acf_init(); - - - // bail early if is field key - if( acf_is_field_key($selector) ) { - - return acf_get_field( $selector ); - - } - - - // save selector as field_name (could be sub field name 'images_0_image') - $field_name = $selector; - - - // get valid post_id - $post_id = acf_get_valid_post_id( $post_id ); - - - // get reference - $field_key = acf_get_reference( $selector, $post_id ); - - - // update selector - if( $field_key ) { - - $selector = $field_key; - - // bail early if no reference - } elseif( $strict ) { - - return false; - - } - - - // get field - $field = acf_get_field( $selector ); - - - // update name - if( $field ) $field['name'] = $field_name; - - - // return - return $field; - -} - - -/* -* acf_get_field_id -* -* This function will lookup a field's ID from the DB -* Useful for local fields to find DB sibling -* -* @type function -* @date 25/06/2015 -* @since 5.2.3 -* -* @param $key (string) -* @return $post_id (int) -*/ - -function acf_get_field_id( $key = '' ) { - - // vars - $args = array( - 'posts_per_page' => 1, - 'post_type' => 'acf-field', - 'orderby' => 'menu_order title', - 'order' => 'ASC', - 'suppress_filters' => false, - 'acf_field_key' => $key, - 'update_post_meta_cache' => false, - 'update_post_term_cache' => false - ); - - - // load posts - $posts = get_posts( $args ); - - - // validate - if( empty($posts) ) return 0; - - - // return - return $posts[0]->ID; - -} - - -/* -* acf_update_field -* -* This function will update a field into the DB. -* The returned field will always contain an ID -* -* @type function -* @date 1/10/13 -* @since 5.0.0 -* -* @param $field (array) -* @return $field (array) -*/ - -function acf_update_field( $field = false, $specific = false ) { - - // $field must be an array - if( !is_array($field) ) return false; - - - // validate - $field = acf_get_valid_field( $field ); - - - // may have been posted. Remove slashes - $field = wp_unslash( $field ); - - - // parse types (converts string '0' to int 0) - $field = acf_parse_types( $field ); - - - // clean up conditional logic keys - if( !empty($field['conditional_logic']) ) { - - // extract groups - $groups = acf_extract_var( $field, 'conditional_logic' ); - - - // clean array - $groups = array_filter($groups); - $groups = array_values($groups); - - - // clean rules - foreach( array_keys($groups) as $i ) { - - $groups[ $i ] = array_filter($groups[ $i ]); - $groups[ $i ] = array_values($groups[ $i ]); - - } - - - // reset conditional logic - $field['conditional_logic'] = $groups; - - } - - - // parent may be a field key - // - lookup parent ID - if( acf_is_field_key($field['parent']) ) { - - $field['parent'] = acf_get_field_id( $field['parent'] ); - - } - - - /** - * Filters the $field array before it is updated. - * - * @date 12/02/2014 - * @since 5.0.0 - * - * @param array $field The field array. - */ - $field = apply_filters( "acf/update_field/type={$field['type']}", $field ); - $field = apply_filters( "acf/update_field/name={$field['_name']}", $field ); - $field = apply_filters( "acf/update_field/key={$field['key']}", $field ); - $field = apply_filters( "acf/update_field", $field ); - - - // store origional field for return - $data = $field; - - - // extract some args - $extract = acf_extract_vars($data, array( - 'ID', - 'key', - 'label', - 'name', - 'prefix', - 'value', - 'menu_order', - 'id', - 'class', - 'parent', - '_name', - '_prepare', - '_valid', - )); - - - // serialize for DB - $data = maybe_serialize( $data ); - - - // save - $save = array( - 'ID' => $extract['ID'], - 'post_status' => 'publish', - 'post_type' => 'acf-field', - 'post_title' => $extract['label'], - 'post_name' => $extract['key'], - 'post_excerpt' => $extract['name'], - 'post_content' => $data, - 'post_parent' => $extract['parent'], - 'menu_order' => $extract['menu_order'], - ); - - - // specific - if( acf_is_array($specific) ) { - - // append ID - $specific[] = 'ID'; - - - // get sub array - $save = acf_get_sub_array( $save, $specific ); - - } - - - // allow fields to contain the same name - add_filter( 'wp_unique_post_slug', 'acf_update_field_wp_unique_post_slug', 999, 6 ); - - - // slash data - // - WP expects all data to be slashed and will unslash it (fixes '\' character issues) - $save = wp_slash( $save ); - - - // update the field and update the ID - if( $field['ID'] ) { - - wp_update_post( $save ); - - } else { - - $field['ID'] = wp_insert_post( $save ); - - } - - - // clear cache - acf_delete_cache("get_field/key={$field['key']}"); - - - // return - return $field; - -} - -function acf_update_field_wp_unique_post_slug( $slug, $post_ID, $post_status, $post_type, $post_parent, $original_slug ) { - - if( $post_type == 'acf-field' ) { - - $slug = $original_slug; - - } - - // return - return $slug; - -} - - -/* -* acf_duplicate_fields -* -* This function will duplicate an array of fields and update conditional logic references -* -* @type function -* @date 16/06/2014 -* @since 5.0.0 -* -* @param $fields (array) -* @param $new_parent (int) -* @return n/a -*/ - -function acf_duplicate_fields( $fields, $new_parent = 0 ) { - - // bail early if no fields - if( empty($fields) ) return; - - - // create new field keys (for conditional logic fixes) - foreach( $fields as $field ) { - - // ensure a delay for unique ID - usleep(1); - - acf_update_setting( 'duplicate_key_' . $field['key'] , uniqid('field_') ); - - } - - - // duplicate fields - foreach( $fields as $field ) { - - // duplicate - acf_duplicate_field( $field['ID'], $new_parent ); - - } - -} - - -/* -* acf_duplicate_field -* -* This function will duplicate a field and attach it to the given field group ID -* -* @type function -* @date 17/10/13 -* @since 5.0.0 -* -* @param $selector (int) -* @param $new_parent (int) -* @return $field (array) the new field -*/ - -function acf_duplicate_field( $selector = 0, $new_parent = 0 ){ - - // disable filters to ensure ACF loads raw data from DB - acf_disable_filters(); - - - // load the origional field - $field = acf_get_field( $selector ); - - - // bail early if field did not load correctly - if( empty($field) ) { - - return false; - - } - - - // update ID - $field['ID'] = false; - - - // try duplicate keys - $field['key'] = acf_get_setting( 'duplicate_key_' . $field['key'] ); - - - // default key - if( empty($field['key']) ) { - - $field['key'] = uniqid('field_'); - - } - - - // update parent - if( $new_parent ) { - - $field['parent'] = $new_parent; - - } - - - // update conditional logic references (because field keys have changed) - if( !empty($field['conditional_logic']) ) { - - // extract groups - $groups = acf_extract_var( $field, 'conditional_logic' ); - - - // loop over groups - foreach( array_keys($groups) as $g ) { - - // extract group - $group = acf_extract_var( $groups, $g ); - - - // bail early if empty - if( empty($group) ) { - - continue; - - } - - - // loop over rules - foreach( array_keys($group) as $r ) { - - // extract rule - $rule = acf_extract_var( $group, $r ); - - - // vars - $new_key = acf_get_setting( 'duplicate_key_' . $rule['field'] ); - - - // update rule with new key - if( $new_key ) { - - $rule['field'] = $new_key; - - } - - - // append to group - $group[ $r ] = $rule; - - } - - - // append to groups - $groups[ $g ] = $group; - - } - - - // update conditional logic - $field['conditional_logic'] = $groups; - - - } - - - /** - * Filters the $field array after it has been duplicated. - * - * @date 12/02/2014 - * @since 5.0.0 - * - * @param array $field The field array. - */ - $field = apply_filters( "acf/duplicate_field/type={$field['type']}", $field ); - $field = apply_filters( "acf/duplicate_field", $field); - - - // save - return acf_update_field( $field ); - -} - - -/* -* acf_delete_field -* -* This function will delete a field from the databse -* -* @type function -* @date 2/10/13 -* @since 5.0.0 -* -* @param $id (int) -* @return (boolean) -*/ - -function acf_delete_field( $selector = 0 ) { - - // disable filters to ensure ACF loads raw data from DB - acf_disable_filters(); - - - // load the origional field gorup - $field = acf_get_field( $selector ); - - - // bail early if field did not load correctly - if( empty($field) ) return false; - - - // delete field - wp_delete_post( $field['ID'], true ); - - - /** - * Fires immediately after a field has been deleted. - * - * @date 12/02/2014 - * @since 5.0.0 - * - * @param array $field The field array. - */ - do_action( "acf/delete_field/type={$field['type']}", $field ); - do_action( "acf/delete_field/name={$field['_name']}", $field ); - do_action( "acf/delete_field/key={$field['key']}", $field ); - do_action( "acf/delete_field", $field ); - - - // clear cache - acf_delete_cache("get_field/key={$field['key']}"); - - - // return - return true; - -} - - -/* -* acf_trash_field -* -* This function will trash a field from the databse -* -* @type function -* @date 2/10/13 -* @since 5.0.0 -* -* @param $id (int) -* @return (boolean) -*/ - -function acf_trash_field( $selector = 0 ) { - - // disable filters to ensure ACF loads raw data from DB - acf_disable_filters(); - - - // load the origional field gorup - $field = acf_get_field( $selector ); - - - // bail early if field did not load correctly - if( empty($field) ) return false; - - - // delete field - wp_trash_post( $field['ID'] ); - - - // action for 3rd party customisation - do_action( 'acf/trash_field', $field ); - - - // return - return true; - -} - - -/* -* acf_untrash_field -* -* This function will restore a field from the trash -* -* @type function -* @date 2/10/13 -* @since 5.0.0 -* -* @param $id (int) -* @return (boolean) -*/ - -function acf_untrash_field( $selector = 0 ) { - - // disable filters to ensure ACF loads raw data from DB - acf_disable_filters(); - - - // load the origional field gorup - $field = acf_get_field( $selector ); - - - // bail early if field did not load correctly - if( empty($field) ) return false; - - - // delete field - wp_untrash_post( $field['ID'] ); - - - // action for 3rd party customisation - do_action( 'acf/untrash_field', $field ); - - - // return - return true; -} - - -/* -* acf_prepare_fields_for_export -* -* description -* -* @type function -* @date 11/03/2014 -* @since 5.0.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_prepare_fields_for_export( $fields = false ) { - - // validate - if( empty($fields) ) return $fields; - - - // format - foreach( array_keys($fields) as $i ) { - - // prepare - $fields[ $i ] = acf_prepare_field_for_export( $fields[ $i ] ); - - } - - - // return - return $fields; - -} - - -/* -* acf_prepare_field_for_export -* -* description -* -* @type function -* @date 11/03/2014 -* @since 5.0.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_prepare_field_for_export( $field ) { - - // extract some args - $extract = acf_extract_vars($field, array( - 'ID', - 'prefix', - 'value', - 'menu_order', - 'id', - 'class', - 'parent', - '_name', - '_prepare', - '_valid', - )); - - - /** - * Filters the $field array before being returned to the export tool. - * - * @date 12/02/2014 - * @since 5.0.0 - * - * @param array $field The field array. - */ - $field = apply_filters( "acf/prepare_field_for_export/type={$field['type']}", $field ); - $field = apply_filters( "acf/prepare_field_for_export", $field ); - - - // return - return $field; -} - - -/* -* acf_prepare_fields_for_import -* -* description -* -* @type function -* @date 11/03/2014 -* @since 5.0.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_prepare_fields_for_import( $fields = false ) { - - // validate - if( empty($fields) ) return array(); - - - // re-index array - $fields = array_values($fields); - - - // vars - $i = 0; - - - // format - while( $i < count($fields) ) { - - // prepare field - $field = acf_prepare_field_for_import( $fields[ $i ] ); - - - // allow multiple fields to be returned ($field + $sub_fields) - if( !isset($field['key']) && isset($field[0]) ) { - - // merge in $field (1 or more fields) - array_splice($fields, $i, 1, $field); - - } - - - // $i - $i++; - - } - - - // filter for 3rd party customization - $fields = apply_filters('acf/prepare_fields_for_import', $fields); - - - // return - return $fields; - -} - - -/* -* acf_prepare_field_for_import -* -* description -* -* @type function -* @date 11/03/2014 -* @since 5.0.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_prepare_field_for_import( $field ) { - - // extract some args - $extract = acf_extract_vars($field, array( - 'value', - 'id', - 'class', - '_name', - '_prepare', - '_valid', - )); - - - /** - * Filters the $field array before being returned to the import tool. - * - * @date 12/02/2014 - * @since 5.0.0 - * - * @param array $field The field array. - */ - $field = apply_filters( "acf/prepare_field_for_import/type={$field['type']}", $field ); - $field = apply_filters( "acf/prepare_field_for_import", $field ); - - - // return - return $field; -} - - -/* -* acf_get_sub_field -* -* This function will return a field for the given selector, and $field (parent). -* -* @type function -* @date 30/09/13 -* @since 5.0.0 -* -* @param $selector (string) -* @param $field (mixed) -* @return $field (array) -*/ - -function acf_get_sub_field( $selector, $field ) { - - // vars - $sub_field = false; - - - // check sub_fields - if( isset($field['sub_fields']) ) { - - // loop - foreach( $field['sub_fields'] as $_sub_field ) { - - // check name and key - if( acf_is_field($_sub_field, $selector) ) { - - $sub_field = $_sub_field; - break; - - } - - } - - } - - - /** - * Filters the $sub_field found. - * - * @date 12/02/2014 - * @since 5.0.0 - * - * @param array $sub_field The found sub field array. - * @param string $selector The selector used to search. - * @param array $field The parent field array. - */ - $sub_field = apply_filters( "acf/get_sub_field/type={$field['type']}", $sub_field, $selector, $field ); - $sub_field = apply_filters( "acf/get_sub_field", $sub_field, $selector, $field ); - - - // return - return $sub_field; - -} - - -/* -* acf_is_field -* -* This function will compare a $selector against a $field array -* -* @type function -* @date 1/7/17 -* @since 5.6.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_is_field( $field, $selector = '' ) { - - // vars - $keys = array( - 'ID', - 'name', - 'key', - '_name', - '__name', - ); - - - // loop - foreach( $keys as $k ) { - - if( isset($field[ $k ]) && $field[ $k ] === $selector ) return true; - - } - - - // return - return false; - -} - - -/* -* acf_get_field_ancestors -* -* This function will return an array of all ancestor fields -* -* @type function -* @date 22/06/2016 -* @since 5.3.8 -* -* @param $field (array) -* @return (array) -*/ - -function acf_get_field_ancestors( $field ) { - - // get field - $ancestors = array(); - - - // loop - while( $field && acf_is_field_key($field['parent']) ) { - - $ancestors[] = $field['parent']; - $field = acf_get_field($field['parent']); - - } - - - // return - return $ancestors; - -} - - -/* -* acf_maybe_get_sub_field -* -* This function will attempt to find a sub field -* -* @type function -* @date 3/10/2016 -* @since 5.4.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_maybe_get_sub_field( $selectors, $post_id = false, $strict = true ) { - - // bail ealry if not enough selectors - if( !is_array($selectors) || count($selectors) < 3 ) return false; - - - // vars - $offset = acf_get_setting('row_index_offset'); - $selector = acf_extract_var( $selectors, 0 ); - $selectors = array_values( $selectors ); // reset keys - - - // attempt get field - $field = acf_maybe_get_field( $selector, $post_id, $strict ); - - - // bail early if no field - if( !$field ) return false; - - - // loop - for( $j = 0; $j < count($selectors); $j+=2 ) { - - // vars - $sub_i = $selectors[ $j ]; - $sub_s = $selectors[ $j+1 ]; - $field_name = $field['name']; - - - // find sub field - $field = acf_get_sub_field( $sub_s, $field ); - - - // bail early if no sub field - if( !$field ) return false; - - - // add to name - $field['name'] = $field_name . '_' . ($sub_i-$offset) . '_' . $field['name']; - - } - - - // return - return $field; - - -} - - -/* -* acf_prefix_fields -* -* This funtion will safely change the prefix for an array of fields -* Needed to allow clone field to continue working on nave menu item and widget forms -* -* @type function -* @date 5/9/17 -* @since 5.6.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_prefix_fields( &$fields, $prefix = 'acf' ) { - - // loop - foreach( $fields as &$field ) { - - // replace 'acf' with $prefix - $field['prefix'] = substr_replace($field['prefix'], $prefix, 0, 3); - - } - - - // return - return $fields; - -} - -/** -* acf_apply_field_filters -* -* description -* -* @date 11/9/18 -* @since 5.7.6 -* -* @param type $var Description. Default. -* @return type Description. -*/ -/* -function acf_apply_field_filters( $value ) { - - // get function args - $args = func_get_args(); - - // find field in $args - $field = false; - foreach( $args as $arg ) { - if( is_array($arg) && isset($arg['key'], $arg['type'], $arg['_name']) ) { - $field = $arg; - break; - } - } - - // vars - $filter = current_filter(); - - // unshift tag to args - array_unshift($args, $filter); - - // apply field filters - if( $field ) { - - // $filter/type=$type - $args[0] = "{$filter}/type={$field['type']}"; - $value = call_user_func_array('apply_filters', $args); - - // $filter/name=$name - $args[0] = "{$filter}/name={$field['_name']}"; - $value = call_user_func_array('apply_filters', $args); - - // $filter/key=$key - $args[0] = "{$filter}/key={$field['key']}"; - $value = call_user_func_array('apply_filters', $args); - } - - // return - return $value; -} -*/ - -?> \ No newline at end of file diff --git a/includes/api/api-helpers.php b/includes/api/api-helpers.php index 0019fba..9ffa31e 100644 --- a/includes/api/api-helpers.php +++ b/includes/api/api-helpers.php @@ -546,10 +546,7 @@ function acf_parse_args( $args, $defaults = array() ) { */ function acf_parse_types( $array ) { - - // return return array_map( 'acf_parse_type', $array ); - } @@ -567,26 +564,21 @@ function acf_parse_types( $array ) { */ function acf_parse_type( $v ) { + + // Check if is string. + if( is_string($v) ) { - // bail early if not string - if( !is_string($v) ) return $v; - - - // trim - $v = trim($v); - - - // convert int (string) to int - if( is_numeric($v) && strval((int)$v) === $v ) { - - $v = intval( $v ); + // Trim ("Word " = "Word"). + $v = trim( $v ); + // Convert int strings to int ("123" = 123). + if( is_numeric($v) && strpos($v, '.') === false ) { + $v = intval( $v ); + } } - - // return + // return. return $v; - } @@ -3607,14 +3599,6 @@ function acf_str_join( $s1 = '', $s2 = '' ) { return $s1 . $s2; } -// Tests. -//acf_test( acf_str_join('http://multisite.local/sub1/', '/sample-page/'), 'http://multisite.local/sub1/sample-page/' ); -//acf_test( acf_str_join('http://multisite.local/sub1/', 'sample-page/'), 'http://multisite.local/sub1/sample-page/' ); -//acf_test( acf_str_join('http://multisite.local/sub1/', '/sub1'), 'http://multisite.local/sub1/sub1' ); -//acf_test( acf_str_join('http://multisite.local/sub1/', '/sub1/sample-page/'), 'http://multisite.local/sub1/sample-page/' ); -//acf_test( acf_str_join('http://multisite.local/', '/sub1/sample-page/'), 'http://multisite.local/sub1/sample-page/' ); - - /* * acf_current_user_can_admin * @@ -4474,6 +4458,20 @@ function acf_format_date( $value, $format ) { } +/** + * acf_clear_log + * + * Deletes the debug.log file. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param type $var Description. Default. + * @return type Description. + */ +function acf_clear_log() { + unlink( WP_CONTENT_DIR . '/debug.log' ); +} /* * acf_log @@ -4530,23 +4528,6 @@ function acf_dev_log() { } } -/** -* acf_test -* -* Tests a function against an expected result and logs the pass/fail. -* -* @date 19/11/18 -* @since 5.8.0 -* -* @param type $var Description. Default. -* @return type Description. -*/ -function acf_test( $result, $expected_result ) { - $success = ($result === $expected_result); - acf_log('ACF Test', $success ? '(Pass)' : '(Fail)', $result, $expected_result); -} - - /* * acf_doing * @@ -4645,183 +4626,6 @@ function acf_is_plugin_active() { } - -/** -* acf_get_filters -* -* Returns the registered filters -* -* @date 2/2/18 -* @since 5.6.5 -* -* @param type $var Description. Default. -* @return type Description. -*/ - -function acf_get_filters() { - - // get - $filters = acf_raw_setting('filters'); - - // array - $filters = is_array($filters) ? $filters : array(); - - // return - return $filters; -} - - -/** -* acf_update_filters -* -* Updates the registered filters -* -* @date 2/2/18 -* @since 5.6.5 -* -* @param type $var Description. Default. -* @return type Description. -*/ - -function acf_update_filters( $filters ) { - return acf_update_setting('filters', $filters); -} - - -/* -* acf_enable_filter -* -* This function will enable a filter -* -* @type function -* @date 15/07/2016 -* @since 5.4.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_enable_filter( $filter = '' ) { - - // get - $filters = acf_get_filters(); - - // append - $filters[ $filter ] = true; - - // update - acf_update_filters( $filters ); -} - - -/* -* acf_disable_filter -* -* This function will disable a filter -* -* @type function -* @date 15/07/2016 -* @since 5.4.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_disable_filter( $filter = '' ) { - - // get - $filters = acf_get_filters(); - - // append - $filters[ $filter ] = false; - - // update - acf_update_filters( $filters ); -} - - -/* -* acf_enable_filters -* -* ACF uses filters to modify field group and field data -* This function will enable them allowing ACF to interact with all data -* -* @type function -* @date 14/07/2016 -* @since 5.4.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_enable_filters() { - - // get - $filters = acf_get_filters(); - - // loop - foreach( array_keys($filters) as $k ) { - $filters[ $k ] = true; - } - - // update - acf_update_filters( $filters ); -} - - -/* -* acf_disable_filters -* -* ACF uses filters to modify field group and field data -* This function will disable them allowing ACF to interact only with raw DB data -* -* @type function -* @date 14/07/2016 -* @since 5.4.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_disable_filters() { - - // get - $filters = acf_get_filters(); - - // loop - foreach( array_keys($filters) as $k ) { - $filters[ $k ] = false; - } - - // update - acf_update_filters( $filters ); -} - - -/* -* acf_is_filter_enabled -* -* ACF uses filters to modify field group and field data -* This function will return true if they are enabled -* -* @type function -* @date 14/07/2016 -* @since 5.4.0 -* -* @param $post_id (int) -* @return $post_id (int) -*/ - -function acf_is_filter_enabled( $filter = '' ) { - - // get - $filters = acf_get_filters(); - - // return - return !empty($filters[ $filter ]); -} - - /* * acf_send_ajax_results * @@ -5180,42 +4984,6 @@ function acf_decrypt( $data = '' ) { } - -/* -* acf_get_post_templates -* -* This function will return an array of all post templates (including parent theme templates) -* -* @type function -* @date 29/8/17 -* @since 5.6.2 -* -* @param n/a -* @return (array) -*/ - -function acf_get_post_templates() { - - // vars - $post_types = acf_get_post_types(); - $post_templates = array(); - - - // loop - foreach( $post_types as $post_type ) { - $post_templates[ $post_type ] = wp_get_theme()->get_page_templates(null, $post_type); - } - - - // remove empty templates - $post_templates = array_filter( $post_templates ); - - - // return - return $post_templates; - -} - /** * acf_parse_markdown * diff --git a/includes/api/api-template.php b/includes/api/api-template.php index bbfba1f..bfd0fd2 100644 --- a/includes/api/api-template.php +++ b/includes/api/api-template.php @@ -147,6 +147,106 @@ function get_field_object( $selector, $post_id = false, $format_value = true, $l } +/* +* acf_get_object_field +* +* This function will return a field for the given selector. +* It will also review the field_reference to ensure the correct field is returned which makes it useful for the template API +* +* @type function +* @date 4/08/2015 +* @since 5.2.3 +* +* @param $selector (mixed) identifyer of field. Can be an ID, key, name or post object +* @param $post_id (mixed) the post_id of which the value is saved against +* @param $strict (boolean) if true, return a field only when a field key is found. +* @return $field (array) +*/ +function acf_maybe_get_field( $selector, $post_id = false, $strict = true ) { + + // init + acf_init(); + + // Check if field key was given. + if( acf_is_field_key($selector) ) { + return acf_get_field( $selector ); + } + + // Lookup field via reference. + $post_id = acf_get_valid_post_id( $post_id ); + $field = acf_get_meta_field( $selector, $post_id ); + if( $field ) { + return $field; + } + + // Lookup field loosely via name. + if( !$strict ) { + return acf_get_field( $selector ); + } + + // Return no result. + return false; +} + +/* +* acf_maybe_get_sub_field +* +* This function will attempt to find a sub field +* +* @type function +* @date 3/10/2016 +* @since 5.4.0 +* +* @param $post_id (int) +* @return $post_id (int) +*/ + +function acf_maybe_get_sub_field( $selectors, $post_id = false, $strict = true ) { + + // bail ealry if not enough selectors + if( !is_array($selectors) || count($selectors) < 3 ) return false; + + + // vars + $offset = acf_get_setting('row_index_offset'); + $selector = acf_extract_var( $selectors, 0 ); + $selectors = array_values( $selectors ); // reset keys + + + // attempt get field + $field = acf_maybe_get_field( $selector, $post_id, $strict ); + + + // bail early if no field + if( !$field ) return false; + + + // loop + for( $j = 0; $j < count($selectors); $j+=2 ) { + + // vars + $sub_i = $selectors[ $j ]; + $sub_s = $selectors[ $j+1 ]; + $field_name = $field['name']; + + + // find sub field + $field = acf_get_sub_field( $sub_s, $field ); + + + // bail early if no sub field + if( !$field ) return false; + + + // add to name + $field['name'] = $field_name . '_' . ($sub_i-$offset) . '_' . $field['name']; + + } + + + // return + return $field; +} /* * get_fields() @@ -206,6 +306,9 @@ function get_fields( $post_id = false, $format_value = true ) { function get_field_objects( $post_id = false, $format_value = true, $load_value = true ) { + // init + acf_init(); + // validate post_id $post_id = acf_get_valid_post_id( $post_id ); diff --git a/includes/api/api-value.php b/includes/api/api-value.php deleted file mode 100644 index bd59965..0000000 --- a/includes/api/api-value.php +++ /dev/null @@ -1,715 +0,0 @@ - https://core.trac.wordpress.org/browser/tags/3.4.2/wp-includes/meta.php#L82: line 101 (does use stripslashes_deep) - // update_option -> https://core.trac.wordpress.org/browser/tags/3.5.1/wp-includes/option.php#L0: line 215 (does not use stripslashes_deep) - $value = stripslashes_deep($value); - - - // add or update - if( get_option($option) !== false ) { - - $return = update_option( $option, $value ); - - } else { - - $return = add_option( $option, $value, $deprecated, $autoload ); - - } - - - // return - return $return; - -} - - -/** -* acf_get_reference -* -* Finds the field key for a given field name and post_id. -* -* @date 26/1/18 -* @since 5.6.5 -* -* @param string $field_name The name of the field. eg 'sub_heading' -* @param mixed $post_id The post_id of which the value is saved against -* @return string $reference The field key -*/ - -function acf_get_reference( $field_name, $post_id ) { - - // allow filter to short-circuit load_value logic - $reference = apply_filters( "acf/pre_load_reference", null, $field_name, $post_id ); - if( $reference !== null ) { - return $reference; - } - - // get hidden meta for this field name - $reference = acf_get_metadata( $post_id, $field_name, true ); - - // filter - $reference = apply_filters('acf/load_reference', $reference, $field_name, $post_id); - $reference = apply_filters('acf/get_field_reference', $reference, $field_name, $post_id); - - // return - return $reference; - -} - -// deprecated in 5.6.8 -function acf_get_field_reference( $field_name, $post_id ) { - return acf_get_reference( $field_name, $post_id ); -} - - -/* -* acf_get_value -* -* This function will load in a field's value -* -* @type function -* @date 28/09/13 -* @since 5.0.0 -* -* @param $post_id (int) -* @param $field (array) -* @return (mixed) -*/ - -function acf_get_value( $post_id = 0, $field ) { - - // allow filter to short-circuit load_value logic - $value = apply_filters( "acf/pre_load_value", null, $post_id, $field ); - if( $value !== null ) { - return $value; - } - - - // vars - $cache_key = "get_value/post_id={$post_id}/name={$field['name']}"; - - - // return early if cache is found - if( acf_isset_cache($cache_key) ) { - return acf_get_cache($cache_key); - } - - - // load value - $value = acf_get_metadata( $post_id, $field['name'] ); - - - // if value was duplicated, it may now be a serialized string! - $value = maybe_unserialize( $value ); - - - // no value? try default_value - if( $value === null && isset($field['default_value']) ) { - $value = $field['default_value']; - } - - - /** - * Filters the $value after it has been loaded. - * - * @date 28/09/13 - * @since 5.0.0 - * - * @param mixed $value The value to preview. - * @param string $post_id The post ID for this value. - * @param array $field The field array. - */ - $value = apply_filters( "acf/load_value/type={$field['type']}", $value, $post_id, $field ); - $value = apply_filters( "acf/load_value/name={$field['_name']}", $value, $post_id, $field ); - $value = apply_filters( "acf/load_value/key={$field['key']}", $value, $post_id, $field ); - $value = apply_filters( "acf/load_value", $value, $post_id, $field ); - - - // update cache - acf_set_cache($cache_key, $value); - - - // return - return $value; - -} - - -/* -* acf_format_value -* -* This function will format the value for front end use -* -* @type function -* @date 3/07/2014 -* @since 5.0.0 -* -* @param $value (mixed) -* @param $post_id (mixed) -* @param $field (array) -* @return $value -*/ - -function acf_format_value( $value, $post_id, $field ) { - - // vars - $cache_key = "format_value/post_id={$post_id}/name={$field['name']}"; - - - // return early if cache is found - if( acf_isset_cache($cache_key) ) { - - return acf_get_cache($cache_key); - - } - - - /** - * Filters the $value for use in a template function. - * - * @date 28/09/13 - * @since 5.0.0 - * - * @param mixed $value The value to preview. - * @param string $post_id The post ID for this value. - * @param array $field The field array. - */ - $value = apply_filters( "acf/format_value/type={$field['type']}", $value, $post_id, $field ); - $value = apply_filters( "acf/format_value/name={$field['_name']}", $value, $post_id, $field ); - $value = apply_filters( "acf/format_value/key={$field['key']}", $value, $post_id, $field ); - $value = apply_filters( "acf/format_value", $value, $post_id, $field ); - - - // update cache - acf_set_cache($cache_key, $value); - - - // return - return $value; - -} - - -/* -* acf_update_value -* -* updates a value into the db -* -* @type action -* @date 23/01/13 -* -* @param $value (mixed) -* @param $post_id (mixed) -* @param $field (array) -* @return (boolean) -*/ - -function acf_update_value( $value = null, $post_id = 0, $field ) { - - // strip slashes - if( acf_get_setting('stripslashes') ) { - $value = stripslashes_deep($value); - } - - - /** - * Allows developers to run a custom update function. - * - * @date 28/09/13 - * @since 5.0.0 - * - * @param null $check Return a non null value to prevent default. - * @param mixed $value The value to update. - * @param string $post_id The post ID for this value. - * @param array $field The field array. - */ - $check = apply_filters( "acf/pre_update_value", null, $value, $post_id, $field ); - if( $check !== null ) { - return $check; - } - - - /** - * Filters the $value before it is saved. - * - * @date 28/09/13 - * @since 5.0.0 - * @since 5.7.6 Added $_value parameter. - * - * @param mixed $value The value to update. - * @param string $post_id The post ID for this value. - * @param array $field The field array. - * @param mixed $_value The original value before modification. - */ - $_value = $value; - $value = apply_filters( "acf/update_value/type={$field['type']}", $value, $post_id, $field, $_value ); - $value = apply_filters( "acf/update_value/name={$field['_name']}", $value, $post_id, $field, $_value ); - $value = apply_filters( "acf/update_value/key={$field['key']}", $value, $post_id, $field, $_value ); - $value = apply_filters( "acf/update_value", $value, $post_id, $field, $_value ); - - - // allow null to delete - if( $value === null ) { - - return acf_delete_value( $post_id, $field ); - - } - - - // update value - $return = acf_update_metadata( $post_id, $field['name'], $value ); - - - // update reference - acf_update_metadata( $post_id, $field['name'], $field['key'], true ); - - - // clear cache - acf_delete_cache("get_value/post_id={$post_id}/name={$field['name']}"); - acf_delete_cache("format_value/post_id={$post_id}/name={$field['name']}"); - - - // return - return $return; - -} - - -/* -* acf_delete_value -* -* This function will delete a value from the database -* -* @type function -* @date 28/09/13 -* @since 5.0.0 -* -* @param $post_id (mixed) -* @param $field (array) -* @return (boolean) -*/ - -function acf_delete_value( $post_id = 0, $field ) { - - /** - * Fires before a value is deleted. - * - * @date 28/09/13 - * @since 5.0.0 - * - * @param string $post_id The post ID for this value. - * @param mixed $name The meta name. - * @param array $field The field array. - */ - do_action( "acf/delete_value/type={$field['type']}", $post_id, $field['name'], $field ); - do_action( "acf/delete_value/name={$field['_name']}", $post_id, $field['name'], $field ); - do_action( "acf/delete_value/key={$field['key']}", $post_id, $field['name'], $field ); - do_action( "acf/delete_value", $post_id, $field['name'], $field ); - - - // delete value - $return = acf_delete_metadata( $post_id, $field['name'] ); - - - // delete reference - acf_delete_metadata( $post_id, $field['name'], true ); - - - // clear cache - acf_delete_cache("get_value/post_id={$post_id}/name={$field['name']}"); - acf_delete_cache("format_value/post_id={$post_id}/name={$field['name']}"); - - - // return - return $return; - -} - - -/* -* acf_copy_postmeta -* -* This function will copy postmeta from one post to another. -* Very useful for saving and restoring revisions -* -* @type function -* @date 25/06/2016 -* @since 5.3.8 -* -* @param $from_post_id (int) -* @param $to_post_id (int) -* @return n/a -*/ - -function acf_copy_postmeta( $from_post_id, $to_post_id ) { - - // get all postmeta - $meta = get_post_meta( $from_post_id ); - - - // bail early if no meta - if( !$meta ) return; - - - // loop - foreach( $meta as $name => $value ) { - - // attempt to find key value - $key = acf_maybe_get( $meta, '_'.$name ); - - - // bail ealry if no key - if( !$key ) continue; - - - // update vars - $value = $value[0]; - $key = $key[0]; - - - // bail early if $key is a not a field_key - if( !acf_is_field_key($key) ) continue; - - - // get_post_meta will return array before running maybe_unserialize - $value = maybe_unserialize( $value ); - - - // add in slashes - // - update_post_meta will unslash the value, so we must first slash it to avoid losing backslashes - // - https://codex.wordpress.org/Function_Reference/update_post_meta#Character_Escaping - if( is_string($value) ) { - - $value = wp_slash($value); - - } - - - // update value - acf_update_metadata( $to_post_id, $name, $value ); - acf_update_metadata( $to_post_id, $name, $key, true ); - - } - -} - - -/* -* acf_preview_value -* -* This function will return a human freindly 'preview' for a given field value -* -* @type function -* @date 24/10/16 -* @since 5.5.0 -* -* @param $value (mixed) -* @param $post_id (mixed) -* @param $field (array) -* @return (string) -*/ - -function acf_preview_value( $value, $post_id, $field ) { - - /** - * Filters the $value before used in HTML. - * - * @date 24/10/16 - * @since 5.5.0 - * - * @param mixed $value The value to preview. - * @param string $post_id The post ID for this value. - * @param array $field The field array. - */ - $value = apply_filters( "acf/preview_value/type={$field['type']}", $value, $post_id, $field ); - $value = apply_filters( "acf/preview_value/name={$field['_name']}", $value, $post_id, $field ); - $value = apply_filters( "acf/preview_value/key={$field['key']}", $value, $post_id, $field ); - $value = apply_filters( "acf/preview_value", $value, $post_id, $field ); - - // return - return $value; -} - -/** -* acf_get_option_meta -* -* Returns an array of meta for the given wp_option name prefix. -* -* @date 9/10/18 -* @since 5.8.0 -* -* @param string $prefix The wp_option name prefix. -* @return array -*/ -function acf_get_option_meta( $prefix = '' ) { - - // global - global $wpdb; - - // vars - $meta = array(); - $search = "{$prefix}_%"; - $_search = "_{$prefix}_%"; - - // escape underscores - $search = str_replace('_', '\_', $search); - $_search = str_replace('_', '\_', $_search); - - // query - $rows = $wpdb->get_results($wpdb->prepare( - "SELECT * - FROM $wpdb->options - WHERE option_name LIKE %s - OR option_name LIKE %s", - $search, - $_search - ), ARRAY_A); - - // loop - $len = strlen("{$prefix}_"); - foreach( $rows as $row ) { - $meta[ substr($row['option_name'], $len) ][] = $row['option_value']; - } - - // return unserialized - return array_map('maybe_unserialize', $meta); -} - -/** -* acf_get_meta -* -* Returns an array of "ACF only" meta for the given post_id. -* -* @date 9/10/18 -* @since 5.8.0 -* -* @param mixed $post_id The post_id for this data. -* @return array -*/ -function acf_get_meta( $post_id = 0 ) { - - // allow filter to short-circuit load_value logic - $pre = apply_filters( "acf/pre_load_meta", null, $post_id ); - if( $pre !== null ) { - return $pre; - } - - // get post_id info - extract( acf_get_post_id_info($post_id) ); - - // use get_$type_meta() function when possible - if( function_exists("get_{$type}_meta") ) { - $allmeta = call_user_func("get_{$type}_meta", $id, '', true); - - // default to wp_options - } else { - $allmeta = acf_get_option_meta( $id ); - } - - // loop - $meta = array(); - foreach( $allmeta as $key => $value ) { - - // if is value - if( isset($allmeta["_$key"]) ) { - $meta[ $key ] = $allmeta[ $key ][0]; - $meta[ "_$key" ] = $allmeta[ "_$key" ][0]; - } - } - - // return - return $meta; -} - -?> \ No newline at end of file diff --git a/includes/cache.php b/includes/cache.php deleted file mode 100644 index f74a203..0000000 --- a/includes/cache.php +++ /dev/null @@ -1,445 +0,0 @@ -active; - - } - - - /* - * enable - * - * This function will enable ACF caching - * - * @type function - * @date 26/6/17 - * @since 5.6.0 - * - * @param n/a - * @return n/a - */ - - function enable() { - - $this->active = true; - - } - - - /* - * disable - * - * This function will disable ACF caching - * - * @type function - * @date 26/6/17 - * @since 5.6.0 - * - * @param n/a - * @return n/a - */ - - function disable() { - - $this->active = false; - - } - - - /* - * get_key - * - * This function will check for references and modify the key - * - * @type function - * @date 30/06/2016 - * @since 5.4.0 - * - * @param $key (string) - * @return $key - */ - - function get_key( $key = '' ) { - - // check for reference - if( isset($this->reference[ $key ]) ) { - - $key = $this->reference[ $key ]; - - } - - - // return - return $key; - - } - - - - /* - * isset_cache - * - * This function will return true if a cached data exists for the given key - * - * @type function - * @date 30/06/2016 - * @since 5.4.0 - * - * @param $key (string) - * @return (boolean) - */ - - function isset_cache( $key = '' ) { - - // bail early if not active - if( !$this->is_active() ) return false; - - - // vars - $key = $this->get_key($key); - $found = false; - - - // get cache - $cache = wp_cache_get($key, 'acf', false, $found); - - - // return - return $found; - - } - - - /* - * get_cache - * - * This function will return cached data for a given key - * - * @type function - * @date 30/06/2016 - * @since 5.4.0 - * - * @param $key (string) - * @return (mixed) - */ - - function get_cache( $key = '' ) { - - // bail early if not active - if( !$this->is_active() ) return false; - - - // vars - $key = $this->get_key($key); - $found = false; - - - // get cache - $cache = wp_cache_get($key, 'acf', false, $found); - - - // return - return $cache; - - } - - - /* - * set_cache - * - * This function will set cached data for a given key - * - * @type function - * @date 30/06/2016 - * @since 5.4.0 - * - * @param $key (string) - * @param $data (mixed) - * @return n/a - */ - - function set_cache( $key = '', $data = '' ) { - - // bail early if not active - if( !$this->is_active() ) return false; - - - // set - wp_cache_set($key, $data, 'acf'); - - - // return - return $key; - - } - - - /* - * set_cache_reference - * - * This function will set a reference to cached data for a given key - * - * @type function - * @date 30/06/2016 - * @since 5.4.0 - * - * @param $key (string) - * @param $reference (string) - * @return n/a - */ - - function set_cache_reference( $key = '', $reference = '' ) { - - // bail early if not active - if( !$this->is_active() ) return false; - - - // add - $this->reference[ $key ] = $reference; - - - // resturn - return $key; - - } - - - /* - * delete_cache - * - * This function will delete cached data for a given key - * - * @type function - * @date 30/06/2016 - * @since 5.4.0 - * - * @param $key (string) - * @return n/a - */ - - function delete_cache( $key = '' ) { - - // bail early if not active - if( !$this->is_active() ) return false; - - - // delete - return wp_cache_delete( $key, 'acf' ); - - } - -} - - -// initialize -acf()->cache = new acf_cache(); - -endif; // class_exists check - - -/* -* acf_is_cache_active -* -* alias of acf()->cache->is_active() -* -* @type function -* @date 26/6/17 -* @since 5.6.0 -* -* @param n/a -* @return n/a -*/ - -function acf_is_cache_active() { - - return acf()->cache->is_active(); - -} - - -/* -* acf_disable_cache -* -* alias of acf()->cache->disable() -* -* @type function -* @date 26/6/17 -* @since 5.6.0 -* -* @param n/a -* @return n/a -*/ - -function acf_disable_cache() { - - return acf()->cache->disable(); - -} - - -/* -* acf_enable_cache -* -* alias of acf()->cache->enable() -* -* @type function -* @date 26/6/17 -* @since 5.6.0 -* -* @param n/a -* @return n/a -*/ - -function acf_enable_cache() { - - return acf()->cache->enable(); - -} - - -/* -* acf_isset_cache -* -* alias of acf()->cache->isset_cache() -* -* @type function -* @date 30/06/2016 -* @since 5.4.0 -* -* @param n/a -* @return n/a -*/ - -function acf_isset_cache( $key = '' ) { - - return acf()->cache->isset_cache( $key ); - -} - - -/* -* acf_get_cache -* -* alias of acf()->cache->get_cache() -* -* @type function -* @date 30/06/2016 -* @since 5.4.0 -* -* @param n/a -* @return n/a -*/ - -function acf_get_cache( $key = '' ) { - - return acf()->cache->get_cache( $key ); - -} - - -/* -* acf_set_cache -* -* alias of acf()->cache->set_cache() -* -* @type function -* @date 30/06/2016 -* @since 5.4.0 -* -* @param n/a -* @return n/a -*/ - -function acf_set_cache( $key = '', $data ) { - - return acf()->cache->set_cache( $key, $data ); - -} - - -/* -* acf_set_cache_reference -* -* alias of acf()->cache->set_cache_reference() -* -* @type function -* @date 30/06/2016 -* @since 5.4.0 -* -* @param n/a -* @return n/a -*/ - -function acf_set_cache_reference( $key = '', $reference = '' ) { - - return acf()->cache->set_cache_reference( $key, $reference ); - -} - - -/* -* acf_delete_cache -* -* alias of acf()->cache->delete_cache() -* -* @type function -* @date 30/06/2016 -* @since 5.4.0 -* -* @param n/a -* @return n/a -*/ - -function acf_delete_cache( $key = '' ) { - - return acf()->cache->delete_cache( $key ); - -} - -?> \ No newline at end of file diff --git a/includes/class-acf-data.php b/includes/class-acf-data.php index 3cf987c..455410b 100644 --- a/includes/class-acf-data.php +++ b/includes/class-acf-data.php @@ -12,6 +12,12 @@ class ACF_Data { /** @var array Storage for data. */ var $data = array(); + /** @var array Storage for data aliases. */ + var $aliases = array(); + + /** @var bool Enables unique data per site. */ + var $multisite = false; + /** * __construct * @@ -29,8 +35,8 @@ class ACF_Data { $this->cid = acf_uniqid(); // Set data. - if( $data && is_array($data) ) { - $this->data = array_merge($this->data, $data); + if( $data ) { + $this->set( $data ); } // Initialize. @@ -49,7 +55,43 @@ class ACF_Data { * @return void */ function initialize() { + // Do nothing. + } + + /** + * prop + * + * Sets a property for the given name and returns $this for chaining. + * + * @date 9/1/19 + * @since 5.7.10 + * + * @param (string|array) $name The data name or an array of data. + * @param mixed $value The data value. + * @return ACF_Data + */ + function prop( $name = '', $value = null ) { + // Update property. + $this->{$name} = $value; + + // Return this for chaining. + return $this; + } + + /** + * _key + * + * Returns a key for the given name allowing aliasses to work. + * + * @date 18/1/19 + * @since 5.7.10 + * + * @param type $var Description. Default. + * @return type Description. + */ + function _key( $name = '' ) { + return isset($this->aliases[ $name ]) ? $this->aliases[ $name ] : $name; } /** @@ -64,7 +106,23 @@ class ACF_Data { * @return boolean */ function has( $name = '' ) { - return isset($this->data[ $name ]); + $key = $this->_key($name); + return isset($this->data[ $key ]); + } + + /** + * is + * + * Similar to has() but does not check aliases. + * + * @date 7/2/19 + * @since 5.7.11 + * + * @param type $var Description. Default. + * @return type Description. + */ + function is( $key = '' ) { + return isset($this->data[ $key ]); } /** @@ -78,8 +136,17 @@ class ACF_Data { * @param string $name The data name. * @return mixed */ - function get( $name = '' ) { - return isset($this->data[ $name ]) ? $this->data[ $name ] : null; + function get( $name = false ) { + + // Get all. + if( $name === false ) { + return $this->data; + + // Get specific. + } else { + $key = $this->_key($name); + return isset($this->data[ $key ]) ? $this->data[ $key ] : null; + } } /** @@ -109,7 +176,7 @@ class ACF_Data { * @param mixed $value The data value. * @return ACF_Data */ - function set( $name = '', $value ) { + function set( $name = '', $value = null ) { // Set multiple. if( is_array($name) ) { @@ -124,6 +191,26 @@ class ACF_Data { return $this; } + /** + * append + * + * Appends data for the given name and returns $this for chaining. + * + * @date 9/1/19 + * @since 5.7.10 + * + * @param mixed $value The data value. + * @return ACF_Data + */ + function append( $value = null ) { + + // Append. + $this->data[] = $value; + + // Return this for chaining. + return $this; + } + /** * remove * @@ -143,6 +230,125 @@ class ACF_Data { // Return this for chaining. return $this; } + + /** + * reset + * + * Resets the data. + * + * @date 22/1/19 + * @since 5.7.10 + * + * @param void + * @return void + */ + function reset() { + $this->data = array(); + $this->aliases = array(); + } + + /** + * count + * + * Returns the data count. + * + * @date 23/1/19 + * @since 5.7.10 + * + * @param void + * @return int + */ + function count() { + return count( $this->data ); + } + + /** + * query + * + * Returns a filtered array of data based on the set of key => value arguments. + * + * @date 23/1/19 + * @since 5.7.10 + * + * @param void + * @return int + */ + function query( $args, $operator = 'AND' ) { + return wp_list_filter( $this->data, $args, $operator ); + } + + /** + * alias + * + * Sets an alias for the given name allowing data to be found via multiple identifiers. + * + * @date 18/1/19 + * @since 5.7.10 + * + * @param type $var Description. Default. + * @return type Description. + */ + function alias( $name = '' /*, $alias, $alias2, etc */ ) { + + // Get all aliases. + $args = func_get_args(); + array_shift( $args ); + + // Loop over aliases and add to data. + foreach( $args as $alias ) { + $this->aliases[ $alias ] = $name; + } + + // Return this for chaining. + return $this; + } + + /** + * switch_site + * + * Triggered when switching between sites on a multisite installation. + * + * @date 13/2/19 + * @since 5.7.11 + * + * @param int $site_id New blog ID. + * @param int prev_blog_id Prev blog ID. + * @return void + */ + function switch_site( $site_id, $prev_site_id ) { + + // Bail early if not multisite compatible. + if( !$this->multisite ) { + return; + } + + // Bail early if no change in blog ID. + if( $site_id === $prev_site_id ) { + return; + } + + // Create storage. + if( !isset($this->site_data) ) { + $this->site_data = array(); + $this->site_aliases = array(); + } + + // Save state. + $this->site_data[ $prev_site_id ] = $this->data; + $this->site_aliases[ $prev_site_id ] = $this->aliases; + + // Reset state. + $this->data = array(); + $this->aliases = array(); + + // Load state. + if( isset($this->site_data[ $site_id ]) ) { + $this->data = $this->site_data[ $site_id ]; + $this->aliases = $this->site_aliases[ $site_id ]; + unset( $this->site_data[ $site_id ] ); + unset( $this->site_aliases[ $site_id ] ); + } + } } endif; // class_exists check diff --git a/includes/compatibility.php b/includes/compatibility.php index f5988eb..8778177 100644 --- a/includes/compatibility.php +++ b/includes/compatibility.php @@ -34,11 +34,39 @@ class ACF_Compatibility { add_filter('acf/validate_field/type=user', array($this, 'validate_user_field'), 20, 1); add_filter('acf/validate_field_group', array($this, 'validate_field_group'), 20, 1); + // Modify field wrapper attributes + add_filter('acf/field_wrapper_attributes', array($this, 'field_wrapper_attributes'), 20, 2); + // location add_filter('acf/location/validate_rule/type=post_taxonomy', array($this, 'validate_post_taxonomy_location_rule'), 20, 1); add_filter('acf/location/validate_rule/type=post_category', array($this, 'validate_post_taxonomy_location_rule'), 20, 1); } + /** + * field_wrapper_attributes + * + * Adds compatibility with deprecated field wrap attributes. + * + * @date 21/1/19 + * @since 5.7.10 + * + * @param array $wrapper The wrapper attributes array. + * @param array $field The field array. + */ + function field_wrapper_attributes( $wrapper, $field ) { + + // Check compatibility setting. + if( acf_get_compatibility('field_wrapper_class') ) { + $wrapper['class'] .= " field_type-{$field['type']}"; + if( $field['key'] ) { + $wrapper['class'] .= " field_key-{$field['key']}"; + } + } + + // Return wrapper. + return $wrapper; + } + /** * validate_field * @@ -326,7 +354,7 @@ class ACF_Compatibility { // detect ACF4 data and generate key if( !$field_group['key'] ) { $version = 4; - $field_group['key'] = uniqid('group_'); + $field_group['key'] = isset($field_group['id']) ? "group_{$field_group['id']}" : uniqid('group_'); } // prior to version 5.0.0, settings were saved in an 'options' array diff --git a/includes/fields/class-acf-field-text.php b/includes/fields/class-acf-field-text.php index e9a9feb..528385e 100644 --- a/includes/fields/class-acf-field-text.php +++ b/includes/fields/class-acf-field-text.php @@ -160,6 +160,30 @@ class acf_field_text extends acf_field { } + /** + * validate_value + * + * Validates a field's value. + * + * @date 29/1/19 + * @since 5.7.11 + * + * @param (bool|string) Whether the value is vaid or not. + * @param mixed $value The field value. + * @param array $field The field array. + * @param string $input The HTML input name. + * @return (bool|string) + */ + function validate_value( $valid, $value, $field, $input ){ + + // Check maxlength + if( $field['maxlength'] && mb_strlen(wp_unslash($value)) > $field['maxlength'] ) { + return sprintf( __('Value must not exceed %d characters', 'acf'), $field['maxlength'] ); + } + + // Return. + return $valid; + } } diff --git a/includes/fields/class-acf-field-textarea.php b/includes/fields/class-acf-field-textarea.php index 8c41ee8..6999fdf 100644 --- a/includes/fields/class-acf-field-textarea.php +++ b/includes/fields/class-acf-field-textarea.php @@ -192,6 +192,30 @@ class acf_field_textarea extends acf_field { return $value; } + /** + * validate_value + * + * Validates a field's value. + * + * @date 29/1/19 + * @since 5.7.11 + * + * @param (bool|string) Whether the value is vaid or not. + * @param mixed $value The field value. + * @param array $field The field array. + * @param string $input The HTML input name. + * @return (bool|string) + */ + function validate_value( $valid, $value, $field, $input ){ + + // Check maxlength + if( $field['maxlength'] && mb_strlen(wp_unslash($value)) > $field['maxlength'] ) { + return sprintf( __('Value must not exceed %d characters', 'acf'), $field['maxlength'] ); + } + + // Return. + return $valid; + } } diff --git a/includes/forms/form-gutenberg.php b/includes/forms/form-gutenberg.php index 707f9f1..fa98964 100644 --- a/includes/forms/form-gutenberg.php +++ b/includes/forms/form-gutenberg.php @@ -85,9 +85,20 @@ class ACF_Form_Gutenberg {