/include/ACFQuickEdit/Admin/CurrentView.php

https://github.com/mcguffin/acf-quick-edit-fields · PHP · 404 lines · 221 code · 82 blank · 101 comment · 82 complexity · 3374759f7765272d940e8ca0ee10a895 MD5 · raw file

  1. <?php
  2. /**
  3. * @package ACFQuickEdit\Admin
  4. * @version 1.0.0
  5. * 2018-09-22
  6. */
  7. namespace ACFQuickEdit\Admin;
  8. if ( ! defined('ABSPATH') ) {
  9. die('FU!');
  10. }
  11. use ACFQuickEdit\Ajax;
  12. use ACFQuickEdit\Asset;
  13. use ACFQuickEdit\Core;
  14. use ACFQuickEdit\Fields;
  15. class CurrentView extends Core\Singleton {
  16. private $_available_field_groups = null;
  17. private $object_kind = null; // post, term, user
  18. private $object_type = null; // post, term, user
  19. private $screen_param = []; // post_type|taxonomy
  20. private $field_group_filter = null;
  21. private $field_to_group = [];
  22. /**
  23. * @inheritdoc
  24. */
  25. protected function __construct() {
  26. /**
  27. * Setup object_kind, screen_param and object_type
  28. */
  29. if ( wp_doing_ajax() ) {
  30. // get content type by $_REQUEST['action']
  31. if ( isset( $_REQUEST['action'] ) ) {
  32. $this->screen_param = $this->referer_params();
  33. if ( in_array( $_REQUEST['action'], apply_filters( 'acf_quick_edit_post_ajax_actions', [ 'inline-save' ] ) ) ) {
  34. $this->object_kind = 'post';
  35. } else if ( in_array( $_REQUEST['action'], apply_filters('acf_quick_edit_term_ajax_actions', [ 'inline-save-tax' ] ) ) ) {
  36. $this->object_kind = 'term';
  37. }
  38. }
  39. } else {
  40. $wp_screen = get_current_screen();
  41. if ( 'edit' === $wp_screen->base ) {
  42. $this->object_kind = 'post';
  43. if ( isset( $_REQUEST['action'] ) && 'edit' === $_REQUEST['action'] ) {
  44. // bulk edit save
  45. // screen opts in $_REQUEST['_wp_http_referer'];
  46. $this->screen_param = $this->referer_params();
  47. } else {
  48. $this->screen_param = $this->get_params();
  49. }
  50. } else if ( 'upload' === $wp_screen->base ) {
  51. $this->object_kind = 'post';
  52. $this->screen_param = $this->get_params();
  53. } else if ( 'edit-tags' === $wp_screen->base ) {
  54. $this->object_kind = 'term';
  55. $this->screen_param = $this->get_params();
  56. } else if ( 'users' === $wp_screen->base ) {
  57. $this->object_kind = 'user';
  58. $this->object_type = null;
  59. $this->screen_param = $this->get_params();
  60. }
  61. }
  62. // set screen param defaults
  63. if ( $this->object_kind === 'post' ) {
  64. $this->screen_param = wp_parse_args( $this->screen_param, [
  65. 'post_type' => 'post',
  66. ] );
  67. } else if ( $this->object_kind === 'term' ) {
  68. $this->screen_param = wp_parse_args( $this->screen_param, [
  69. 'taxonomy' => 'post_tag',
  70. ] );
  71. // no post type on taxonomies!
  72. $this->screen_param = array_diff_key( $this->screen_param, [ 'post_type' => 0 ] );
  73. } else if ( $this->object_kind === 'user' ) {
  74. }
  75. $this->object_type = $this->get_object_type(); // current taxonomy
  76. }
  77. /**
  78. * @return string 'post', 'term', 'user'
  79. */
  80. public function get_object_kind() {
  81. return $this->object_kind;
  82. }
  83. /**
  84. * @return string|null post type slug or taxonomy name. NULL on users screen
  85. */
  86. public function get_object_type() {
  87. if ( is_null( $this->object_type ) && 'user' !== $this->object_kind ) {
  88. if ( 'term' === $this->object_kind ) {
  89. $this->object_type = 'post_tag';
  90. foreach ( $this->screen_param as $param => $value ) {
  91. if ( 'cat' === $param ) {
  92. $this->object_type = 'post_category';
  93. break;
  94. } else if ( 'tag' === $param ) {
  95. $this->object_type = 'post_tag';
  96. break;
  97. } else if ( 'taxonomy' === $param ) {
  98. $this->object_type = $value;
  99. } else if ( taxonomy_exists( $param ) ) {
  100. $this->object_type = $param;
  101. break;
  102. }
  103. }
  104. } else if ( 'post' === $this->object_kind ) {
  105. $this->object_type = 'post';
  106. foreach ( $this->screen_param as $param => $value ) {
  107. if ( 'post_type' === $param ) {
  108. $this->object_type = $value;
  109. break;
  110. }
  111. }
  112. }
  113. }
  114. return $this->object_type;
  115. }
  116. /**
  117. * Calculate field group filter for current screen
  118. *
  119. * @return array filter for acf_getfield_groups
  120. */
  121. private function get_fieldgroup_filter() {
  122. if ( is_null( $this->field_group_filter ) ) {
  123. $this->field_group_filter = [];
  124. foreach ( $this->screen_param as $param => $value ) {
  125. if ( 'post_type' === $param && ! empty( $value ) ) {
  126. $this->field_group_filter['post_type'] = $value;
  127. } else if ( 'attachment-filter' === $param ) {
  128. $filtered_type = urldecode( $param );
  129. $filtered_type = substr( $filtered_type, strpos( $filtered_type, ':' ) + 1 );
  130. $this->field_group_filter['attachment'] = $filtered_type;
  131. } else if ( in_array( $param, [ 'cat', 'tag' ] ) && ! empty( $value ) ) {
  132. // post_category
  133. $this->field_group_filter['post_taxonomy'] = sprintf( 'post_%s:%s', $param, $value );
  134. } else if ( taxonomy_exists( $param ) && ! empty( $value ) ) {
  135. // post_taxonomy => <taxo>:<term_slug>
  136. $this->field_group_filter['post_taxonomy'] = sprintf( '%s:%s', $param, $value );
  137. } else if ( 'taxonomy' === $param && ! empty( $value ) ) {
  138. $this->field_group_filter['taxonomy'] = $value;
  139. } else if ( 'role' === $param && ! empty( $value ) ) {
  140. //*
  141. $this->field_group_filter[] = [ 'user_form' => 'all', 'user_role' => $value ];
  142. $this->field_group_filter[] = [ 'user_form' => 'edit', 'user_role' => $value ];
  143. /*/
  144. $this->field_group_filter[] = array( 'user_role' => $value );
  145. //*/
  146. }
  147. }
  148. if ( 'user' === $this->object_kind && ! count( $this->field_group_filter ) ) {
  149. $this->field_group_filter[] = [ 'user_form' => 'all' ];
  150. $this->field_group_filter[] = [ 'user_form' => 'edit' ];
  151. }
  152. add_filter( 'acf/location/rule_match/post_taxonomy', [ $this, 'match_post_taxonomy' ], 11, 3 );
  153. add_filter( 'acf/location/rule_match/post_format', [ $this, 'match_post_format' ], 11, 3 );
  154. add_filter( 'acf/location/rule_match/post_status', [ $this, 'match_post_status' ], 11, 3 );
  155. add_filter( 'acf/location/rule_match/attachment', [ $this, 'match_attachment' ], 11, 3 );
  156. }
  157. /*
  158. ACF get_field_groups filter:
  159. [ 'post_type' => 'post' ] --> matches post type
  160. [ [ 'post_type' => 'post' ] ] --> doesn't match anything
  161. [ 'post_type' => 'post', 'taxonomy' => 'post_tag' ] --> matches post type OR Taxo
  162. [ [ 'post_type' => 'post', 'taxonomy' => 'post_tag' ] ] --> matches post type AND Taxo
  163. */
  164. // if ( $this->is_assoc( $this->field_group_filter ) && count( $this->field_group_filter ) > 1 ) {
  165. // $this->field_group_filter = array( $this->field_group_filter );
  166. // }
  167. return apply_filters( 'acf_quick_edit_fields_group_filter', $this->field_group_filter );
  168. }
  169. /**
  170. * Return field associated with current view
  171. *
  172. * @param array $query Field properties
  173. * @return array
  174. */
  175. public function get_fields( $query = [] ) {
  176. $groups = $this->get_available_field_groups();
  177. $fields = [];
  178. foreach ( $groups as $field_group ) {
  179. $group_fields = acf_get_fields( $field_group );
  180. $group_fields = $this->filter_fields( $query, $group_fields );
  181. foreach ( $group_fields as $field ) {
  182. // map to group
  183. $this->field_to_group[ $field['key'] ] = $field_group;
  184. }
  185. $fields = array_merge( $fields, $group_fields );
  186. }
  187. return $fields;
  188. }
  189. /**
  190. * Return fields by properties
  191. *
  192. * @param array $query Field properties
  193. * @param array $fields
  194. * @return array
  195. */
  196. private function filter_fields( $query, $fields ) {
  197. $found_fields = [];
  198. foreach ( $fields as $field ) {
  199. $match = true;
  200. if ( 'group' === $field['type'] ) {
  201. $found_fields = array_merge( $found_fields, $this->filter_fields( $query, $field['sub_fields'] ) );
  202. }
  203. foreach ( $query as $prop => $value ) {
  204. if ( is_bool( $value ) ) {
  205. $value = intval($value);
  206. }
  207. if ( ! isset( $field[ $prop ] ) || $field[ $prop ] !== $value ) {
  208. $match = false;
  209. break;
  210. }
  211. }
  212. if ( $match ) {
  213. $found_fields[] = $field;
  214. }
  215. }
  216. return $found_fields;
  217. }
  218. /**
  219. * Return field groups relevant for the current view
  220. *
  221. * @return array acf field gruops
  222. */
  223. private function get_available_field_groups() {
  224. global $typenow, $pagenow;
  225. if ( is_null( $this->_available_field_groups ) ) {
  226. $filters = $this->get_fieldgroup_filter();
  227. $this->_available_field_groups = acf_get_field_groups( $filters );
  228. }
  229. return $this->_available_field_groups;
  230. }
  231. /**
  232. * Whether an arrays has string keys
  233. *
  234. * @param array $arr
  235. * @return boolean
  236. */
  237. private function is_assoc( $arr ) {
  238. if( ! is_array( $arr ) ) {
  239. trigger_error( 'Argument should be an array for is_assoc()', E_USER_WARNING );
  240. return false;
  241. }
  242. return count( array_filter( array_keys( $arr ), 'is_string' ) ) > 0;
  243. }
  244. /**
  245. * Get field group of field
  246. *
  247. * @param array $field ACF Field
  248. * @return array ACF field group
  249. */
  250. public function get_group_of_field( $field ) {
  251. if ( isset( $this->field_to_group[ $field['key'] ] ) ) {
  252. return $this->field_to_group[ $field['key'] ];
  253. }
  254. }
  255. /**
  256. * @filter 'acf/location/rule_match/post_taxonomy'
  257. * @return boolean Whether a field group rule matches
  258. */
  259. function match_post_taxonomy( $match, $rule, $screen ) {
  260. if ( isset( $screen['post_taxonomy'] ) ) {
  261. // WP categories
  262. return $rule['operator'] == '==' && $rule['value'] == $screen['post_taxonomy'];
  263. }
  264. return $match;
  265. }
  266. /**
  267. * @filter 'acf/location/rule_match/post_format'
  268. * @return boolean Whether a field group rule matches
  269. */
  270. function match_post_format( $match, $rule, $screen ) {
  271. if ( isset( $screen['post_format'] ) ) {
  272. return $rule['operator'] == '==' && $rule['value'] == $screen['post_format'];
  273. }
  274. return $match;
  275. }
  276. /**
  277. * @filter 'acf/location/rule_match/post_status'
  278. * @return boolean Whether a field group rule matches
  279. */
  280. function match_post_status( $match, $rule, $options ) {
  281. if ( isset( $screen['post_status'] ) ) {
  282. return $rule['operator'] == '==' && $rule['value'] == $screen['post_status'];
  283. }
  284. return $match;
  285. }
  286. /**
  287. * @filter 'acf/location/rule_match/attachment'
  288. * @return boolean Whether a field group rule matches
  289. */
  290. function match_attachment( $match, $rule, $options ) {
  291. if ( isset( $screen['attachment'] ) ) {
  292. return $rule['operator'] == '==' && $rule['value'] == $screen['attachment'];
  293. }
  294. return $match;
  295. }
  296. /**
  297. * Set screen params from http referer (prefer _wp_http_referer)
  298. *
  299. * @return array $_GET-Params of $_REQUEST['_wp_http_referer']
  300. */
  301. private function referer_params() {
  302. $url = false;
  303. $filter = [];
  304. if ( isset( $_REQUEST['_wp_http_referer'] ) ) {
  305. $url = wp_unslash( $_REQUEST['_wp_http_referer'] );
  306. } else if ( isset( $_SERVER['HTTP_REFERER'] ) ) {
  307. $url = wp_unslash( $_SERVER['HTTP_REFERER'] );
  308. }
  309. if ( $url ) {
  310. parse_str( parse_url( $url, PHP_URL_QUERY ), $filter );
  311. }
  312. return $filter;
  313. }
  314. /**
  315. * Set screen params from $_GET
  316. *
  317. * @return array $_GET-Params
  318. */
  319. private function get_params() {
  320. return $_GET;
  321. }
  322. }