增强套件,可改进"高级自定义字段"管理
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

600 lines
15 KiB

  1. <?php
  2. if(!defined('ABSPATH'))
  3. exit;
  4. // Register store
  5. acf_register_store('acfe/meta')->prop('multisite', true);
  6. // Check setting
  7. if(!acf_get_setting('acfe/modules/single_meta'))
  8. return;
  9. if(!class_exists('acfe_single_meta')):
  10. class acfe_single_meta{
  11. public $data = array();
  12. public $restricted = array();
  13. public $post_types = array();
  14. public $taxonomies = array();
  15. public $options = array();
  16. function __construct(){
  17. $this->restricted = acfe_get_setting('reserved_post_types', array());
  18. $this->post_types = apply_filters('acfe/modules/single_meta/post_types', array());
  19. $this->taxonomies = apply_filters('acfe/modules/single_meta/taxonomies', array());
  20. $this->options = apply_filters('acfe/modules/single_meta/options', false);
  21. // Field Objects
  22. add_filter('acf/pre_load_meta', array($this, 'pre_load_meta'), 999, 2);
  23. // Values
  24. add_filter('acf/pre_load_metadata', array($this, 'pre_load_metadata'), 999, 4);
  25. add_filter('acf/update_value', array($this, 'update_value'), 999, 3);
  26. add_filter('acf/pre_update_metadata', array($this, 'pre_update_metadata'), 999, 5);
  27. add_filter('acf/pre_delete_metadata', array($this, 'pre_delete_metadata'), 999, 4);
  28. // Save Post
  29. add_action('acf/save_post', array($this, 'pre_save_post'), 0);
  30. add_action('acf/save_post', array($this, 'save_post'), 999);
  31. // Settings
  32. add_action('acf/render_field_settings', array($this, 'field_setting'));
  33. // Post
  34. add_action('load-post.php', array($this, 'load_post'));
  35. add_action('load-post-new.php', array($this, 'load_post'));
  36. // Term
  37. add_action('load-edit-tags.php', array($this, 'load_term'));
  38. add_action('load-term.php', array($this, 'load_term'));
  39. // User
  40. add_action('load-user-new.php', array($this, 'load_user'));
  41. add_action('load-user-edit.php', array($this, 'load_user'));
  42. add_action('load-profile.php', array($this, 'load_user'));
  43. // Nav Menu Items
  44. add_action('wp_nav_menu_item_custom_fields', array($this, 'wp_nav_menu_item_custom_fields'), 5);
  45. // Options
  46. //add_action('acf/options_page/submitbox_before_major_actions', array($this, 'load_options'));
  47. }
  48. /*
  49. * Preload Meta
  50. */
  51. function pre_load_meta($return, $post_id){
  52. if(acf_is_filter_enabled('acfe/meta/native_load'))
  53. return $return;
  54. // Validate Post ID
  55. $validate = $this->validate_post_id($post_id);
  56. if(!$validate)
  57. return $return;
  58. // Get store
  59. $store = $this->get_store($post_id);
  60. $acf = $store->get("$post_id:acf");
  61. return $acf;
  62. }
  63. /*
  64. * Load Metadata
  65. */
  66. function pre_load_metadata($return, $post_id, $name, $hidden){
  67. if($name === 'acf')
  68. return $return;
  69. // Validate Post ID
  70. $validate = $this->validate_post_id($post_id);
  71. if(!$validate)
  72. return $return;
  73. // Get store
  74. $store = $this->get_store($post_id);
  75. $acf = $store->get("$post_id:acf");
  76. // Bail early if empty
  77. if(empty($acf))
  78. return $return;
  79. // Unslash values if needed
  80. if(acf_is_filter_enabled('acfe/meta/unslash')){
  81. $acf = wp_unslash($acf);
  82. }
  83. // Prefix
  84. $prefix = $hidden ? '_' : '';
  85. if(isset($acf["{$prefix}{$name}"])){
  86. // Value
  87. $return = $acf["{$prefix}{$name}"];
  88. }
  89. return $return;
  90. }
  91. /*
  92. * Update Value
  93. */
  94. function update_value($value, $post_id, $field){
  95. acf_disable_filter('acfe/meta/native_save');
  96. if(acf_maybe_get($field, 'acfe_save_meta')){
  97. acf_enable_filter('acfe/meta/native_save');
  98. }
  99. return $value;
  100. }
  101. /*
  102. * Update Metadata
  103. */
  104. function pre_update_metadata($return, $post_id, $name, $value, $hidden){
  105. if($name === 'acf')
  106. return $return;
  107. // Validate Post ID
  108. $validate = $this->validate_post_id($post_id);
  109. if(!$validate)
  110. return $return;
  111. // Get store
  112. $store = $this->get_store($post_id);
  113. $acf = $store->get("$post_id:acf");
  114. // Prefix
  115. $prefix = $hidden ? '_' : '';
  116. // Value
  117. $acf["{$prefix}{$name}"] = $value;
  118. // Update store
  119. $store->set("$post_id:acf", $acf);
  120. // Update if not compiling
  121. if(!acf_is_filter_enabled("acfe/meta/compile/{$post_id}")){
  122. $this->update_meta('acf', $acf, $post_id);
  123. }
  124. // Save normally
  125. if(acf_is_filter_enabled('acfe/meta/native_save')){
  126. return null;
  127. }
  128. // Delete Native ACF field if it already exists
  129. acf_enable_filter('acfe/meta/native_delete');
  130. acf_delete_metadata($post_id, $name, $hidden);
  131. acf_disable_filter('acfe/meta/native_delete');
  132. // Do not save as meta
  133. return true;
  134. }
  135. /*
  136. * Delete Metadata
  137. */
  138. function pre_delete_metadata($return, $post_id, $name, $hidden){
  139. if($name === 'acf' || acf_is_filter_enabled('acfe/meta/native_delete'))
  140. return $return;
  141. // Validate Post ID
  142. $validate = $this->validate_post_id($post_id);
  143. if(!$validate)
  144. return $return;
  145. // Get store
  146. $store = $this->get_store($post_id);
  147. $acf = $store->get("$post_id:acf");
  148. // Bail early if empty
  149. if(empty($acf))
  150. return $return;
  151. // Prefix
  152. $prefix = $hidden ? '_' : '';
  153. if(isset($acf["{$prefix}{$name}"])){
  154. // Value
  155. unset($acf["{$prefix}{$name}"]);
  156. // Update store
  157. $store->set("$post_id:acf", $acf);
  158. $this->update_meta('acf', $acf, $post_id);
  159. }
  160. return $return;
  161. }
  162. /*
  163. * acf/save_post:0
  164. */
  165. function pre_save_post($post_id = 0){
  166. if(!acf_maybe_get_POST('acfe_clean_meta'))
  167. return;
  168. // Validate Post ID
  169. $validate = $this->validate_post_id($post_id);
  170. if(!$validate)
  171. return;
  172. // Enable filter
  173. acf_enable_filter("acfe/meta/compile/{$post_id}");
  174. acf_enable_filter("acfe/meta/clean/{$post_id}");
  175. // Check store
  176. $store = acf_get_store('acfe/meta');
  177. $store->set("$post_id:acf", array());
  178. }
  179. /*
  180. * acf/save_post:999
  181. */
  182. function save_post($post_id = 0){
  183. if(!acf_is_filter_enabled("acfe/meta/compile/{$post_id}"))
  184. return;
  185. // Validate Post ID
  186. $validate = $this->validate_post_id($post_id);
  187. if(!$validate)
  188. return;
  189. $store = $this->get_store($post_id);
  190. $acf = $store->get("$post_id:acf");
  191. $this->update_meta('acf', $acf, $post_id);
  192. // Clean
  193. if(acf_is_filter_enabled("acfe/meta/clean/{$post_id}")){
  194. acf_enable_filter('acfe/meta/native_load');
  195. $meta = acf_get_meta($post_id);
  196. acf_disable_filter('acfe/meta/native_load');
  197. // Bail early if no meta to clean
  198. if(empty($meta))
  199. return;
  200. acf_enable_filter('acfe/meta/native_delete');
  201. foreach($meta as $key => $value){
  202. // bail if not ACF field
  203. if(!isset($meta["_$key"]))
  204. continue;
  205. // Bail early if exists in Single Value array
  206. if(isset($acf[$key]))
  207. continue;
  208. acf_delete_metadata($post_id, $key);
  209. acf_delete_metadata($post_id, $key, true);
  210. }
  211. acf_disable_filter('acfe/meta/native_delete');
  212. }
  213. }
  214. function validate_post_id($post_id){
  215. // Type + ID
  216. extract(acf_decode_post_id($post_id));
  217. // Validate ID
  218. if(!$id)
  219. return false;
  220. // Post Type
  221. if($type === 'post'){
  222. if($this->post_types === false)
  223. return false;
  224. $post_type = get_post_type($id);
  225. if(in_array($post_type, $this->restricted))
  226. return false;
  227. if(!empty($this->post_types) && !in_array($post_type, $this->post_types))
  228. return false;
  229. return true;
  230. // Taxonomy
  231. }elseif($type === 'term'){
  232. if($this->taxonomies === false)
  233. return false;
  234. $term = get_term($id);
  235. if(is_a($term, 'WP_Term')){
  236. $taxonomy = $term->taxonomy;
  237. if(!empty($this->taxonomies) && !in_array($taxonomy, $this->taxonomies))
  238. return false;
  239. return true;
  240. }
  241. // Option
  242. }elseif($type === 'option'){
  243. if($this->options === false)
  244. return false;
  245. if(!empty($this->options) && !in_array($id, $this->options))
  246. return false;
  247. return true;
  248. }
  249. return false;
  250. }
  251. function get_store($post_id){
  252. // Check store.
  253. $store = acf_get_store('acfe/meta');
  254. // Store found
  255. if(!$store->has("$post_id:acf")){
  256. // Get meta
  257. $acf = $this->get_meta('acf', $post_id);
  258. // Set Store: ACF meta
  259. $store->set("$post_id:acf", $acf);
  260. }
  261. return $store;
  262. }
  263. function get_meta($name, $post_id){
  264. // Decode $post_id for $type and $id.
  265. extract(acf_decode_post_id($post_id));
  266. // Get option
  267. if($type === 'option'){
  268. $value = get_option($id, null);
  269. // Get meta
  270. }else{
  271. $value = acf_get_metadata($post_id, $name);
  272. }
  273. return $value;
  274. }
  275. function update_meta($name, $value, $post_id){
  276. // Decode $post_id for $type and $id.
  277. extract(acf_decode_post_id($post_id));
  278. // Update option
  279. if($type === 'option'){
  280. $value = wp_unslash($value);
  281. $autoload = (bool) acf_get_setting('autoload');
  282. return update_option($id, $value, $autoload);
  283. // Update meta
  284. }else{
  285. return acf_update_metadata($post_id, $name, $value);
  286. }
  287. }
  288. /*
  289. * Field Setting
  290. */
  291. function field_setting($field){
  292. // Settings
  293. acf_render_field_setting($field, array(
  294. 'label' => __('Save as individual meta'),
  295. 'key' => 'acfe_save_meta',
  296. 'name' => 'acfe_save_meta',
  297. 'instructions' => __('Save the field as an individual meta.'),
  298. 'type' => 'true_false',
  299. 'required' => false,
  300. 'conditional_logic' => false,
  301. 'wrapper' => array(
  302. 'width' => '',
  303. 'class' => '',
  304. 'id' => '',
  305. ),
  306. 'message' => '',
  307. 'default_value' => false,
  308. 'ui' => true,
  309. 'ui_on_text' => '',
  310. 'ui_off_text' => '',
  311. ));
  312. }
  313. function load_post(){
  314. if($this->post_types === false)
  315. return;
  316. // globals
  317. global $typenow;
  318. $post_type = $typenow;
  319. if(in_array($post_type, $this->restricted))
  320. return;
  321. if(!empty($this->post_types) && !in_array($post_type, $this->post_types))
  322. return;
  323. // actions
  324. add_action('add_meta_boxes', array($this, 'add_meta_boxes'), 10, 2);
  325. }
  326. function add_meta_boxes($post_type, $post){
  327. add_meta_box('acfe-clean-meta', 'ACF Single Meta', array($this, 'render_metabox'), $post_type, 'side', 'core');
  328. }
  329. function load_term(){
  330. if($this->taxonomies === false)
  331. return;
  332. $screen = get_current_screen();
  333. $taxonomy = $screen->taxonomy;
  334. if(!empty($this->taxonomies) && !in_array($taxonomy, $this->taxonomies))
  335. return;
  336. // actions
  337. add_action("{$taxonomy}_edit_form", array($this, 'edit_term'), 20, 2);
  338. }
  339. function edit_term($term, $taxonomy){
  340. add_meta_box('acfe-clean-meta', 'ACF Single Meta', array($this, 'render_metabox'), 'edit-' . $term->taxonomy, 'side', 'core');
  341. }
  342. function load_user(){
  343. add_meta_box('acfe-clean-meta', 'ACF Single Meta', array($this, 'render_metabox'), 'user-edit', 'side', 'default');
  344. }
  345. /*
  346. function load_options($page){
  347. if($this->options === false)
  348. return;
  349. if(!empty($this->options) && !in_array($page['post_id'], $this->options))
  350. return;
  351. add_meta_box('acfe-clean-meta', 'ACF Single Meta', array($this, 'render_metabox'), 'acf_options_page', 'side', 'default');
  352. }
  353. */
  354. /*
  355. * WP Nav Menu
  356. * Unlash values after values updated
  357. */
  358. function wp_nav_menu_item_custom_fields(){
  359. acf_enable_filter('acfe/meta/unslash');
  360. }
  361. function render_metabox($post, $metabox){
  362. $field = array(
  363. 'key' => false,
  364. 'label' => false,
  365. 'name' => 'acfe_clean_meta',
  366. 'prefix' => false,
  367. 'type' => 'true_false',
  368. 'instructions' => '',
  369. 'required' => 0,
  370. 'conditional_logic' => 0,
  371. 'wrapper' => array(
  372. 'width' => '',
  373. 'class' => '',
  374. 'id' => '',
  375. ),
  376. 'message' => 'Clean orphan meta data',
  377. 'default_value' => 0,
  378. 'ui' => 0,
  379. 'ui_on_text' => '',
  380. 'ui_off_text' => '',
  381. );
  382. acf_render_field_wrap($field);
  383. ?>
  384. <script type="text/javascript">
  385. if( typeof acf !== 'undefined' ) {
  386. acf.newPostbox({
  387. 'id': 'acfe-clean-meta',
  388. 'label': 'top'
  389. });
  390. }
  391. </script>
  392. <?php
  393. }
  394. }
  395. new acfe_single_meta();
  396. endif;