PluginProbe
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets / trunk
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets vtrunk
4.5.4 4.2.1 4.2.2 4.2.3 4.5.0 4.5.2 4.5.3 4.2.0 4.1.18 4.1.17 4.1.16 4.1.15 4.1.14 4.1.13 4.1.12 4.1.11 4.1.10 4.1.9 4.1.8 4.0.9 4.1.0 4.1.1 4.1.2 4.1.3 4.1.4 All 146 releases
ultimate-post-kit / includes / controls / select-input / dynamic-select-input-module.php

dynamic-select-input-module.php in Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets trunk, at includes/controls/select-input/dynamic-select-input-module.php

343 lines 8.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace UltimatePostKit\Includes\Controls\SelectInput;
4
5 defined('ABSPATH') || die();
6 class UltimatePostKit_Dynamic_Select_Input_Module {
7
8 const ACTION = '';
9
10 private static $instance = null;
11
12 /**
13 * Returns the instance.
14 *
15 * @return object
16 * @since 1.0.0
17 */
18 public static function get_instance() {
19 // If the single instance hasn't been set, set it now.
20 if (null == self::$instance) {
21 self::$instance = new self;
22 }
23
24 return self::$instance;
25 }
26
27 /**
28 * Init method
29 */
30
31 /**
32 * Constructor.
33 */
34 public function init() {
35 add_action('wp_ajax_upk_dynamic_select_input_data', array($this, 'getSelectInputData'));
36 }
37
38 /**
39 * get Ajax Data
40 */
41 public function getSelectInputData() {
42 $nonce = isset($_POST['security']) ? sanitize_text_field(wp_unslash($_POST['security'])) : '';
43
44 try {
45 if (!wp_verify_nonce($nonce, 'upk_dynamic_select')) {
46 throw new \Exception('Invalid request');
47 }
48
49 if (!current_user_can('edit_posts')) {
50 throw new \Exception('Unauthorized request');
51 }
52
53 $query = isset($_POST['query']) ? sanitize_text_field(wp_unslash($_POST['query'])) : '';
54
55 if ($query == 'terms') {
56 $data = $this->getTerms();
57 } else if ($query == 'authors') {
58 $data = $this->getAuthors();
59 } else {
60 $data = $this->getPosts();
61 }
62
63 wp_send_json_success($data);
64 } catch (\Exception $e) {
65 wp_send_json_error($e->getMessage());
66 }
67
68 die();
69 }
70
71 /**
72 * Get Post Type
73 * @return string
74 */
75 protected function getPostType() {
76 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in getSelectInputData() before this helper runs.
77 return isset($_POST['post_type']) ? sanitize_text_field(wp_unslash($_POST['post_type'])) : '';
78 }
79
80 /**
81 * @return string[]|\WP_Post_Type[]
82 */
83 protected function getAllPublicPostTypes() {
84 return array_values(get_post_types(['public' => true]));
85 }
86
87 /**
88 * @return string
89 */
90 protected function getSearchQuery() {
91 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in getSelectInputData() before this helper runs.
92 return isset($_POST['search_text']) ? sanitize_text_field(wp_unslash($_POST['search_text'])) : '';
93 }
94
95 /**
96 * @return array|mixed
97 */
98 protected function getselecedIds() {
99 if ( ! check_ajax_referer( 'upk_dynamic_select', 'security', false ) ) {
100 return [];
101 }
102
103 if ( ! isset( $_POST['ids'] ) ) {
104 return [];
105 }
106
107 return array_values( array_filter( wp_parse_id_list( (array) wp_unslash( $_POST['ids'] ) ) ) );
108 }
109
110
111 /**
112 * @param string $taxonomy
113 *
114 * @return mixed|string
115 */
116 public function getTaxonomyName($taxonomy = '') {
117 $taxonomies = get_taxonomies(['public' => true], 'objects');
118 $taxonomies = array_column($taxonomies, 'label', 'name');
119
120 return isset($taxonomies[$taxonomy]) ? $taxonomies[$taxonomy] : '';
121 }
122
123 /**
124 * @return string[]|\WP_Taxonomy[]
125 */
126 protected function getAllPublicTaxonomies() {
127 return array_values(get_taxonomies(['public' => true]));
128 }
129
130 /**
131 * Get Post Query Data
132 *
133 * @return array
134 */
135 public function getPosts() {
136 $include = $this->getselecedIds();
137 $searchText = $this->getSearchQuery();
138
139 $args = [];
140
141 $args['post_status'] = 'publish';
142
143 $public_post_types = $this->getAllPublicPostTypes();
144 $requested_post_type = $this->getPostType();
145
146 // post_type comes straight from $_POST. Restrict it to the public post types this
147 // control is meant to browse so it cannot be pointed at a private post type.
148 if ($requested_post_type && in_array($requested_post_type, $public_post_types, true)) {
149 $args['post_type'] = $requested_post_type;
150 } else {
151 $args['post_type'] = $public_post_types;
152 }
153
154 if (!empty($include)) {
155 $args['post__in'] = $include;
156 $args['posts_per_page'] = min(100, count($include));
157 } else {
158 // Never run this unbounded: it is reachable by any 'edit_posts' user and
159 // -1 returns every published post of every public post type.
160 $args['posts_per_page'] = 50;
161 }
162 if ($searchText) {
163 $args['s'] = $searchText;
164 }
165
166 $query = new \WP_Query($args);
167 $results = [];
168 foreach ($query->posts as $post) {
169 $post_type_obj = get_post_type_object($post->post_type);
170 if (!empty($data['include_type'])) {
171 $text = $post_type_obj->labels->name . ': ' . $post->post_title;
172 } else {
173 $text = ($post_type_obj->hierarchical) ? $this->get_post_name_with_parents($post) : $post->post_title;
174 }
175
176 $results[] = [
177 'id' => $post->ID,
178 'text' => esc_html($text),
179 ];
180 }
181
182 return $results;
183 }
184
185 private function get_post_name_with_parents($post, $max = 3) {
186 if (0 === $post->post_parent) {
187 return $post->post_title;
188 }
189 $separator = is_rtl() ? ' < ' : ' > ';
190 $test_post = $post;
191 $names = [];
192 while ($test_post->post_parent > 0) {
193 $test_post = get_post($test_post->post_parent);
194 if (!$test_post) {
195 break;
196 }
197 $names[] = $test_post->post_title;
198 }
199
200 $names = array_reverse($names);
201 if (count($names) < ($max)) {
202 return implode($separator, $names) . $separator . $post->post_title;
203 }
204
205 $name_string = '';
206 for ($i = 0; $i < ($max - 1); $i++) {
207 $name_string .= $names[$i] . $separator;
208 }
209 return $name_string . '...' . $separator . $post->post_title;
210 }
211
212 /**
213 * Get Terms query data
214 *
215 * @return array
216 */
217 public function getTerms() {
218 $search_text = $this->getSearchQuery();
219 $taxonomies = $this->getAllPublicTaxonomies();
220 $include = $this->getselecedIds();
221
222 $post_type = '';
223
224 if ($this->getPostType() == '_ultimate_post_kit_pro_related_post_type') {
225 $post_type = $this->getAllPublicPostTypes();
226 } elseif ($this->getPostType()) {
227 $post_type = $this->getPostType();
228 }
229
230 if (empty($post_type)) {
231 return [];
232 }
233
234 $post_taxonomies = get_object_taxonomies($post_type);
235 $taxonomies = array_intersect($post_taxonomies, $taxonomies);
236 $data = [];
237
238 if (empty($taxonomies)) {
239 return $data;
240 }
241
242 $args = [
243 'taxonomy' => $taxonomies,
244 'hide_empty' => true,
245 ];
246
247 if (!empty($include)) {
248 $args['include'] = $include;
249 }
250
251 if ($search_text) {
252 $args['number'] = 20;
253 $args['search'] = $search_text;
254 }
255
256 $terms = get_terms($args);
257
258 if (is_wp_error($terms) || empty($terms)) {
259 return $data;
260 }
261
262 foreach ($terms as $term) {
263 $label = $term->name;
264 $taxonomy_name = $this->getTaxonomyName($term->taxonomy);
265
266 if ($taxonomy_name) {
267 $label = "{$taxonomy_name}: {$label}";
268 }
269
270 $data[] = [
271 'id' => $term->term_taxonomy_id,
272 'text' => $label,
273 ];
274 }
275
276 return $data;
277 }
278
279 /**
280 * Get Authors query Data
281 *
282 * @return array
283 */
284 public function getAuthors() {
285 $include = $this->getselecedIds();
286 $search_text = $this->getSearchQuery();
287
288 $args = [
289 'fields' => ['ID', 'display_name'],
290 'orderby' => 'display_name',
291 // Always bound the result set. Without this an empty search returns every
292 // user on the site, unpaged.
293 'number' => 20,
294 ];
295
296 // This endpoint is only capability-gated on 'edit_posts', so a Contributor can
297 // reach it. WordPress core restricts callers without 'list_users' to users who
298 // have published something (see WP_REST_Users_Controller::get_items), so match
299 // that restriction rather than exposing the full user table.
300 if (!current_user_can('list_users')) {
301 $args['has_published_posts'] = true;
302 }
303
304 if (!empty($include)) {
305 $args['include'] = $include;
306 // Resolving already-selected values needs room for all of them, but still
307 // bounded so a long id list cannot be used to dump the table.
308 $args['number'] = min(100, max(20, count($include)));
309 }
310
311 if ($search_text) {
312 $args['search'] = "*$search_text*";
313 // WP_User_Query searches user_email when the term contains "@", which turns
314 // this into an address oracle. Core strips user_email from the searchable
315 // columns for callers without 'list_users'; do the same here.
316 $args['search_columns'] = ['ID', 'user_login', 'user_nicename', 'display_name'];
317 }
318
319 $users = get_users($args);
320
321 $data = [];
322
323 if (empty($users)) {
324 return $data;
325 }
326
327 foreach ($users as $user) {
328 $data[] = [
329 'id' => $user->ID,
330 'text' => $user->display_name,
331 ];
332 }
333
334 return $data;
335 }
336 }
337
338 function ultimatePostKit_dynamic_select_input_module() {
339
340 return UltimatePostKit_Dynamic_Select_Input_Module::get_instance();
341 }
342 ultimatePostKit_dynamic_select_input_module()->init();
343