PluginProbe
Adminify – White Label, Admin Menu Editor, Login Customizer / 4.2.21
Adminify – White Label, Admin Menu Editor, Login Customizer v4.2.21
4.3.1 4.3.0 4.2.26 4.2.25 4.2.24 4.2.23 4.2.22 4.2.21 4.2.20 4.2.19 4.2.18 4.2.17 4.2.16 4.2.15 4.2.14 4.2.13 4.2.12 4.2.11 4.2.10 4.2.9 4.2.8 4.2.7 4.2.6 4.2.5 4.1.17 All 164 releases
adminify / Inc / Modules / PostTypesOrder / PostTypesOrder.php

PostTypesOrder.php in Adminify – White Label, Admin Menu Editor, Login Customizer 4.2.21, at Inc/Modules/PostTypesOrder/PostTypesOrder.php

729 lines 25.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2
3 namespace PXLBSAdminify\Inc\Modules\PostTypesOrder;
4
5 use PXLBSAdminify\Inc\Admin\AdminSettings;
6 use PXLBSAdminify\Inc\Admin\AdminSettingsModel;
7 use PXLBSAdminify\Inc\Modules\PostTypesOrder\PostTypesOrderWalker;
8
9 // no direct access allowed
10 if ( ! defined( 'ABSPATH' ) ) {
11 exit;
12 }
13
14 /**
15 * WP Adminify
16 *
17 * @package WP Adminify: Post Types Order
18 *
19 * @author WP Adminify <support@wpadminify.com>
20 */
21
22 class PostTypesOrder extends AdminSettingsModel {
23 public function __construct() {
24 $this->options = (array) AdminSettings::get_instance()->get('post_types_order');
25
26 // Admin Init
27 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only check, no state change.
28 if ( empty( $_GET ) ) {
29 add_action( 'admin_init', [ $this, 'refresh' ] );
30 }
31
32 add_action( 'admin_init', [ $this, 'pto_load_scripts' ] );
33 add_action( 'admin_enqueue_scripts', [ $this, 'pto_css' ], 99 );
34
35 // sortable ajax action
36 add_action( 'wp_ajax_pxlbsadminify_update_post_types_order', [ $this, 'pto_update_order' ] );
37 add_action( 'wp_ajax_pxlbsadminify_update_post_types_taxonomy_order', [ $this, 'pto_update_taxonomy' ] );
38
39 // reorder post types
40 add_action( 'pre_get_posts', [ $this, 'pto_pre_get_posts' ] );
41
42 // Order the admin taxonomy list (edit-tags.php) by term_order so
43 // drag-sorted taxonomies persist visibly. Front-end term ordering
44 // stays a Pro feature (Pro/Classes/Filters.php).
45 add_filter( 'get_terms', [ $this, 'pto_admin_order_terms' ], 10, 1 );
46
47 add_filter( 'get_previous_post_where', array( $this, 'pto_previous_post_where' ) );
48 add_filter( 'get_previous_post_sort', [ $this, 'pto_previous_post_sort' ] );
49 add_filter( 'get_next_post_where', [ $this, 'pto_next_post_where' ] );
50 add_filter( 'get_next_post_sort', [ $this, 'pto_next_post_sort' ] );
51
52 // Taxonomy ordering (pto_taxonomies) is a Pro-only feature.
53 // The get_terms_orderby / wp_get_object_terms / get_terms
54 // filters are registered by Pro/Classes/Filters.php when the
55 // Pro plugin is active.
56
57 // reorder sites
58 if ( function_exists( 'is_multisite' ) && is_multisite() ) {
59 add_action( 'wp_ajax_pxlbsadminify_update_post_types_order_sites', [ $this, 'pto_update_sites' ] );
60
61 // networkadmin
62 if (
63 empty( $_SERVER['QUERY_STRING'] ) ||
64 ( ! empty( $_SERVER['QUERY_STRING'] ) &&
65 'action=deleteblog' !== $_SERVER['QUERY_STRING'] && // delete
66 'action=allblogs' !== $_SERVER['QUERY_STRING'] // delete all
67 )
68 ) {
69
70 // call from 'get_sites'
71 add_filter( 'sites_clauses', [ $this, 'pto_sites_clauses' ], 10, 1 );
72
73 add_action( 'admin_init', [ $this, 'pto_refresh_network' ] );
74
75 // adminbar sites reorder
76 add_filter( 'get_blogs_of_user', [ $this, 'pto_get_blogs_of_user' ], 10, 3 );
77 }
78 }
79 }
80
81 public function pto_media_list( $args = '' ) {
82 $defaults = [
83 'depth' => -1,
84 'date_format' => get_option( 'date_format' ),
85 'child_of' => 0,
86 'sort_column' => 'menu_order',
87 'post_status' => 'any',
88 ];
89
90 $r = wp_parse_args( $args, $defaults );
91 extract( $r, EXTR_SKIP );
92
93 $output = '';
94
95 $r['exclude'] = implode( ',', apply_filters( 'wp_list_pages_excludes', [] ) ); // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.PostNotIn_exclude -- small admin page-exclude list, not a VIP-scale query.
96
97 // Query pages.
98 $r['hierarchical'] = 0;
99 $args = [
100 'sort_column' => 'menu_order',
101 'post_type' => $post_type,
102 'posts_per_page' => -1,
103 'post_status' => 'any',
104 'orderby' => [
105 'menu_order' => 'ASC',
106 'post_date' => 'DESC',
107 ],
108 ];
109
110 $the_query = new \WP_Query( $args );
111 $pages = $the_query->posts;
112
113 if ( ! empty( $pages ) ) {
114 $output .= $this->walkTree( $pages, $r['depth'], $r );
115 }
116
117 echo wp_kses_post( $output );
118 }
119
120 public function walkTree( $pages, $depth, $r ) {
121 $walker = new PostTypesOrderWalker();
122
123 $args = [ $pages, $depth, $r ];
124 return call_user_func_array( [ &$walker, 'walk' ], $args );
125 }
126
127
128 public function refresh() {
129 global $wpdb;
130 $objects = $this->pto_get_order_objects();
131
132 if ( ! empty( $objects ) ) {
133 foreach ( $objects as $object ) {
134 $status_sql = $this->pto_post_status_sql( $object );
135 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- direct query required for menu_order aggregate over posts; status list is a hardcoded literal, not user input.
136 $result = $wpdb->get_results(
137 $wpdb->prepare(
138 "SELECT count(*) as cnt, max(menu_order) as max, min(menu_order) as min
139 FROM $wpdb->posts
140 WHERE post_type = %s AND post_status IN ($status_sql)",
141 $object
142 )
143 );
144 if ( $result[0]->cnt == 0 || $result[0]->cnt == $result[0]->max ) {
145 continue;
146 }
147
148 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- direct query required to fetch ordered post IDs; status list is a hardcoded literal, not user input.
149 $results = $wpdb->get_results(
150 $wpdb->prepare(
151 "SELECT ID
152 FROM $wpdb->posts
153 WHERE post_type = %s AND post_status IN ($status_sql)
154 ORDER BY menu_order ASC",
155 $object
156 )
157 );
158 foreach ( $results as $key => $result ) {
159 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required to persist menu_order; not cached intentionally.
160 $wpdb->update( $wpdb->posts, [ 'menu_order' => $key + 1 ], [ 'ID' => $result->ID ] );
161 }
162 }
163 }
164 }
165
166
167
168 function pto_pre_get_posts( $wp_query ) {
169 $objects = $this->pto_get_order_objects();
170 if ( empty( $objects ) ) {
171 return false;
172 }
173
174 /**
175 * for Admin
176 * @default
177 * post pto: [order] => null(desc) [orderby] => null(date)
178 * page: [order] => asc [orderby] => menu_order title
179 */
180
181 if ( is_admin() ) {
182 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only check, no state change.
183 if ( isset( $wp_query->query['post_type'] ) && ! isset( $_GET['orderby'] ) ) {
184 // Don't reorder the ajax media modal / grid (query-attachments);
185 // only the upload.php list view, which is a normal page load.
186 if ( 'attachment' === $wp_query->query['post_type'] && wp_doing_ajax() ) {
187 return;
188 }
189 if ( in_array( $wp_query->query['post_type'], $objects ) ) {
190 $wp_query->set( 'orderby', 'menu_order' );
191 $wp_query->set( 'order', 'ASC' );
192 }
193 }
194 } else {
195 $active = false;
196
197 // page or custom post types
198 if ( isset( $wp_query->query['post_type'] ) ) {
199 // exclude array()
200 if ( ! is_array( $wp_query->query['post_type'] ) ) {
201 if ( in_array( $wp_query->query['post_type'], $objects ) ) {
202 $active = true;
203 }
204 }
205 // post
206 } else {
207 if ( in_array( 'post', $objects ) ) {
208 $active = true;
209 }
210 }
211
212 if ( ! $active ) {
213 return false;
214 }
215
216 // get_posts()
217 if ( isset( $wp_query->query['suppress_filters'] ) ) {
218 if ( $wp_query->get( 'orderby' ) == 'date' || $wp_query->get( 'orderby' ) == 'menu_order' ) {
219 $wp_query->set( 'orderby', 'menu_order' );
220 $wp_query->set( 'order', 'ASC' );
221 } elseif ( $wp_query->get( 'orderby' ) == 'default_date' ) {
222 $wp_query->set( 'orderby', 'date' );
223 }
224 // WP_Query( contain main_query )
225 } else {
226 if (
227 ! $wp_query->get( 'orderby' )
228 ) {
229 $wp_query->set( 'orderby', 'menu_order' );
230 }
231 if (
232 ! $wp_query->get( 'order' )
233 ) {
234 $wp_query->set( 'order', 'ASC' );
235 }
236 }
237 }
238 }
239
240
241 public function pto_update_order() {
242 // Security check - verify nonce and capability
243 check_ajax_referer( 'pxlbsadminify-pto-security-nonce', 'security' );
244
245 if ( ! current_user_can( 'edit_others_posts' ) ) {
246 wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'adminify' ) ) );
247 }
248
249 global $wpdb;
250 $data = [];
251 $order = isset( $_POST['order'] ) ? sanitize_text_field( wp_unslash( $_POST['order'] ) ) : '';
252 parse_str( $order, $data );
253
254 if ( ! is_array( $data ) ) {
255 return false;
256 }
257
258 // get objects per now page
259 $id_arr = [];
260 foreach ( $data as $key => $values ) {
261 foreach ( $values as $position => $id ) {
262 $id_arr[] = $id;
263 }
264 }
265
266 // get menu_order of objects per now page
267 $menu_order_arr = [];
268 foreach ( $id_arr as $key => $id ) {
269 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required to read menu_order by ID; not cached intentionally.
270 $results = $wpdb->get_results( "SELECT menu_order FROM $wpdb->posts WHERE ID = " . intval( $id ) );
271 foreach ( $results as $result ) {
272 $menu_order_arr[] = $result->menu_order;
273 }
274 }
275
276 // maintains key association = no
277 sort( $menu_order_arr );
278
279 foreach ( $data as $key => $values ) {
280 foreach ( $values as $position => $id ) {
281 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required to persist menu_order; not cached intentionally.
282 $wpdb->update( $wpdb->posts, [ 'menu_order' => $menu_order_arr[ $position ] ], [ 'ID' => intval( $id ) ] );
283 }
284 }
285
286 // same number check
287 $post_type = get_post_type( $id );
288 $status_sql = $this->pto_post_status_sql( $post_type );
289 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- direct query required for menu_order duplicate detection; status list is a hardcoded literal, not user input.
290 $results = $wpdb->get_results(
291 $wpdb->prepare(
292 "SELECT COUNT(menu_order) AS mo_count, post_type, menu_order FROM $wpdb->posts
293 WHERE post_type = %s AND post_status IN ($status_sql)
294 GROUP BY post_type, menu_order HAVING (mo_count) > 1",
295 $post_type
296 )
297 );
298 if ( count( $results ) > 0 ) {
299 $sort_ids = [];
300
301 // menu_order refresh
302 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching,WordPress.DB.PreparedSQL.InterpolatedNotPrepared -- direct query required to fetch ordered post IDs; status list is a hardcoded literal, not user input.
303 $results = $wpdb->get_results(
304 $wpdb->prepare(
305 "SELECT ID, menu_order FROM $wpdb->posts
306 WHERE post_type = %s AND post_status IN ($status_sql)
307 ORDER BY menu_order",
308 $post_type
309 )
310 );
311 foreach ( $results as $key => $result ) {
312 $view_posi = array_search( $result->ID, $id_arr, true );
313 if ( $view_posi === false ) {
314 $view_posi = 999;
315 }
316 $sort_key = ( $result->menu_order * 1000 ) + $view_posi;
317 $sort_ids[ $sort_key ] = $result->ID;
318 }
319 ksort( $sort_ids );
320 $oreder_no = 0;
321 foreach ( $sort_ids as $key => $id ) {
322 $oreder_no = $oreder_no + 1;
323 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required to persist menu_order; not cached intentionally.
324 $wpdb->update( $wpdb->posts, [ 'menu_order' => $oreder_no ], [ 'ID' => intval( $id ) ] );
325 }
326 }
327 }
328
329 public function pto_update_taxonomy() {
330 // Security check - verify nonce and capability
331 check_ajax_referer( 'pxlbsadminify-pto-security-nonce', 'security' );
332
333 if ( ! current_user_can( 'manage_categories' ) ) {
334 wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'adminify' ) ) );
335 }
336
337 global $wpdb;
338
339 $order = isset( $_POST['order'] ) ? sanitize_text_field( wp_unslash( $_POST['order'] ) ) : '';
340 parse_str( $order, $data );
341
342 if ( ! is_array( $data ) ) {
343 return false;
344 }
345
346 $id_arr = [];
347 foreach ( $data as $key => $values ) {
348 foreach ( $values as $position => $id ) {
349 $id_arr[] = $id;
350 }
351 }
352
353 $menu_order_arr = [];
354 foreach ( $id_arr as $key => $id ) {
355 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required to read term_order by ID; not cached intentionally.
356 $results = $wpdb->get_results( "SELECT term_order FROM $wpdb->terms WHERE term_id = " . intval( $id ) );
357 foreach ( $results as $result ) {
358 $menu_order_arr[] = $result->term_order;
359 }
360 }
361 sort( $menu_order_arr );
362
363 foreach ( $data as $key => $values ) {
364 foreach ( $values as $position => $id ) {
365 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required to persist term_order; not cached intentionally.
366 $wpdb->update( $wpdb->terms, [ 'term_order' => $menu_order_arr[ $position ] ], [ 'term_id' => intval( $id ) ] );
367 }
368 }
369
370 // same number check
371 $term = get_term( $id );
372 $taxonomy = $term->taxonomy;
373 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required for term_order duplicate detection; not cached intentionally.
374 $results = $wpdb->get_results(
375 $wpdb->prepare(
376 "SELECT COUNT(term_order) AS to_count, term_order
377 FROM $wpdb->terms AS terms
378 INNER JOIN $wpdb->term_taxonomy AS term_taxonomy ON ( terms.term_id = term_taxonomy.term_id )
379 WHERE term_taxonomy.taxonomy = %s GROUP BY taxonomy, term_order HAVING (to_count) > 1",
380 $taxonomy
381 )
382 );
383 if ( count( $results ) > 0 ) {
384 $sort_ids = [];
385 // term_order refresh
386 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required to fetch ordered term IDs; not cached intentionally.
387 $results = $wpdb->get_results(
388 $wpdb->prepare(
389 "SELECT terms.term_id, term_order
390 FROM $wpdb->terms AS terms
391 INNER JOIN $wpdb->term_taxonomy AS term_taxonomy ON ( terms.term_id = term_taxonomy.term_id )
392 WHERE term_taxonomy.taxonomy = %s
393 ORDER BY term_order ASC",
394 $taxonomy
395 )
396 );
397 foreach ( $results as $key => $result ) {
398 $view_posi = array_search( $result->term_id, $id_arr, true );
399 if ( $view_posi === false ) {
400 $view_posi = 999;
401 }
402 $sort_key = ( $result->term_order * 1000 ) + $view_posi;
403 $sort_ids[ $sort_key ] = $result->term_id;
404 }
405 ksort( $sort_ids );
406 $oreder_no = 0;
407 foreach ( $sort_ids as $key => $id ) {
408 $oreder_no = $oreder_no + 1;
409 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required to persist term_order; not cached intentionally.
410 $wpdb->update( $wpdb->terms, [ 'term_order' => $oreder_no ], [ 'term_id' => $id ] );
411 }
412 }
413
414 do_action( 'pxlbsadminify_update_post_types_order_taxonomy' );
415 }
416
417 public function pto_load_scripts() {
418 global $pagenow;
419
420 if ( $this->conditional_script_load() || ( $pagenow === 'upload.php' && ( isset( $this->options['pto_media'] ) && $this->options['pto_media'] ) ) ) {
421 wp_enqueue_script( 'jquery' );
422 wp_enqueue_script( 'jquery-ui-sortable' );
423 wp_enqueue_script( 'adminify-post-type-order', PXLBSADMINIFY_URL . 'Inc/Modules/PostTypesOrder/js/post-type-order.js', [ 'jquery' ], PXLBSADMINIFY_VER, true );
424 wp_localize_script( 'adminify-post-type-order', 'PXLBSADMINIFY_PTO', array(
425 'nonce' => wp_create_nonce( 'pxlbsadminify-pto-security-nonce' ),
426 ) );
427 }
428 }
429
430 public function pto_css() {
431 global $pagenow;
432 if ( $this->conditional_script_load() || ( $pagenow === 'upload.php' && ( ! empty( $this->options['pto_media'] ) ) ) ) {
433 echo '<!-- Start of Post Type and Taxonomy Order --->';
434 echo '<style type="text/css">';
435 echo '.adminify-table-responsive-wrapper .ui-sortable tr:hover{cursor:move}.adminify-table-responsive-wrapper .ui-sortable tr.alternate{background-color:#f9f9f9 !important}.adminify-table-responsive-wrapper .ui-sortable tr.ui-sortable-helper{background-color:#f9f9f9 !important;border-top:1px solid #dfdfdf;border-bottom:1px solid #dfdfdf;box-shadow:0 -5px 5px -4px rgba(0,0,0,.1),0 5px 5px -3px rgba(0,0,0,.1)}.adminify-table-responsive-wrapper .ui-sortable-placeholder{height:50px;background-color:#e9e9e9;border:2px dashed #dfdfdf;visibility:visible!important}.adminify-table-responsive-wrapper .wp-list-table{table-layout:fixed;width:100%;border-collapse:collapse}.adminify-table-responsive-wrapper.wp-list-table td,.adminify-table-responsive-wrapper .wp-list-table th{padding:8px 16px;text-align:left;white-space:nowrap;word-wrap:break-word;overflow:hidden}.adminify-table-responsive-wrapper.wp-list-table .column-title,.wp-list-table td.column-title,.adminify-table-responsive-wrapper{overflow-x:auto;-webkit-overflow-scrolling:touch}@media screen and (max-width:768px){.adminify-table-responsive-wrapper .wp-list-table td,.adminify-table-responsive-wrapper.wp-list-table th{padding:8px}}';
436 echo '</style>';
437 echo '<!-- End of Post Type and Taxonomy Order --->';
438 }
439 }
440
441 public function conditional_script_load() {
442 global $pagenow;
443
444 $active = false;
445
446 // Multisite > Sites
447 if (
448 function_exists( 'is_multisite' )
449 && is_multisite()
450 && $pagenow == 'sites.php'
451 ) {
452 return true;
453 }
454
455 $objects = $this->pto_get_options();
456 $taxonomies = $this->pto_get_taxonomy_options();
457
458 if ( empty( $objects ) && empty( $taxonomies ) ) {
459 return false;
460 }
461
462 // exclude (sorting, addnew page, edit page)
463 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only check, no state change.
464 if ( isset( $_GET['orderby'] ) || ( ! empty( $_SERVER['REQUEST_URI'] ) && strstr( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), 'action=edit' ) || strstr( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), 'wp-admin/post-new.php' ) ) ) {
465 return false;
466 }
467
468 if ( ! empty( $objects ) ) {
469 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only check, no state change.
470 if ( isset( $_GET['post_type'] ) && ! isset( $_GET['taxonomy'] ) && in_array( sanitize_text_field( wp_unslash( $_GET['post_type'] ) ), $objects ) ) { // if page or custom post types
471 $active = true;
472 }
473 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only check, no state change.
474 if ( ! isset( $_GET['post_type'] ) && ( ! empty( $_SERVER['REQUEST_URI'] && strstr( sanitize_text_field( wp_unslash( $_SERVER['REQUEST_URI'] ) ), 'wp-admin/edit.php' ) ) && in_array( 'post', $objects ) ) ) {
475 // if post
476 $active = true;
477 }
478 }
479
480 // Taxonomy list page (edit-tags.php) — Sortable Taxonomies.
481 if ( ! empty( $taxonomies ) ) {
482 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only check, no state change.
483 if ( 'edit-tags.php' === $pagenow && isset( $_GET['taxonomy'] ) && in_array( sanitize_text_field( wp_unslash( $_GET['taxonomy'] ) ), $taxonomies, true ) ) {
484 $active = true;
485 }
486 }
487
488 return $active;
489 }
490
491 function pto_get_options() {
492 $objects = isset( $this->options['pto_posts'] ) && is_array( $this->options['pto_posts'] ) ? $this->options['pto_posts'] : [];
493 return $objects;
494 }
495
496 /**
497 * get_terms filter — order the admin taxonomy list by term_order for
498 * taxonomies the user opted into. Admin only; bails when a column sort
499 * (orderby) is active. Front-end ordering remains a Pro feature.
500 */
501 public function pto_admin_order_terms( $terms ) {
502 if ( ! is_admin() || empty( $terms ) || ! is_array( $terms ) ) {
503 return $terms;
504 }
505 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only check, no state change.
506 if ( isset( $_GET['orderby'] ) ) {
507 return $terms;
508 }
509
510 $taxonomies = $this->pto_get_taxonomy_options();
511 if ( empty( $taxonomies ) ) {
512 return $terms;
513 }
514
515 // Only reorder when every returned term is an object in an opted-in
516 // taxonomy (get_terms may return IDs or mixed taxonomies).
517 foreach ( $terms as $term ) {
518 if ( ! is_object( $term ) || ! isset( $term->taxonomy ) || ! in_array( $term->taxonomy, $taxonomies, true ) ) {
519 return $terms;
520 }
521 }
522
523 usort( $terms, [ $this, 'pto_taxonomy_compare' ] );
524 return $terms;
525 }
526
527 // Stable comparator on term_order.
528 public function pto_taxonomy_compare( $a, $b ) {
529 $ao = isset( $a->term_order ) ? (int) $a->term_order : 0;
530 $bo = isset( $b->term_order ) ? (int) $b->term_order : 0;
531 if ( $ao === $bo ) {
532 return 0;
533 }
534 return ( $ao < $bo ) ? -1 : 1;
535 }
536
537 // Post types whose menu_order should be seeded/reordered.
538 // Includes 'attachment' when Sortable Media Files is enabled.
539 function pto_get_order_objects() {
540 $objects = $this->pto_get_options();
541 if ( ! empty( $this->options['pto_media'] ) && ! in_array( 'attachment', $objects, true ) ) {
542 $objects[] = 'attachment';
543 }
544 return $objects;
545 }
546
547 // Status SQL fragment per post type. Attachments live under
548 // 'inherit', so they are excluded by the default status list.
549 function pto_post_status_sql( $post_type ) {
550 if ( 'attachment' === $post_type ) {
551 return "'inherit'";
552 }
553 return "'publish', 'pending', 'draft', 'private', 'future'";
554 }
555
556 function pto_get_taxonomy_options() {
557 $taxonomies = isset( $this->options['pto_taxonomies'] ) && is_array( $this->options['pto_taxonomies'] ) ? $this->options['pto_taxonomies'] : [];
558 return $taxonomies;
559 }
560
561 function pto_previous_post_where( $where ) {
562 global $post;
563
564 $objects = $this->pto_get_options();
565 if ( empty( $objects ) ) {
566 return $where;
567 }
568
569 if ( isset( $post->post_type ) && in_array( $post->post_type, $objects ) ) {
570 $current_menu_order = $post->menu_order;
571 $where = str_replace( "p.post_date < '" . $post->post_date . "'", "p.menu_order > '" . $current_menu_order . "'", $where );
572 }
573 return $where;
574 }
575
576 function pto_previous_post_sort( $orderby ) {
577 global $post;
578
579 $objects = $this->pto_get_options();
580 if ( empty( $objects ) ) {
581 return $orderby;
582 }
583
584 if ( isset( $post->post_type ) && in_array( $post->post_type, $objects ) ) {
585 $orderby = 'ORDER BY p.menu_order ASC LIMIT 1';
586 }
587 return $orderby;
588 }
589
590
591 function pto_next_post_where( $where ) {
592 global $post;
593
594 $objects = $this->pto_get_options();
595 if ( empty( $objects ) ) {
596 return $where;
597 }
598
599 if ( isset( $post->post_type ) && in_array( $post->post_type, $objects ) ) {
600 $current_menu_order = $post->menu_order;
601 $where = str_replace( "p.post_date > '" . $post->post_date . "'", "p.menu_order < '" . $current_menu_order . "'", $where );
602 }
603 return $where;
604 }
605
606 function pto_next_post_sort( $orderby ) {
607 global $post;
608
609 $objects = $this->pto_get_options();
610 if ( empty( $objects ) ) {
611 return $orderby;
612 }
613
614 if ( isset( $post->post_type ) && in_array( $post->post_type, $objects ) ) {
615 $orderby = 'ORDER BY p.menu_order DESC LIMIT 1';
616 }
617 return $orderby;
618 }
619
620
621 public function pto_sites_clauses( $pieces = [] ) {
622 if ( is_admin() ) {
623 return $pieces;
624 }
625
626 global $wp_version;
627 if ( version_compare( $wp_version, '4.6.0' ) >= 0 ) {
628 if ( 'blog_id ASC' === $pieces['orderby'] ) {
629 $pieces['orderby'] = 'menu_order ASC';
630 }
631 }
632 return $pieces;
633 }
634
635 public function pto_update_sites() {
636 // Security check - verify nonce and capability
637 check_ajax_referer( 'pxlbsadminify-pto-security-nonce', 'security' );
638
639 if ( ! current_user_can( 'manage_sites' ) ) {
640 wp_send_json_error( array( 'message' => __( 'You do not have permission to perform this action.', 'adminify' ) ) );
641 }
642
643 global $wpdb;
644
645 $order = isset( $_POST['order'] ) ? sanitize_text_field( wp_unslash( $_POST['order'] ) ) : '';
646 parse_str( $order, $data );
647
648 if ( ! is_array( $data ) ) {
649 return false;
650 }
651
652 $id_arr = [];
653 foreach ( $data as $key => $values ) {
654 foreach ( $values as $position => $id ) {
655 $id_arr[] = $id;
656 }
657 }
658
659 foreach ( $data as $key => $values ) {
660 foreach ( $values as $position => $id ) {
661 // phpcs:ignore WordPress.DB.DirectDatabaseQuery.DirectQuery,WordPress.DB.DirectDatabaseQuery.NoCaching -- direct query required to persist blog menu_order; not cached intentionally.
662 $wpdb->update( $wpdb->prefix . 'blogs', [ 'menu_order' => $position + 1 ], [ 'blog_id' => intval( $id ) ] );
663 }
664 }
665
666 die();
667 }
668
669 public function pto_refresh_network() {
670 global $pagenow;
671 // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- read-only check, no state change.
672 if ( 'sites.php' === $pagenow && ! isset( $_GET['orderby'] ) ) {
673 add_filter( 'query', [ $this, 'pto_refresh_network_second' ] );
674 }
675 }
676
677
678 public function pto_refresh_network_second( $query ) {
679 global $wpdb, $wp_version, $blog_id;
680
681 // $wpdb->get_varやswitch_to_blog(1)
682 if ( version_compare( $wp_version, '4.7.0' ) >= 0 ) {
683 if ( 1 !== $blog_id ) {
684 return $query;
685 }
686 }
687
688 if (
689 false !== strpos( $query, "SELECT * FROM $wpdb->blogs WHERE site_id = '1'" ) ||
690 false !== strpos( $query, "SQL_CALC_FOUND_ROWS blog_id FROM $wpdb->blogs WHERE site_id = 1" )
691 ) {
692 if ( false !== strpos( $query, ' LIMIT ' ) ) {
693 $query = preg_replace( '/^(.*) LIMIT(.*)$/', '$1 ORDER BY menu_order ASC LIMIT $2', $query );
694 } else {
695 $query .= ' ORDER BY menu_order ASC';
696 }
697 }
698 return $query;
699 }
700
701
702 public function pto_get_blogs_of_user( $blogs ) {
703 $sites = get_sites( [] );
704 $sort_keys = [];
705 foreach ( $sites as $k => $v ) {
706 $sort_keys[] = $v->menu_order;
707 }
708 array_multisort( $sort_keys, SORT_ASC, $sites );
709
710 $blog_list = [];
711 foreach ( $blogs as $k => $v ) {
712 $blog_list[ $v->userblog_id ] = $v;
713 }
714
715 $new = [];
716 foreach ( $sites as $k => $v ) {
717 if (
718 isset( $v->blog_id ) &&
719 isset( $blog_list[ $v->blog_id ] ) &&
720 is_object( $blog_list[ $v->blog_id ] )
721 ) {
722 $new[] = $blog_list[ $v->blog_id ];
723 }
724 }
725
726 return $new;
727 }
728 }
729