PluginProbe
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets / 4.5.3
Ultimate Post Kit – Elementor Post Grid, Post Carousel, Post Slider & Blog Layout Widgets v4.5.3
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 4.5.3, at includes/controls/select-input/dynamic-select-input-module.php

330 lines 8.1 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 // phpcs:ignore WordPress.Security.NonceVerification.Missing -- nonce verified in getSelectInputData() before this helper runs.
100 return isset($_POST['ids']) ? sanitize_text_field(wp_unslash($_POST['ids'])) : [];
101 }
102
103
104 /**
105 * @param string $taxonomy
106 *
107 * @return mixed|string
108 */
109 public function getTaxonomyName($taxonomy = '') {
110 $taxonomies = get_taxonomies(['public' => true], 'objects');
111 $taxonomies = array_column($taxonomies, 'label', 'name');
112
113 return isset($taxonomies[$taxonomy]) ? $taxonomies[$taxonomy] : '';
114 }
115
116 /**
117 * @return string[]|\WP_Taxonomy[]
118 */
119 protected function getAllPublicTaxonomies() {
120 return array_values(get_taxonomies(['public' => true]));
121 }
122
123 /**
124 * Get Post Query Data
125 *
126 * @return array
127 */
128 public function getPosts() {
129 $include = $this->getselecedIds();
130 $searchText = $this->getSearchQuery();
131
132 $args = [];
133
134 $args['post_status'] = 'publish';
135
136 $public_post_types = $this->getAllPublicPostTypes();
137 $requested_post_type = $this->getPostType();
138
139 // post_type comes straight from $_POST. Restrict it to the public post types this
140 // control is meant to browse so it cannot be pointed at a private post type.
141 if ($requested_post_type && in_array($requested_post_type, $public_post_types, true)) {
142 $args['post_type'] = $requested_post_type;
143 } else {
144 $args['post_type'] = $public_post_types;
145 }
146
147 if (!empty($include)) {
148 $args['post__in'] = $include;
149 $args['posts_per_page'] = min(100, count($include));
150 } else {
151 // Never run this unbounded: it is reachable by any 'edit_posts' user and
152 // -1 returns every published post of every public post type.
153 $args['posts_per_page'] = 50;
154 }
155 if ($searchText) {
156 $args['s'] = $searchText;
157 }
158
159 $query = new \WP_Query($args);
160 $results = [];
161 foreach ($query->posts as $post) {
162 $post_type_obj = get_post_type_object($post->post_type);
163 if (!empty($data['include_type'])) {
164 $text = $post_type_obj->labels->name . ': ' . $post->post_title;
165 } else {
166 $text = ($post_type_obj->hierarchical) ? $this->get_post_name_with_parents($post) : $post->post_title;
167 }
168
169 $results[] = [
170 'id' => $post->ID,
171 'text' => esc_html($text),
172 ];
173 }
174
175 return $results;
176 }
177
178 private function get_post_name_with_parents($post, $max = 3) {
179 if (0 === $post->post_parent) {
180 return $post->post_title;
181 }
182 $separator = is_rtl() ? ' < ' : ' > ';
183 $test_post = $post;
184 $names = [];
185 while ($test_post->post_parent > 0) {
186 $test_post = get_post($test_post->post_parent);
187 if (!$test_post) {
188 break;
189 }
190 $names[] = $test_post->post_title;
191 }
192
193 $names = array_reverse($names);
194 if (count($names) < ($max)) {
195 return implode($separator, $names) . $separator . $post->post_title;
196 }
197
198 $name_string = '';
199 for ($i = 0; $i < ($max - 1); $i++) {
200 $name_string .= $names[$i] . $separator;
201 }
202 return $name_string . '...' . $separator . $post->post_title;
203 }
204
205 /**
206 * Get Terms query data
207 *
208 * @return array
209 */
210 public function getTerms() {
211 $search_text = $this->getSearchQuery();
212 $taxonomies = $this->getAllPublicTaxonomies();
213 $include = $this->getselecedIds();
214
215 if ($this->getPostType() == '_ultimate_post_kit_pro_related_post_type') {
216 $post_type = $this->getAllPublicPostTypes();
217 } elseif ($this->getPostType()) {
218 $post_type = $this->getPostType();
219 }
220
221 $post_taxonomies = get_object_taxonomies($post_type);
222 $taxonomies = array_intersect($post_taxonomies, $taxonomies);
223 $data = [];
224
225 if (empty($taxonomies)) {
226 return $data;
227 }
228
229 $args = [
230 'taxonomy' => $taxonomies,
231 'hide_empty' => true,
232 ];
233
234 if (!empty($include)) {
235 $args['include'] = $include;
236 }
237
238 if ($search_text) {
239 $args['number'] = 20;
240 $args['search'] = $search_text;
241 }
242
243 $terms = get_terms($args);
244
245 if (is_wp_error($terms) || empty($terms)) {
246 return $data;
247 }
248
249 foreach ($terms as $term) {
250 $label = $term->name;
251 $taxonomy_name = $this->getTaxonomyName($term->taxonomy);
252
253 if ($taxonomy_name) {
254 $label = "{$taxonomy_name}: {$label}";
255 }
256
257 $data[] = [
258 'id' => $term->term_taxonomy_id,
259 'text' => $label,
260 ];
261 }
262
263 return $data;
264 }
265
266 /**
267 * Get Authors query Data
268 *
269 * @return array
270 */
271 public function getAuthors() {
272 $include = $this->getselecedIds();
273 $search_text = $this->getSearchQuery();
274
275 $args = [
276 'fields' => ['ID', 'display_name'],
277 'orderby' => 'display_name',
278 // Always bound the result set. Without this an empty search returns every
279 // user on the site, unpaged.
280 'number' => 20,
281 ];
282
283 // This endpoint is only capability-gated on 'edit_posts', so a Contributor can
284 // reach it. WordPress core restricts callers without 'list_users' to users who
285 // have published something (see WP_REST_Users_Controller::get_items), so match
286 // that restriction rather than exposing the full user table.
287 if (!current_user_can('list_users')) {
288 $args['has_published_posts'] = true;
289 }
290
291 if (!empty($include)) {
292 $args['include'] = $include;
293 // Resolving already-selected values needs room for all of them, but still
294 // bounded so a long id list cannot be used to dump the table.
295 $args['number'] = min(100, max(20, count($include)));
296 }
297
298 if ($search_text) {
299 $args['search'] = "*$search_text*";
300 // WP_User_Query searches user_email when the term contains "@", which turns
301 // this into an address oracle. Core strips user_email from the searchable
302 // columns for callers without 'list_users'; do the same here.
303 $args['search_columns'] = ['ID', 'user_login', 'user_nicename', 'display_name'];
304 }
305
306 $users = get_users($args);
307
308 $data = [];
309
310 if (empty($users)) {
311 return $data;
312 }
313
314 foreach ($users as $user) {
315 $data[] = [
316 'id' => $user->ID,
317 'text' => $user->display_name,
318 ];
319 }
320
321 return $data;
322 }
323 }
324
325 function ultimatePostKit_dynamic_select_input_module() {
326
327 return UltimatePostKit_Dynamic_Select_Input_Module::get_instance();
328 }
329 ultimatePostKit_dynamic_select_input_module()->init();
330