PluginProbe ʕ •ᴥ•ʔ
Simple Page Ordering / 2.2.3
Simple Page Ordering v2.2.3
2.3.1 2.3.2 2.3.3 2.3.4 2.4.0 2.4.1 2.4.2 2.4.3 2.4.4 2.5.0 2.5.1 2.6.0 2.6.1 2.6.2 2.6.3 2.7.0 2.7.1 2.7.2 2.7.3 2.7.4 2.8.0 trunk 0.8.4 0.9 0.9.1 0.9.5 0.9.6 1.0 2.0 2.1 2.1.1 2.1.2 2.2 2.2.1 2.2.2 2.2.3 2.2.4 2.3
simple-page-ordering / simple-page-ordering.php
simple-page-ordering Last commit date
localization 11 years ago readme.txt 11 years ago simple-page-ordering.css 11 years ago simple-page-ordering.dev.js 11 years ago simple-page-ordering.js 11 years ago simple-page-ordering.php 11 years ago
simple-page-ordering.php
316 lines
1 <?php
2 /*
3 Plugin Name: Simple Page Ordering
4 Plugin URI: http://10up.com/plugins/simple-page-ordering-wordpress/
5 Description: Order your pages and hierarchical post types using drag and drop on the built in page list. For further instructions, open the "Help" tab on the Pages screen.
6 Version: 2.2.3
7 Author: Jake Goldman, 10up
8 Author URI: http://10up.com
9 License: GPLv2 or later
10 Text Domain: simple-page-ordering
11 Domain Path: /localization/
12 */
13
14 if ( ! class_exists( 'Simple_Page_Ordering' ) ) :
15
16 class Simple_Page_Ordering {
17
18 /**
19 * Handles initializing this class and returning the singleton instance after it's been cached.
20 *
21 * @return null|Simple_page_Ordering
22 */
23 public static function get_instance() {
24 // Store the instance locally to avoid private static replication
25 static $instance = null;
26
27 if ( null === $instance ) {
28 $instance = new self();
29 self::_add_actions();
30 }
31
32 return $instance;
33 }
34
35 /**
36 * An empty constructor
37 */
38 public function __construct() { /* Purposely do nothing here */ }
39
40 /**
41 * Handles registering hooks that initialize this plugin.
42 */
43 public static function _add_actions() {
44 add_action( 'load-edit.php', array( __CLASS__, 'load_edit_screen' ) );
45 add_action( 'wp_ajax_simple_page_ordering', array( __CLASS__, 'ajax_simple_page_ordering' ) );
46 add_action( 'plugins_loaded', array( __CLASS__, 'load_textdomain' ) );
47 }
48
49 /**
50 * Loads the plugin textdomain
51 */
52 public static function load_textdomain() {
53 load_plugin_textdomain( 'simple-page-ordering', false, dirname( plugin_basename( __FILE__ ) ) . '/localization/' );
54 }
55
56 /**
57 * Load up page ordering scripts for the edit screen
58 */
59 public static function load_edit_screen() {
60 $screen = get_current_screen();
61 $post_type = $screen->post_type;
62
63 // is post type sortable?
64 $sortable = ( post_type_supports( $post_type, 'page-attributes' ) || is_post_type_hierarchical( $post_type ) ); // check permission
65 if ( ! $sortable = apply_filters( 'simple_page_ordering_is_sortable', $sortable, $post_type ) ) {
66 return;
67 }
68
69 // does user have the right to manage these post objects?
70 if ( ! self::check_edit_others_caps( $post_type ) ) {
71 return;
72 }
73
74 add_filter( 'views_' . $screen->id, array( __CLASS__, 'sort_by_order_link' ) ); // add view by menu order to views
75 add_action( 'wp', array( __CLASS__, 'wp' ) );
76 add_action( 'admin_head', array( __CLASS__, 'admin_head' ) );
77 }
78
79 /**
80 * when we load up our posts query, if we're actually sorting by menu order, initialize sorting scripts
81 */
82 public static function wp() {
83 $orderby = get_query_var('orderby');
84 if ( ( is_string( $orderby ) && 0 === strpos( $orderby, 'menu_order' ) ) || ( isset( $orderby['menu_order'] ) && $orderby['menu_order'] == 'ASC' ) ) {
85 $script_name = ( defined( 'SCRIPT_DEBUG' ) && SCRIPT_DEBUG ) ? 'simple-page-ordering.dev.js' : 'simple-page-ordering.js';
86 wp_enqueue_script( 'simple-page-ordering', plugins_url( $script_name, __FILE__ ), array('jquery-ui-sortable'), '2.1', true );
87 wp_enqueue_style( 'simple-page-ordering', plugins_url( 'simple-page-ordering.css', __FILE__ ) );
88 }
89 }
90
91 /**
92 * Add page ordering help to the help tab
93 */
94 public static function admin_head() {
95 $screen = get_current_screen();
96 $screen->add_help_tab(array(
97 'id' => 'simple_page_ordering_help_tab',
98 'title' => 'Simple Page Ordering',
99 'content' => '<p>' . __( 'To reposition an item, simply drag and drop the row by "clicking and holding" it anywhere (outside of the links and form controls) and moving it to its new position.', 'simple-page-ordering' ) . '</p>',
100 ));
101 }
102
103 public static function ajax_simple_page_ordering() {
104 // check and make sure we have what we need
105 if ( empty( $_POST['id'] ) || ( !isset( $_POST['previd'] ) && !isset( $_POST['nextid'] ) ) ) {
106 die(-1);
107 }
108
109 // real post?
110 if ( ! $post = get_post( $_POST['id'] ) ) {
111 die(-1);
112 }
113
114 // does user have the right to manage these post objects?
115 if ( ! self::check_edit_others_caps( $post->post_type ) ) {
116 die(-1);
117 }
118
119 // badly written plug-in hooks for save post can break things
120 if ( ! defined( 'WP_DEBUG' ) || ! WP_DEBUG ) {
121 error_reporting( 0 );
122 }
123
124 global $wp_version;
125
126 $previd = empty( $_POST['previd'] ) ? false : (int) $_POST['previd'];
127 $nextid = empty( $_POST['nextid'] ) ? false : (int) $_POST['nextid'];
128 $start = empty( $_POST['start'] ) ? 1 : (int) $_POST['start'];
129 $excluded = empty( $_POST['excluded'] ) ? array( $post->ID ) : array_filter( (array) $_POST['excluded'], 'intval' );
130
131 $new_pos = array(); // store new positions for ajax
132 $return_data = new stdClass;
133
134 do_action( 'simple_page_ordering_pre_order_posts', $post, $start );
135
136 // attempt to get the intended parent... if either sibling has a matching parent ID, use that
137 $parent_id = $post->post_parent;
138 $next_post_parent = $nextid ? wp_get_post_parent_id( $nextid ) : false;
139 if ( $previd == $next_post_parent ) { // if the preceding post is the parent of the next post, move it inside
140 $parent_id = $next_post_parent;
141 } elseif ( $next_post_parent !== $parent_id ) { // otherwise, if the next post's parent isn't the same as our parent, we need to study
142 $prev_post_parent = $previd ? wp_get_post_parent_id( $previd ) : false;
143 if ( $prev_post_parent !== $parent_id ) { // if the previous post is not our parent now, make it so!
144 $parent_id = ( $prev_post_parent !== false ) ? $prev_post_parent : $next_post_parent;
145 }
146 }
147 // if the next post's parent isn't our parent, it might as well be false (irrelevant to our query)
148 if ( $next_post_parent !== $parent_id ) {
149 $nextid = false;
150 }
151
152 $max_sortable_posts = (int) apply_filters( 'simple_page_ordering_limit', 50 ); // should reliably be able to do about 50 at a time
153 if ( $max_sortable_posts < 5 ) { // don't be ridiculous!
154 $max_sortable_posts = 50;
155 }
156
157 // we need to handle all post stati, except trash (in case of custom stati)
158 $post_stati = get_post_stati(array(
159 'show_in_admin_all_list' => true,
160 ));
161
162 $siblings_query = array(
163 'depth' => 1,
164 'posts_per_page' => $max_sortable_posts,
165 'post_type' => $post->post_type,
166 'post_status' => $post_stati,
167 'post_parent' => $parent_id,
168 'orderby' => array( 'menu_order' => 'ASC', 'title' => 'ASC' ),
169 'post__not_in' => $excluded,
170 'update_post_term_cache' => false,
171 'update_post_meta_cache' => false,
172 'suppress_filters' => true,
173 'ignore_sticky_posts' => true,
174 );
175 if ( version_compare( $wp_version, '4.0', '<' ) ) {
176 $siblings_query['orderby'] = 'menu_order title';
177 $siblings_query['order'] = 'ASC';
178 }
179 $siblings = new WP_Query( $siblings_query ); // fetch all the siblings (relative ordering)
180
181 // don't waste overhead of revisions on a menu order change (especially since they can't *all* be rolled back at once)
182 remove_action( 'pre_post_update', 'wp_save_post_revision' );
183
184 foreach( $siblings->posts as $sibling ) :
185
186 // don't handle the actual post
187 if ( $sibling->ID === $post->ID ) {
188 continue;
189 }
190
191 // if this is the post that comes after our repositioned post, set our repositioned post position and increment menu order
192 if ( $nextid === $sibling->ID ) {
193 wp_update_post(array(
194 'ID' => $post->ID,
195 'menu_order' => $start,
196 'post_parent' => $parent_id,
197 ));
198 $ancestors = get_post_ancestors( $post->ID );
199 $new_pos[$post->ID] = array(
200 'menu_order' => $start,
201 'post_parent' => $parent_id,
202 'depth' => count( $ancestors ),
203 );
204 $start++;
205 }
206
207 // if repositioned post has been set, and new items are already in the right order, we can stop
208 if ( isset( $new_pos[$post->ID] ) && $sibling->menu_order >= $start ) {
209 $return_data->next = false;
210 break;
211 }
212
213 // set the menu order of the current sibling and increment the menu order
214 if ( $sibling->menu_order != $start ) {
215 wp_update_post(array(
216 'ID' => $sibling->ID,
217 'menu_order' => $start,
218 ));
219 }
220 $new_pos[$sibling->ID] = $start;
221 $start++;
222
223 if ( !$nextid && $previd == $sibling->ID ) {
224 wp_update_post(array(
225 'ID' => $post->ID,
226 'menu_order' => $start,
227 'post_parent' => $parent_id
228 ));
229 $ancestors = get_post_ancestors( $post->ID );
230 $new_pos[$post->ID] = array(
231 'menu_order' => $start,
232 'post_parent' => $parent_id,
233 'depth' => count($ancestors) );
234 $start++;
235 }
236
237 endforeach;
238
239 // max per request
240 if ( !isset( $return_data->next ) && $siblings->max_num_pages > 1 ) {
241 $return_data->next = array(
242 'id' => $post->ID,
243 'previd' => $previd,
244 'nextid' => $nextid,
245 'start' => $start,
246 'excluded' => array_merge( array_keys( $new_pos ), $excluded ),
247 );
248 } else {
249 $return_data->next = false;
250 }
251
252 do_action( 'simple_page_ordering_ordered_posts', $post, $new_pos );
253
254 if ( ! $return_data->next ) {
255 // if the moved post has children, we need to refresh the page (unless we're continuing)
256 $children = get_posts(array(
257 'numberposts' => 1,
258 'post_type' => $post->post_type,
259 'post_status' => $post_stati,
260 'post_parent' => $post->ID,
261 'fields' => 'ids',
262 'update_post_term_cache' => false,
263 'update_post_meta_cache' => false,
264 ));
265
266 if ( ! empty( $children ) ) {
267 die( 'children' );
268 }
269 }
270
271 $return_data->new_pos = $new_pos;
272
273 die( json_encode( $return_data ) );
274 }
275
276 /**
277 * Append a sort by order link to the post actions
278 *
279 * @param string $views
280 * @return string
281 */
282 public static function sort_by_order_link( $views ) {
283 $class = ( get_query_var('orderby') == 'menu_order title' ) ? 'current' : '';
284 $query_string = esc_url( remove_query_arg( array( 'orderby', 'order' ) ) );
285 if ( ! is_post_type_hierarchical( get_post_type() ) ) {
286 $query_string = add_query_arg( 'orderby', urlencode( 'menu_order title' ), $query_string );
287 $query_string = add_query_arg( 'order', 'asc', $query_string );
288 }
289 $views['byorder'] = sprintf('<a href="%s" class="%s">%s</a>', $query_string, $class, __("Sort by Order", 'simple-page-ordering'));
290
291 return $views;
292 }
293
294 /**
295 * Checks to see if the current user has the capability to "edit others" for a post type
296 *
297 * @param string $post_type Post type name
298 * @return bool True or false
299 */
300 private static function check_edit_others_caps( $post_type ) {
301 $post_type_object = get_post_type_object( $post_type );
302 $edit_others_cap = empty( $post_type_object ) ? 'edit_others_' . $post_type . 's' : $post_type_object->cap->edit_others_posts;
303 return apply_filters( 'simple_page_ordering_edit_rights', current_user_can( $edit_others_cap ), $post_type );
304 }
305 }
306
307 Simple_Page_Ordering::get_instance();
308
309 endif;
310
311 // dummy, to be used by poedit
312 if (false) {
313 // Plugin description
314 __('Order your pages and hierarchical post types using drag and drop on the built in page list. For further instructions, open the "Help" tab on the Pages screen.', 'simple-page-ordering');
315 }
316