Changeset 3322001
- Timestamp:
- 07/03/2025 08:27:25 PM (9 months ago)
- Location:
- simple-menu-order-column
- Files:
-
- 21 edited
- 1 copied
-
tags/1.0.0/assets/js/simple-menu-order-column.js (modified) (1 diff)
-
tags/1.0.0/includes/class-simplemenuordercolumn.php (modified) (1 diff)
-
tags/1.0.0/simple-menu-order-column.php (modified) (2 diffs)
-
tags/1.0.1/assets/js/simple-menu-order-column.js (modified) (1 diff)
-
tags/1.0.1/includes/class-simplemenuordercolumn.php (modified) (1 diff)
-
tags/1.0.1/simple-menu-order-column.php (modified) (2 diffs)
-
tags/1.0.2/assets/js/simple-menu-order-column.js (modified) (1 diff)
-
tags/1.0.2/includes/class-simplemenuordercolumn.php (modified) (1 diff)
-
tags/1.0.2/simple-menu-order-column.php (modified) (1 diff)
-
tags/2.0.0 (copied) (copied from simple-menu-order-column/trunk)
-
tags/2.0.0/assets/js/simple-menu-order-column.js (modified) (1 diff)
-
tags/2.0.0/assets/js/simple-menu-order-column.min.js (modified) (1 diff)
-
tags/2.0.0/changelog.txt (modified) (1 diff)
-
tags/2.0.0/includes/class-simplemenuordercolumn.php (modified) (14 diffs)
-
tags/2.0.0/readme.txt (modified) (3 diffs)
-
tags/2.0.0/simple-menu-order-column.php (modified) (2 diffs)
-
trunk/assets/js/simple-menu-order-column.js (modified) (1 diff)
-
trunk/assets/js/simple-menu-order-column.min.js (modified) (1 diff)
-
trunk/changelog.txt (modified) (1 diff)
-
trunk/includes/class-simplemenuordercolumn.php (modified) (14 diffs)
-
trunk/readme.txt (modified) (3 diffs)
-
trunk/simple-menu-order-column.php (modified) (2 diffs)
Legend:
- Unmodified
- Added
- Removed
-
simple-menu-order-column/tags/1.0.0/assets/js/simple-menu-order-column.js
r3074004 r3322001 9 9 */ 10 10 (function ($) { 11 $.fn.smocDoReorder = function (currentObject) { 12 13 const reorderCurrentProduct = this; 14 15 if (!reorderCurrentProduct || $(currentObject).prop('disabled')) { 16 return false; 17 } 18 19 function disableInput(errorContainer, errorMessage, disable) { 20 /** Reset input value. */ 21 currentObject.value = currentObject.defaultValue; 22 /** Show error icon. */ 23 errorContainer && errorContainer.css('display', 'inline-block'); 24 /** Disable input field. */ 25 $(currentObject).prop('disabled', disable).prop('title', errorMessage); 26 /** Output message to widow console. */ 27 window.console.warn('[Simple Menu Order Column] ' + errorMessage); 28 }; 29 30 let reorderPostID = $(reorderCurrentProduct).data('post-id'); 31 32 if (!reorderPostID || isNaN(reorderPostID)) { 33 disableInput(null, 'Invalid Post ID.', true); 34 return false; 35 } 36 37 reorderPostID = parseInt(reorderPostID); 38 39 /** 40 * Create loader and result containers. 41 */ 42 const reorderLoaderID = 'smoc-' + reorderPostID.toString(); 43 44 const reorderResultContainer = $(reorderCurrentProduct).closest('.smoc-container'); 45 46 /** 47 * Create loader. 48 */ 49 let reorderLoaderSelector = $('#' + reorderLoaderID + '-loader'); 50 51 if (!reorderLoaderSelector.length) { 52 reorderLoaderSelector = $('<span>') 53 .attr({ 54 id: reorderLoaderID + '-loader', 55 class: 'smoc-loader dashicons dashicons-update', 56 role: 'img', 57 'aria-label': 'Updating Menu Order', 58 }) 59 .css({ 60 color: '#2ea2cc', 61 animation: 'iconrotation 2s infinite linear', 62 display: 'inline-block', 63 }); 64 } 65 66 let reorderLoaderSelectorContainer = $( 67 '#' + reorderLoaderID + '-loader-container' 68 ); 69 70 if (!reorderLoaderSelectorContainer.length) { 71 reorderLoaderSelectorContainer = $('<div>') 72 .attr({ 73 id: reorderLoaderID + '-loader-container', 74 }) 75 .css({ 76 'padding-top': '5px', 77 display: 'none', 78 }); 79 80 reorderLoaderSelectorContainer.append(reorderLoaderSelector); 81 82 reorderResultContainer.append(reorderLoaderSelectorContainer); 83 } else { 84 reorderLoaderSelectorContainer.css({ display: 'none' }); 85 } 86 87 /** 88 * Create Success result. 89 */ 90 91 let reorderSuccessSelector = $('#' + reorderLoaderID + '-success'); 92 93 if (!reorderSuccessSelector.length) { 94 reorderSuccessSelector = $('<span>') 95 .attr({ 96 id: reorderLoaderID + '-success', 97 class: 'smoc-success dashicons dashicons-yes-alt', 98 role: 'img', 99 'aria-label': 'Success', 100 }) 101 .css({ 102 'padding-top': '5px', 103 color: '#7ad03a', 104 display: 'none', 105 }); 106 107 reorderResultContainer.append(reorderSuccessSelector); 108 } else { 109 reorderSuccessSelector.css({ display: 'none' }); 110 } 111 112 /** 113 * Create Error result. 114 */ 115 116 let reorderErrorSelector = $('#' + reorderLoaderID + '-error'); 117 118 if (!reorderErrorSelector.length) { 119 reorderErrorSelector = $('<span>') 120 .attr({ 121 id: reorderLoaderID + '-error', 122 class: 'smoc-error dashicons dashicons-dismiss', 123 role: 'img', 124 'aria-label': 'Error', 125 }) 126 .css({ 127 'padding-top': '5px', 128 color: '#a00', 129 display: 'none', 130 }); 131 132 reorderResultContainer.append(reorderErrorSelector); 133 } else { 134 reorderErrorSelector.css({ display: 'none' }); 135 } 136 137 /** 138 * Check WP configuration. 139 */ 140 if (!typenow || !ajaxurl) { 141 disableInput(reorderErrorSelector, 'Invalid WP installation, variables typenow or ajaxurl not initialized.', true); 142 return false; 143 } 144 145 let reorderPostMenuOrder = $(reorderCurrentProduct).val(); 146 147 /** 148 * Populate and validate Product Order 149 */ 150 reorderPostMenuOrder = $(reorderCurrentProduct).val(); 151 152 if (!reorderPostMenuOrder || isNaN(reorderPostMenuOrder)) { 153 disableInput(reorderErrorSelector, 'Invalid menu order value.', false); 154 return false; 155 } 156 157 reorderPostMenuOrder = parseInt(reorderPostMenuOrder); 158 159 /** 160 * Populate wpnonce 161 */ 162 let postNonce = $(reorderCurrentProduct).data('wpnonce'); 163 164 if (!postNonce) { 165 disableInput(reorderErrorSelector, 'Invalid field postNonce.', true); 166 return false; 167 } 168 169 /** 170 * Disable INPUT while doing ajax 171 */ 172 $(currentObject).prop('disabled', true); 173 174 reorderLoaderSelectorContainer.css({ display: 'inline-block' }); 175 176 /** 177 * Format POST URL. 178 */ 179 const searchParams = new URLSearchParams(); 180 181 searchParams.set('action', 'smoc_reorder'); 182 searchParams.set('_wpnonce', postNonce); 183 184 const request = jQuery.ajax({ 185 url: ajaxurl + '?' + searchParams, 186 type: 'POST', 187 data: { 188 post_type: typenow, 189 post_id: reorderPostID, 190 post_menu_order: reorderPostMenuOrder, 191 }, 192 }); 193 194 request.done(function (response) { 195 if (response.success) { 196 reorderSuccessSelector.css('display', 'inline-block'); 197 198 $(currentObject).prop('title', reorderPostMenuOrder); 199 200 currentObject.currentValue = reorderPostMenuOrder; 201 currentObject.defaultValue = reorderPostMenuOrder; 202 // If success go to next product. 203 const currentObjectPosition = $(':input[id^=smoc]').index(currentObject); 204 $(':input[id^=smoc]').eq(currentObjectPosition + 1).trigger('select'); 205 } else { 206 currentObject.value = currentObject.defaultValue; 207 208 reorderErrorSelector.css('display', 'inline-block'); 209 } 210 }); 211 212 request.fail(function () { 213 currentObject.value = currentObject.defaultValue; 214 215 reorderLoaderSelectorContainer.css('display', 'none'); 216 reorderSuccessSelector.css('display', 'none'); 217 reorderErrorSelector.css('display', 'inline-block'); 218 }); 219 220 request.always(function () { 221 reorderLoaderSelectorContainer.css({ display: 'none' }); 222 223 /** Enable INPUT after doing Ajax */ 224 $(currentObject).prop('disabled', false); 225 }); 226 }; 227 228 $('input[id^=smoc]').on('focus', function () { 229 this.currentValue = this.value; 230 231 $(this).prop('title', parseInt(this.value)); 232 233 const reorderLoaderID = 'smoc-' + $(this).data('post-id').toString(); 234 235 $('#' + reorderLoaderID + '-loader-container').css({ display: 'none' }); 236 $('#' + reorderLoaderID + '-success').css({ display: 'none' }); 237 $('#' + reorderLoaderID + '-error').css({ display: 'none' }); 238 }); 239 240 $('input[id^=smoc]').on('focusout', function (e) { 241 if ($(this).prop('disabled')) { 242 return false; 243 } 244 245 if (this.currentValue !== this.value) { 246 if (window.confirm('Do you want to save the menu order?')) { 247 $(this).smocDoReorder(this); 248 } else { 249 this.value = this.defaultValue; 250 } 251 } 252 }); 253 254 $('input[id^=smoc]').on('keypress', function (e) { 255 if (e.key === 'Enter') { 256 e.preventDefault(); 257 258 $(this).smocDoReorder(this); 259 } 260 }); 11 $.fn.smocDoReorder = function (currentObject) { 12 13 const reorderCurrentProduct = this; 14 15 if (!reorderCurrentProduct || $(currentObject).prop('disabled')) { 16 return false; 17 } 18 19 function disableInput(errorContainer, errorMessage, disable) 20 { 21 /** 22 * Reset input value. 23 */ 24 currentObject.value = currentObject.defaultValue; 25 /** 26 * Show error icon. 27 */ 28 errorContainer && errorContainer.css('display', 'inline-block'); 29 /** 30 * Disable input field. 31 */ 32 $(currentObject).prop('disabled', disable).prop('title', errorMessage); 33 /** 34 * Output message to widow console. 35 */ 36 window.console.warn('[Simple Menu Order Column] ' + errorMessage); 37 }; 38 39 let reorderPostID = $(reorderCurrentProduct).data('post-id'); 40 41 if (!reorderPostID || isNaN(reorderPostID)) { 42 disableInput(null, 'Invalid Post ID.', true); 43 return false; 44 } 45 46 reorderPostID = parseInt(reorderPostID); 47 48 /** 49 * Create loader and result containers. 50 */ 51 const reorderLoaderID = 'smoc-' + reorderPostID.toString(); 52 53 const reorderResultContainer = $(reorderCurrentProduct).closest('.smoc-container'); 54 55 /** 56 * Create loader. 57 */ 58 let reorderLoaderSelector = $('#' + reorderLoaderID + '-loader'); 59 60 if (!reorderLoaderSelector.length) { 61 reorderLoaderSelector = $('<span>') 62 .attr( 63 { 64 id: reorderLoaderID + '-loader', 65 class: 'smoc-loader dashicons dashicons-update', 66 role: 'img', 67 'aria-label': 'Updating Menu Order', 68 } 69 ) 70 .css( 71 { 72 color: '#2ea2cc', 73 animation: 'iconrotation 2s infinite linear', 74 display: 'inline-block', 75 } 76 ); 77 } 78 79 let reorderLoaderSelectorContainer = $( 80 '#' + reorderLoaderID + '-loader-container' 81 ); 82 83 if (!reorderLoaderSelectorContainer.length) { 84 reorderLoaderSelectorContainer = $('<div>') 85 .attr( 86 { 87 id: reorderLoaderID + '-loader-container', 88 } 89 ) 90 .css( 91 { 92 'padding-top': '5px', 93 display: 'none', 94 } 95 ); 96 97 reorderLoaderSelectorContainer.append(reorderLoaderSelector); 98 99 reorderResultContainer.append(reorderLoaderSelectorContainer); 100 } else { 101 reorderLoaderSelectorContainer.css({ display: 'none' }); 102 } 103 104 /** 105 * Create Success result. 106 */ 107 108 let reorderSuccessSelector = $('#' + reorderLoaderID + '-success'); 109 110 if (!reorderSuccessSelector.length) { 111 reorderSuccessSelector = $('<span>') 112 .attr( 113 { 114 id: reorderLoaderID + '-success', 115 class: 'smoc-success dashicons dashicons-yes-alt', 116 role: 'img', 117 'aria-label': 'Success', 118 } 119 ) 120 .css( 121 { 122 'padding-top': '5px', 123 color: '#7ad03a', 124 display: 'none', 125 } 126 ); 127 128 reorderResultContainer.append(reorderSuccessSelector); 129 } else { 130 reorderSuccessSelector.css({ display: 'none' }); 131 } 132 133 /** 134 * Create Error result. 135 */ 136 137 let reorderErrorSelector = $('#' + reorderLoaderID + '-error'); 138 139 if (!reorderErrorSelector.length) { 140 reorderErrorSelector = $('<span>') 141 .attr( 142 { 143 id: reorderLoaderID + '-error', 144 class: 'smoc-error dashicons dashicons-dismiss', 145 role: 'img', 146 'aria-label': 'Error', 147 } 148 ) 149 .css( 150 { 151 'padding-top': '5px', 152 color: '#a00', 153 display: 'none', 154 } 155 ); 156 157 reorderResultContainer.append(reorderErrorSelector); 158 } else { 159 reorderErrorSelector.css({ display: 'none' }); 160 } 161 162 /** 163 * Check WP configuration. 164 */ 165 if (!typenow || !ajaxurl) { 166 disableInput(reorderErrorSelector, 'Invalid WP installation, variables typenow or ajaxurl not initialized.', true); 167 return false; 168 } 169 170 let reorderPostMenuOrder = $(reorderCurrentProduct).val(); 171 172 /** 173 * Populate and validate Product Order 174 */ 175 reorderPostMenuOrder = $(reorderCurrentProduct).val(); 176 177 if (!reorderPostMenuOrder || isNaN(reorderPostMenuOrder)) { 178 disableInput(reorderErrorSelector, 'Invalid menu order value.', false); 179 return false; 180 } 181 182 reorderPostMenuOrder = parseInt(reorderPostMenuOrder); 183 184 /** 185 * Populate wpnonce 186 */ 187 let postNonce = $(reorderCurrentProduct).data('wpnonce'); 188 189 if (!postNonce) { 190 disableInput(reorderErrorSelector, 'Invalid field postNonce.', true); 191 return false; 192 } 193 194 /** 195 * Disable INPUT while doing ajax 196 */ 197 $(currentObject).prop('disabled', true); 198 199 reorderLoaderSelectorContainer.css({ display: 'inline-block' }); 200 201 /** 202 * Format POST URL. 203 */ 204 const searchParams = new URLSearchParams(); 205 206 searchParams.set('action', 'smoc_reorder'); 207 searchParams.set('_wpnonce', postNonce); 208 209 const request = jQuery.ajax( 210 { 211 url: ajaxurl + '?' + searchParams, 212 type: 'POST', 213 data: { 214 post_type: typenow, 215 post_id: reorderPostID, 216 post_menu_order: reorderPostMenuOrder, 217 }, 218 } 219 ); 220 221 request.done( 222 function (response) { 223 if (response.success) { 224 reorderSuccessSelector.css('display', 'inline-block'); 225 226 $(currentObject).prop('title', reorderPostMenuOrder); 227 228 currentObject.currentValue = reorderPostMenuOrder; 229 currentObject.defaultValue = reorderPostMenuOrder; 230 // If success go to next product. 231 const currentObjectPosition = $(':input[id^=smoc]').index(currentObject); 232 $(':input[id^=smoc]').eq(currentObjectPosition + 1).trigger('select'); 233 } else { 234 currentObject.value = currentObject.defaultValue; 235 236 reorderErrorSelector.css('display', 'inline-block'); 237 } 238 } 239 ); 240 241 request.fail( 242 function () { 243 currentObject.value = currentObject.defaultValue; 244 245 reorderLoaderSelectorContainer.css('display', 'none'); 246 reorderSuccessSelector.css('display', 'none'); 247 reorderErrorSelector.css('display', 'inline-block'); 248 } 249 ); 250 251 request.always( 252 function () { 253 reorderLoaderSelectorContainer.css({ display: 'none' }); 254 255 /** 256 * Enable INPUT after doing Ajax 257 */ 258 $(currentObject).prop('disabled', false); 259 } 260 ); 261 }; 262 263 $('input[id^=smoc]').on( 264 'focus', function () { 265 this.currentValue = this.value; 266 267 $(this).prop('title', parseInt(this.value)); 268 269 const reorderLoaderID = 'smoc-' + $(this).data('post-id').toString(); 270 271 $('#' + reorderLoaderID + '-loader-container').css({ display: 'none' }); 272 $('#' + reorderLoaderID + '-success').css({ display: 'none' }); 273 $('#' + reorderLoaderID + '-error').css({ display: 'none' }); 274 } 275 ); 276 277 $('input[id^=smoc]').on( 278 'focusout', function (e) { 279 if ($(this).prop('disabled')) { 280 return false; 281 } 282 283 if (this.currentValue !== this.value) { 284 if (window.confirm('Do you want to save the menu order?')) { 285 $(this).smocDoReorder(this); 286 } else { 287 this.value = this.defaultValue; 288 } 289 } 290 } 291 ); 292 293 $('input[id^=smoc]').on( 294 'keypress', function (e) { 295 if (e.key === 'Enter') { 296 e.preventDefault(); 297 298 $(this).smocDoReorder(this); 299 } 300 } 301 ); 261 302 })(jQuery); -
simple-menu-order-column/tags/1.0.0/includes/class-simplemenuordercolumn.php
r3074004 r3322001 12 12 use WP_Error; 13 13 14 defined( 'ABSPATH') || exit;14 defined('ABSPATH') || exit; 15 15 16 16 /** 17 17 * SMOCWC class. 18 18 */ 19 final class SimpleMenuOrderColumn { 20 21 /** 22 * The single instance of the class. 23 * 24 * @var SimpleMenuOrderColumn 25 */ 26 private static $smoc_instace; 27 28 /** 29 * Allowed types. 30 * 31 * We allow all WP_Post since has menu_order column and are sortable. 32 * 33 * @var array 34 */ 35 private static $smoc_allowed_types = array( 'post', 'page', 'product', 'attachment' ); 36 37 /** 38 * Construtor. 39 */ 40 public function __construct() { 41 add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) ); 42 } 43 44 /** 45 * After plugins loaded. 46 * 47 * @return void 48 */ 49 public function plugins_loaded() { 50 51 /** If we are not in admin pages nothing to do. */ 52 if ( ! is_admin() ) { 53 return; 54 } 55 56 /** 57 * If it's an ajax call add the reorder action and ignore the rest. 58 * 59 * Same as usig $GLOBAL['pagenow'] === 'admin-ajax.php 60 */ 61 if ( wp_doing_ajax() ) { 62 add_action( 'wp_ajax_smoc_reorder', array( __CLASS__, 'ajax_set_post_menu_order' ) ); 63 64 return; 65 } 66 67 add_action( 'init', array( $this, 'init' ) ); 68 add_action( 'current_screen', array( $this, 'current_screen' ) ); 69 } 70 71 /** 72 * Initialize plugin. 73 * 74 * @return void 75 */ 76 public function init() { 77 if ( function_exists( 'load_plugin_textdomain' ) ) { 78 load_plugin_textdomain( 'simple-menu-order-column', false, dirname( plugin_basename( SMOC_PLUGIN_FILE ) ) . '/i18n/languages/' ); 79 } 80 } 81 82 /** 83 * Manage columns when we are on the screen we want. 84 * 85 * @return void 86 */ 87 public function current_screen() { 88 /** Add only on listings pages and compatible post types. */ 89 $current_screen = get_current_screen(); 90 91 if ( 92 ! in_array( $current_screen->base, array( 'edit', 'upload' ), true ) || 93 ! in_array( $current_screen->post_type, self::$smoc_allowed_types, true ) 94 ) { 95 return; 96 } 97 98 add_filter( 'manage_' . $current_screen->id . '_columns', array( __CLASS__, 'manage_edit_columns' ) ); 99 add_filter( 'manage_' . $current_screen->id . '_sortable_columns', array( __CLASS__, 'manage_edit_sortable_columns' ) ); 100 101 if ( 'upload' === $current_screen->base ) { 102 /** This filter is called directly. */ 103 add_filter( 'manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 ); 104 } else { 105 add_action( 'manage_' . $current_screen->post_type . '_posts_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 ); 106 } 107 108 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); 109 } 110 111 /** 112 * Enqueue scripts. 113 */ 114 public function admin_enqueue_scripts() { 115 $wp_scripts_get_suffix = wp_scripts_get_suffix(); 116 117 wp_enqueue_script( 'simple-menu-order-column', plugins_url( 'assets/js/simple-menu-order-column' . $wp_scripts_get_suffix . '.js', SMOC_PLUGIN_FILE ), array( 'jquery' ), SMOC_PLUGIN_VERSION, true ); 118 wp_enqueue_style( 'simple-menu-order-column', plugins_url( 'assets/css/simple-menu-order-column' . $wp_scripts_get_suffix . '.css', SMOC_PLUGIN_FILE ), array(), SMOC_PLUGIN_VERSION ); 119 } 120 121 /** 122 * Allowed post_types. 123 * 124 * @return array 125 */ 126 public static function get_allowed_types() { 127 return self::$smoc_allowed_types; 128 } 129 130 /** 131 * Ajax call to reorder. 132 * 133 * @return void 134 */ 135 public static function ajax_set_post_menu_order() { 136 if ( false === check_ajax_referer( 'set-post-menu-order', '_wpnonce', false ) ) { 137 wp_send_json_error(); 138 } 139 140 /** 141 * Check post_type. 142 */ 143 $post_type = filter_input( INPUT_POST, 'post_type', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); 144 145 if ( ! $post_type || ! in_array( $post_type, self::$smoc_allowed_types, true ) ) { 146 wp_send_json_error(); 147 } 148 149 /** 150 * Get post_id & post_menu_order. 151 */ 152 $post_id = filter_input( INPUT_POST, 'post_id', FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) ) ); 153 $post_menu_order = filter_input( INPUT_POST, 'post_menu_order', FILTER_VALIDATE_INT ); 154 155 if ( 156 ! is_integer( $post_id ) || 157 ! is_integer( $post_menu_order ) || 158 ! current_user_can( 'edit_post', $post_id ) || 159 self::set_post_menu_order( $post_id, $post_menu_order ) instanceof WP_Error 160 ) { 161 wp_send_json_error(); 162 } 163 164 wp_send_json_success(); 165 } 166 167 /** 168 * Set post menu order. 169 * 170 * @param int $post_id Post id. 171 * @param int $post_menu_order Post order. 172 */ 173 private static function set_post_menu_order( int $post_id, int $post_menu_order ) { 174 175 $post = get_post( $post_id ); 176 177 if ( ! $post ) { 178 return new WP_Error(); 179 } 180 181 $post->menu_order = $post_menu_order; 182 183 return wp_update_post( $post, true ); 184 } 185 186 /** 187 * Generate html column order input field. 188 * 189 * @param int $post_id Post id. 190 * @param int $post_menu_order Post order. 191 */ 192 private static function output_menu_order_column( int $post_id, int $post_menu_order ) { 193 /** 194 * NOTE: Is better to use woocommerce_wp_text_input method but to keep the plugin Woo free we create it here. 195 */ 196 // Even all output is XSS secure to prevent bot complaining we cast variables again. 197 print '<div class="smoc-container">'; 198 print '<input id="smoc-' . (int) $post_id . '" type="text" class="smoc-input" value="' . (int) $post_menu_order . '" title="' . (int) $post_menu_order . '" data-wpnonce="' . esc_attr( wp_create_nonce( 'set-post-menu-order' ) ) . '" data-post-id="' . (int) $post_id . '" />'; 199 print '</div>'; 200 } 201 202 /** 203 * Append menu order column to listings pages. 204 * 205 * @param string $column Column name. 206 * @param int $postid Post order. 207 */ 208 public static function manage_posts_custom_column( $column, $postid ) { 209 if ( 'menu_order' === $column ) { 210 $post = get_post( $postid ); 211 212 self::output_menu_order_column( $postid, $post->menu_order ); 213 } 214 } 215 216 /** 217 * Add menu order column. 218 * 219 * @param array $columns Post list columns. 220 * @return array 221 */ 222 public static function manage_edit_columns( $columns ) { 223 $columns['menu_order'] = esc_html__( 'Order', 'simple-menu-order-column' ); 224 225 return $columns; 226 } 227 228 /** 229 * Add menu order column to sortable columns. 230 * 231 * @param array $sortable_columns Post list columns. 232 * @return array 233 */ 234 public static function manage_edit_sortable_columns( $sortable_columns ) { 235 $sortable_columns['menu_order'] = 'menu_order'; 236 return $sortable_columns; 237 } 238 239 /** 240 * Get this as singleton. 241 * 242 * @return SimpleMenuOrderColumn 243 */ 244 public static function instance() { 245 if ( is_null( self::$smoc_instace ) ) { 246 self::$smoc_instace = new self(); 247 } 248 249 return self::$smoc_instace; 250 } 19 final class SimpleMenuOrderColumn 20 { 21 22 /** 23 * The single instance of the class. 24 * 25 * @var SimpleMenuOrderColumn 26 */ 27 private static $smoc_instace; 28 29 /** 30 * Allowed types. 31 * 32 * We allow all WP_Post since has menu_order column and are sortable. 33 * 34 * @var array 35 */ 36 private static $smoc_allowed_types = array( 'post', 'page', 'product', 'attachment' ); 37 38 /** 39 * Construtor. 40 */ 41 public function __construct() 42 { 43 add_action('plugins_loaded', array( $this, 'plugins_loaded' )); 44 } 45 46 /** 47 * After plugins loaded. 48 * 49 * @return void 50 */ 51 public function plugins_loaded() 52 { 53 54 /** 55 * If we are not in admin pages nothing to do. 56 */ 57 if (! is_admin() ) { 58 return; 59 } 60 61 /** 62 * If it's an ajax call add the reorder action and ignore the rest. 63 * 64 * Same as usig $GLOBAL['pagenow'] === 'admin-ajax.php 65 */ 66 if (wp_doing_ajax() ) { 67 add_action('wp_ajax_smoc_reorder', array( __CLASS__, 'ajax_set_post_menu_order' )); 68 69 return; 70 } 71 72 add_action('init', array( $this, 'init' )); 73 add_action('current_screen', array( $this, 'current_screen' )); 74 } 75 76 /** 77 * Initialize plugin. 78 * 79 * @return void 80 */ 81 public function init() 82 { 83 if (function_exists('load_plugin_textdomain') ) { 84 load_plugin_textdomain('simple-menu-order-column', false, dirname(plugin_basename(SMOC_PLUGIN_FILE)) . '/i18n/languages/'); 85 } 86 } 87 88 /** 89 * Manage columns when we are on the screen we want. 90 * 91 * @return void 92 */ 93 public function current_screen() 94 { 95 /** 96 * Add only on listings pages and compatible post types. 97 */ 98 $current_screen = get_current_screen(); 99 100 if (! in_array($current_screen->base, array( 'edit', 'upload' ), true) 101 || ! in_array($current_screen->post_type, self::$smoc_allowed_types, true) 102 ) { 103 return; 104 } 105 106 add_filter('manage_' . $current_screen->id . '_columns', array( __CLASS__, 'manage_edit_columns' )); 107 add_filter('manage_' . $current_screen->id . '_sortable_columns', array( __CLASS__, 'manage_edit_sortable_columns' )); 108 109 if ('upload' === $current_screen->base ) { 110 /** 111 * This filter is called directly. 112 */ 113 add_filter('manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2); 114 } else { 115 add_action('manage_' . $current_screen->post_type . '_posts_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2); 116 } 117 118 add_action('admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' )); 119 } 120 121 /** 122 * Enqueue scripts. 123 */ 124 public function admin_enqueue_scripts() 125 { 126 $wp_scripts_get_suffix = wp_scripts_get_suffix(); 127 128 wp_enqueue_script('simple-menu-order-column', plugins_url('assets/js/simple-menu-order-column' . $wp_scripts_get_suffix . '.js', SMOC_PLUGIN_FILE), array( 'jquery' ), SMOC_PLUGIN_VERSION, true); 129 wp_enqueue_style('simple-menu-order-column', plugins_url('assets/css/simple-menu-order-column' . $wp_scripts_get_suffix . '.css', SMOC_PLUGIN_FILE), array(), SMOC_PLUGIN_VERSION); 130 } 131 132 /** 133 * Allowed post_types. 134 * 135 * @return array 136 */ 137 public static function get_allowed_types() 138 { 139 return self::$smoc_allowed_types; 140 } 141 142 /** 143 * Ajax call to reorder. 144 * 145 * @return void 146 */ 147 public static function ajax_set_post_menu_order() 148 { 149 if (false === check_ajax_referer('set-post-menu-order', '_wpnonce', false) ) { 150 wp_send_json_error(); 151 } 152 153 /** 154 * Check post_type. 155 */ 156 $post_type = filter_input(INPUT_POST, 'post_type', FILTER_SANITIZE_FULL_SPECIAL_CHARS); 157 158 if (! $post_type || ! in_array($post_type, self::$smoc_allowed_types, true) ) { 159 wp_send_json_error(); 160 } 161 162 /** 163 * Get post_id & post_menu_order. 164 */ 165 $post_id = filter_input(INPUT_POST, 'post_id', FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) )); 166 $post_menu_order = filter_input(INPUT_POST, 'post_menu_order', FILTER_VALIDATE_INT); 167 168 if (! is_integer($post_id) 169 || ! is_integer($post_menu_order) 170 || ! current_user_can('edit_post', $post_id) 171 || self::set_post_menu_order($post_id, $post_menu_order) instanceof WP_Error 172 ) { 173 wp_send_json_error(); 174 } 175 176 wp_send_json_success(); 177 } 178 179 /** 180 * Set post menu order. 181 * 182 * @param int $post_id Post id. 183 * @param int $post_menu_order Post order. 184 */ 185 private static function set_post_menu_order( int $post_id, int $post_menu_order ) 186 { 187 188 $post = get_post($post_id); 189 190 if (! $post ) { 191 return new WP_Error(); 192 } 193 194 $post->menu_order = $post_menu_order; 195 196 return wp_update_post($post, true); 197 } 198 199 /** 200 * Generate html column order input field. 201 * 202 * @param int $post_id Post id. 203 * @param int $post_menu_order Post order. 204 */ 205 private static function output_menu_order_column( int $post_id, int $post_menu_order ) 206 { 207 /** 208 * NOTE: Is better to use woocommerce_wp_text_input method but to keep the plugin Woo free we create it here. 209 */ 210 // Even all output is XSS secure to prevent bot complaining we cast variables again. 211 print '<div class="smoc-container">'; 212 print '<input id="smoc-' . (int) $post_id . '" type="text" class="smoc-input" value="' . (int) $post_menu_order . '" title="' . (int) $post_menu_order . '" data-wpnonce="' . esc_attr(wp_create_nonce('set-post-menu-order')) . '" data-post-id="' . (int) $post_id . '" />'; 213 print '</div>'; 214 } 215 216 /** 217 * Append menu order column to listings pages. 218 * 219 * @param string $column Column name. 220 * @param int $postid Post order. 221 */ 222 public static function manage_posts_custom_column( $column, $postid ) 223 { 224 if ('menu_order' === $column ) { 225 $post = get_post($postid); 226 227 self::output_menu_order_column($postid, $post->menu_order); 228 } 229 } 230 231 /** 232 * Add menu order column. 233 * 234 * @param array $columns Post list columns. 235 * @return array 236 */ 237 public static function manage_edit_columns( $columns ) 238 { 239 $columns['menu_order'] = esc_html__('Order', 'simple-menu-order-column'); 240 241 return $columns; 242 } 243 244 /** 245 * Add menu order column to sortable columns. 246 * 247 * @param array $sortable_columns Post list columns. 248 * @return array 249 */ 250 public static function manage_edit_sortable_columns( $sortable_columns ) 251 { 252 $sortable_columns['menu_order'] = 'menu_order'; 253 return $sortable_columns; 254 } 255 256 /** 257 * Get this as singleton. 258 * 259 * @return SimpleMenuOrderColumn 260 */ 261 public static function instance() 262 { 263 if (is_null(self::$smoc_instace) ) { 264 self::$smoc_instace = new self(); 265 } 266 267 return self::$smoc_instace; 268 } 251 269 } -
simple-menu-order-column/tags/1.0.0/simple-menu-order-column.php
r3074004 r3322001 26 26 */ 27 27 28 defined( 'ABSPATH') || exit;28 defined('ABSPATH') || exit; 29 29 30 define( 'SMOC_PLUGIN_PATH', __DIR__);31 define( 'SMOC_PLUGIN_FILE', __FILE__);32 define( 'SMOC_PLUGIN_VERSION', '1.0.0');30 define('SMOC_PLUGIN_PATH', __DIR__); 31 define('SMOC_PLUGIN_FILE', __FILE__); 32 define('SMOC_PLUGIN_VERSION', '1.0.0'); 33 33 34 34 require_once SMOC_PLUGIN_PATH . '/includes/class-simplemenuordercolumn.php'; … … 39 39 * Ensures only one instance is loaded or can be loaded. 40 40 * 41 * @since 1.041 * @since 1.0 42 42 * @static 43 43 * @return SMOC\SimpleMenuOrderColumn Main instance. 44 44 */ 45 function SMOC(): SMOC\SimpleMenuOrderColumn { //phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid 46 return SMOC\SimpleMenuOrderColumn::instance(); 45 function SMOC(): SMOC\SimpleMenuOrderColumn //phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid 46 { 47 return SMOC\SimpleMenuOrderColumn::instance(); 47 48 } 48 49 -
simple-menu-order-column/tags/1.0.1/assets/js/simple-menu-order-column.js
r3074004 r3322001 9 9 */ 10 10 (function ($) { 11 $.fn.smocDoReorder = function (currentObject) { 12 13 const reorderCurrentProduct = this; 14 15 if (!reorderCurrentProduct || $(currentObject).prop('disabled')) { 16 return false; 17 } 18 19 function disableInput(errorContainer, errorMessage, disable) { 20 /** Reset input value. */ 21 currentObject.value = currentObject.defaultValue; 22 /** Show error icon. */ 23 errorContainer && errorContainer.css('display', 'inline-block'); 24 /** Disable input field. */ 25 $(currentObject).prop('disabled', disable).prop('title', errorMessage); 26 /** Output message to widow console. */ 27 window.console.warn('[Simple Menu Order Column] ' + errorMessage); 28 }; 29 30 let reorderPostID = $(reorderCurrentProduct).data('post-id'); 31 32 if (!reorderPostID || isNaN(reorderPostID)) { 33 disableInput(null, 'Invalid Post ID.', true); 34 return false; 35 } 36 37 reorderPostID = parseInt(reorderPostID); 38 39 /** 40 * Create loader and result containers. 41 */ 42 const reorderLoaderID = 'smoc-' + reorderPostID.toString(); 43 44 const reorderResultContainer = $(reorderCurrentProduct).closest('.smoc-container'); 45 46 /** 47 * Create loader. 48 */ 49 let reorderLoaderSelector = $('#' + reorderLoaderID + '-loader'); 50 51 if (!reorderLoaderSelector.length) { 52 reorderLoaderSelector = $('<span>') 53 .attr({ 54 id: reorderLoaderID + '-loader', 55 class: 'smoc-loader dashicons dashicons-update', 56 role: 'img', 57 'aria-label': 'Updating Menu Order', 58 }) 59 .css({ 60 color: '#2ea2cc', 61 animation: 'iconrotation 2s infinite linear', 62 display: 'inline-block', 63 }); 64 } 65 66 let reorderLoaderSelectorContainer = $( 67 '#' + reorderLoaderID + '-loader-container' 68 ); 69 70 if (!reorderLoaderSelectorContainer.length) { 71 reorderLoaderSelectorContainer = $('<div>') 72 .attr({ 73 id: reorderLoaderID + '-loader-container', 74 }) 75 .css({ 76 'padding-top': '5px', 77 display: 'none', 78 }); 79 80 reorderLoaderSelectorContainer.append(reorderLoaderSelector); 81 82 reorderResultContainer.append(reorderLoaderSelectorContainer); 83 } else { 84 reorderLoaderSelectorContainer.css({ display: 'none' }); 85 } 86 87 /** 88 * Create Success result. 89 */ 90 91 let reorderSuccessSelector = $('#' + reorderLoaderID + '-success'); 92 93 if (!reorderSuccessSelector.length) { 94 reorderSuccessSelector = $('<span>') 95 .attr({ 96 id: reorderLoaderID + '-success', 97 class: 'smoc-success dashicons dashicons-yes-alt', 98 role: 'img', 99 'aria-label': 'Success', 100 }) 101 .css({ 102 'padding-top': '5px', 103 color: '#7ad03a', 104 display: 'none', 105 }); 106 107 reorderResultContainer.append(reorderSuccessSelector); 108 } else { 109 reorderSuccessSelector.css({ display: 'none' }); 110 } 111 112 /** 113 * Create Error result. 114 */ 115 116 let reorderErrorSelector = $('#' + reorderLoaderID + '-error'); 117 118 if (!reorderErrorSelector.length) { 119 reorderErrorSelector = $('<span>') 120 .attr({ 121 id: reorderLoaderID + '-error', 122 class: 'smoc-error dashicons dashicons-dismiss', 123 role: 'img', 124 'aria-label': 'Error', 125 }) 126 .css({ 127 'padding-top': '5px', 128 color: '#a00', 129 display: 'none', 130 }); 131 132 reorderResultContainer.append(reorderErrorSelector); 133 } else { 134 reorderErrorSelector.css({ display: 'none' }); 135 } 136 137 /** 138 * Check WP configuration. 139 */ 140 if (!typenow || !ajaxurl) { 141 disableInput(reorderErrorSelector, 'Invalid WP installation, variables typenow or ajaxurl not initialized.', true); 142 return false; 143 } 144 145 let reorderPostMenuOrder = $(reorderCurrentProduct).val(); 146 147 /** 148 * Populate and validate Product Order 149 */ 150 reorderPostMenuOrder = $(reorderCurrentProduct).val(); 151 152 if (!reorderPostMenuOrder || isNaN(reorderPostMenuOrder)) { 153 disableInput(reorderErrorSelector, 'Invalid menu order value.', false); 154 return false; 155 } 156 157 reorderPostMenuOrder = parseInt(reorderPostMenuOrder); 158 159 /** 160 * Populate wpnonce 161 */ 162 let postNonce = $(reorderCurrentProduct).data('wpnonce'); 163 164 if (!postNonce) { 165 disableInput(reorderErrorSelector, 'Invalid field postNonce.', true); 166 return false; 167 } 168 169 /** 170 * Disable INPUT while doing ajax 171 */ 172 $(currentObject).prop('disabled', true); 173 174 reorderLoaderSelectorContainer.css({ display: 'inline-block' }); 175 176 /** 177 * Format POST URL. 178 */ 179 const searchParams = new URLSearchParams(); 180 181 searchParams.set('action', 'smoc_reorder'); 182 searchParams.set('_wpnonce', postNonce); 183 184 const request = jQuery.ajax({ 185 url: ajaxurl + '?' + searchParams, 186 type: 'POST', 187 data: { 188 post_type: typenow, 189 post_id: reorderPostID, 190 post_menu_order: reorderPostMenuOrder, 191 }, 192 }); 193 194 request.done(function (response) { 195 if (response.success) { 196 reorderSuccessSelector.css('display', 'inline-block'); 197 198 $(currentObject).prop('title', reorderPostMenuOrder); 199 200 currentObject.currentValue = reorderPostMenuOrder; 201 currentObject.defaultValue = reorderPostMenuOrder; 202 // If success go to next product. 203 const currentObjectPosition = $(':input[id^=smoc]').index(currentObject); 204 $(':input[id^=smoc]').eq(currentObjectPosition + 1).trigger('select'); 205 } else { 206 currentObject.value = currentObject.defaultValue; 207 208 reorderErrorSelector.css('display', 'inline-block'); 209 } 210 }); 211 212 request.fail(function () { 213 currentObject.value = currentObject.defaultValue; 214 215 reorderLoaderSelectorContainer.css('display', 'none'); 216 reorderSuccessSelector.css('display', 'none'); 217 reorderErrorSelector.css('display', 'inline-block'); 218 }); 219 220 request.always(function () { 221 reorderLoaderSelectorContainer.css({ display: 'none' }); 222 223 /** Enable INPUT after doing Ajax */ 224 $(currentObject).prop('disabled', false); 225 }); 226 }; 227 228 $('input[id^=smoc]').on('focus', function () { 229 this.currentValue = this.value; 230 231 $(this).prop('title', parseInt(this.value)); 232 233 const reorderLoaderID = 'smoc-' + $(this).data('post-id').toString(); 234 235 $('#' + reorderLoaderID + '-loader-container').css({ display: 'none' }); 236 $('#' + reorderLoaderID + '-success').css({ display: 'none' }); 237 $('#' + reorderLoaderID + '-error').css({ display: 'none' }); 238 }); 239 240 $('input[id^=smoc]').on('focusout', function (e) { 241 if ($(this).prop('disabled')) { 242 return false; 243 } 244 245 if (this.currentValue !== this.value) { 246 if (window.confirm('Do you want to save the menu order?')) { 247 $(this).smocDoReorder(this); 248 } else { 249 this.value = this.defaultValue; 250 } 251 } 252 }); 253 254 $('input[id^=smoc]').on('keypress', function (e) { 255 if (e.key === 'Enter') { 256 e.preventDefault(); 257 258 $(this).smocDoReorder(this); 259 } 260 }); 11 $.fn.smocDoReorder = function (currentObject) { 12 13 const reorderCurrentProduct = this; 14 15 if (!reorderCurrentProduct || $(currentObject).prop('disabled')) { 16 return false; 17 } 18 19 function disableInput(errorContainer, errorMessage, disable) 20 { 21 /** 22 * Reset input value. 23 */ 24 currentObject.value = currentObject.defaultValue; 25 /** 26 * Show error icon. 27 */ 28 errorContainer && errorContainer.css('display', 'inline-block'); 29 /** 30 * Disable input field. 31 */ 32 $(currentObject).prop('disabled', disable).prop('title', errorMessage); 33 /** 34 * Output message to widow console. 35 */ 36 window.console.warn('[Simple Menu Order Column] ' + errorMessage); 37 }; 38 39 let reorderPostID = $(reorderCurrentProduct).data('post-id'); 40 41 if (!reorderPostID || isNaN(reorderPostID)) { 42 disableInput(null, 'Invalid Post ID.', true); 43 return false; 44 } 45 46 reorderPostID = parseInt(reorderPostID); 47 48 /** 49 * Create loader and result containers. 50 */ 51 const reorderLoaderID = 'smoc-' + reorderPostID.toString(); 52 53 const reorderResultContainer = $(reorderCurrentProduct).closest('.smoc-container'); 54 55 /** 56 * Create loader. 57 */ 58 let reorderLoaderSelector = $('#' + reorderLoaderID + '-loader'); 59 60 if (!reorderLoaderSelector.length) { 61 reorderLoaderSelector = $('<span>') 62 .attr( 63 { 64 id: reorderLoaderID + '-loader', 65 class: 'smoc-loader dashicons dashicons-update', 66 role: 'img', 67 'aria-label': 'Updating Menu Order', 68 } 69 ) 70 .css( 71 { 72 color: '#2ea2cc', 73 animation: 'iconrotation 2s infinite linear', 74 display: 'inline-block', 75 } 76 ); 77 } 78 79 let reorderLoaderSelectorContainer = $( 80 '#' + reorderLoaderID + '-loader-container' 81 ); 82 83 if (!reorderLoaderSelectorContainer.length) { 84 reorderLoaderSelectorContainer = $('<div>') 85 .attr( 86 { 87 id: reorderLoaderID + '-loader-container', 88 } 89 ) 90 .css( 91 { 92 'padding-top': '5px', 93 display: 'none', 94 } 95 ); 96 97 reorderLoaderSelectorContainer.append(reorderLoaderSelector); 98 99 reorderResultContainer.append(reorderLoaderSelectorContainer); 100 } else { 101 reorderLoaderSelectorContainer.css({ display: 'none' }); 102 } 103 104 /** 105 * Create Success result. 106 */ 107 108 let reorderSuccessSelector = $('#' + reorderLoaderID + '-success'); 109 110 if (!reorderSuccessSelector.length) { 111 reorderSuccessSelector = $('<span>') 112 .attr( 113 { 114 id: reorderLoaderID + '-success', 115 class: 'smoc-success dashicons dashicons-yes-alt', 116 role: 'img', 117 'aria-label': 'Success', 118 } 119 ) 120 .css( 121 { 122 'padding-top': '5px', 123 color: '#7ad03a', 124 display: 'none', 125 } 126 ); 127 128 reorderResultContainer.append(reorderSuccessSelector); 129 } else { 130 reorderSuccessSelector.css({ display: 'none' }); 131 } 132 133 /** 134 * Create Error result. 135 */ 136 137 let reorderErrorSelector = $('#' + reorderLoaderID + '-error'); 138 139 if (!reorderErrorSelector.length) { 140 reorderErrorSelector = $('<span>') 141 .attr( 142 { 143 id: reorderLoaderID + '-error', 144 class: 'smoc-error dashicons dashicons-dismiss', 145 role: 'img', 146 'aria-label': 'Error', 147 } 148 ) 149 .css( 150 { 151 'padding-top': '5px', 152 color: '#a00', 153 display: 'none', 154 } 155 ); 156 157 reorderResultContainer.append(reorderErrorSelector); 158 } else { 159 reorderErrorSelector.css({ display: 'none' }); 160 } 161 162 /** 163 * Check WP configuration. 164 */ 165 if (!typenow || !ajaxurl) { 166 disableInput(reorderErrorSelector, 'Invalid WP installation, variables typenow or ajaxurl not initialized.', true); 167 return false; 168 } 169 170 let reorderPostMenuOrder = $(reorderCurrentProduct).val(); 171 172 /** 173 * Populate and validate Product Order 174 */ 175 reorderPostMenuOrder = $(reorderCurrentProduct).val(); 176 177 if (!reorderPostMenuOrder || isNaN(reorderPostMenuOrder)) { 178 disableInput(reorderErrorSelector, 'Invalid menu order value.', false); 179 return false; 180 } 181 182 reorderPostMenuOrder = parseInt(reorderPostMenuOrder); 183 184 /** 185 * Populate wpnonce 186 */ 187 let postNonce = $(reorderCurrentProduct).data('wpnonce'); 188 189 if (!postNonce) { 190 disableInput(reorderErrorSelector, 'Invalid field postNonce.', true); 191 return false; 192 } 193 194 /** 195 * Disable INPUT while doing ajax 196 */ 197 $(currentObject).prop('disabled', true); 198 199 reorderLoaderSelectorContainer.css({ display: 'inline-block' }); 200 201 /** 202 * Format POST URL. 203 */ 204 const searchParams = new URLSearchParams(); 205 206 searchParams.set('action', 'smoc_reorder'); 207 searchParams.set('_wpnonce', postNonce); 208 209 const request = jQuery.ajax( 210 { 211 url: ajaxurl + '?' + searchParams, 212 type: 'POST', 213 data: { 214 post_type: typenow, 215 post_id: reorderPostID, 216 post_menu_order: reorderPostMenuOrder, 217 }, 218 } 219 ); 220 221 request.done( 222 function (response) { 223 if (response.success) { 224 reorderSuccessSelector.css('display', 'inline-block'); 225 226 $(currentObject).prop('title', reorderPostMenuOrder); 227 228 currentObject.currentValue = reorderPostMenuOrder; 229 currentObject.defaultValue = reorderPostMenuOrder; 230 // If success go to next product. 231 const currentObjectPosition = $(':input[id^=smoc]').index(currentObject); 232 $(':input[id^=smoc]').eq(currentObjectPosition + 1).trigger('select'); 233 } else { 234 currentObject.value = currentObject.defaultValue; 235 236 reorderErrorSelector.css('display', 'inline-block'); 237 } 238 } 239 ); 240 241 request.fail( 242 function () { 243 currentObject.value = currentObject.defaultValue; 244 245 reorderLoaderSelectorContainer.css('display', 'none'); 246 reorderSuccessSelector.css('display', 'none'); 247 reorderErrorSelector.css('display', 'inline-block'); 248 } 249 ); 250 251 request.always( 252 function () { 253 reorderLoaderSelectorContainer.css({ display: 'none' }); 254 255 /** 256 * Enable INPUT after doing Ajax 257 */ 258 $(currentObject).prop('disabled', false); 259 } 260 ); 261 }; 262 263 $('input[id^=smoc]').on( 264 'focus', function () { 265 this.currentValue = this.value; 266 267 $(this).prop('title', parseInt(this.value)); 268 269 const reorderLoaderID = 'smoc-' + $(this).data('post-id').toString(); 270 271 $('#' + reorderLoaderID + '-loader-container').css({ display: 'none' }); 272 $('#' + reorderLoaderID + '-success').css({ display: 'none' }); 273 $('#' + reorderLoaderID + '-error').css({ display: 'none' }); 274 } 275 ); 276 277 $('input[id^=smoc]').on( 278 'focusout', function (e) { 279 if ($(this).prop('disabled')) { 280 return false; 281 } 282 283 if (this.currentValue !== this.value) { 284 if (window.confirm('Do you want to save the menu order?')) { 285 $(this).smocDoReorder(this); 286 } else { 287 this.value = this.defaultValue; 288 } 289 } 290 } 291 ); 292 293 $('input[id^=smoc]').on( 294 'keypress', function (e) { 295 if (e.key === 'Enter') { 296 e.preventDefault(); 297 298 $(this).smocDoReorder(this); 299 } 300 } 301 ); 261 302 })(jQuery); -
simple-menu-order-column/tags/1.0.1/includes/class-simplemenuordercolumn.php
r3074004 r3322001 12 12 use WP_Error; 13 13 14 defined( 'ABSPATH') || exit;14 defined('ABSPATH') || exit; 15 15 16 16 /** 17 17 * SMOCWC class. 18 18 */ 19 final class SimpleMenuOrderColumn { 20 21 /** 22 * The single instance of the class. 23 * 24 * @var SimpleMenuOrderColumn 25 */ 26 private static $smoc_instace; 27 28 /** 29 * Allowed types. 30 * 31 * We allow all WP_Post since has menu_order column and are sortable. 32 * 33 * @var array 34 */ 35 private static $smoc_allowed_types = array( 'post', 'page', 'product', 'attachment' ); 36 37 /** 38 * Construtor. 39 */ 40 public function __construct() { 41 add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) ); 42 } 43 44 /** 45 * After plugins loaded. 46 * 47 * @return void 48 */ 49 public function plugins_loaded() { 50 51 /** If we are not in admin pages nothing to do. */ 52 if ( ! is_admin() ) { 53 return; 54 } 55 56 /** 57 * If it's an ajax call add the reorder action and ignore the rest. 58 * 59 * Same as usig $GLOBAL['pagenow'] === 'admin-ajax.php 60 */ 61 if ( wp_doing_ajax() ) { 62 add_action( 'wp_ajax_smoc_reorder', array( __CLASS__, 'ajax_set_post_menu_order' ) ); 63 64 return; 65 } 66 67 add_action( 'init', array( $this, 'init' ) ); 68 add_action( 'current_screen', array( $this, 'current_screen' ) ); 69 } 70 71 /** 72 * Initialize plugin. 73 * 74 * @return void 75 */ 76 public function init() { 77 if ( function_exists( 'load_plugin_textdomain' ) ) { 78 load_plugin_textdomain( 'simple-menu-order-column', false, dirname( plugin_basename( SMOC_PLUGIN_FILE ) ) . '/i18n/languages/' ); 79 } 80 } 81 82 /** 83 * Manage columns when we are on the screen we want. 84 * 85 * @return void 86 */ 87 public function current_screen() { 88 /** Add only on listings pages and compatible post types. */ 89 $current_screen = get_current_screen(); 90 91 if ( 92 ! in_array( $current_screen->base, array( 'edit', 'upload' ), true ) || 93 ! in_array( $current_screen->post_type, self::$smoc_allowed_types, true ) 94 ) { 95 return; 96 } 97 98 add_filter( 'manage_' . $current_screen->id . '_columns', array( __CLASS__, 'manage_edit_columns' ) ); 99 add_filter( 'manage_' . $current_screen->id . '_sortable_columns', array( __CLASS__, 'manage_edit_sortable_columns' ) ); 100 101 if ( 'upload' === $current_screen->base ) { 102 /** This filter is called directly. */ 103 add_filter( 'manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 ); 104 } else { 105 add_action( 'manage_' . $current_screen->post_type . '_posts_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 ); 106 } 107 108 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); 109 } 110 111 /** 112 * Enqueue scripts. 113 */ 114 public function admin_enqueue_scripts() { 115 $wp_scripts_get_suffix = wp_scripts_get_suffix(); 116 117 wp_enqueue_script( 'simple-menu-order-column', plugins_url( 'assets/js/simple-menu-order-column' . $wp_scripts_get_suffix . '.js', SMOC_PLUGIN_FILE ), array( 'jquery' ), SMOC_PLUGIN_VERSION, true ); 118 wp_enqueue_style( 'simple-menu-order-column', plugins_url( 'assets/css/simple-menu-order-column' . $wp_scripts_get_suffix . '.css', SMOC_PLUGIN_FILE ), array(), SMOC_PLUGIN_VERSION ); 119 } 120 121 /** 122 * Allowed post_types. 123 * 124 * @return array 125 */ 126 public static function get_allowed_types() { 127 return self::$smoc_allowed_types; 128 } 129 130 /** 131 * Ajax call to reorder. 132 * 133 * @return void 134 */ 135 public static function ajax_set_post_menu_order() { 136 if ( false === check_ajax_referer( 'set-post-menu-order', '_wpnonce', false ) ) { 137 wp_send_json_error(); 138 } 139 140 /** 141 * Check post_type. 142 */ 143 $post_type = filter_input( INPUT_POST, 'post_type', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); 144 145 if ( ! $post_type || ! in_array( $post_type, self::$smoc_allowed_types, true ) ) { 146 wp_send_json_error(); 147 } 148 149 /** 150 * Get post_id & post_menu_order. 151 */ 152 $post_id = filter_input( INPUT_POST, 'post_id', FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) ) ); 153 $post_menu_order = filter_input( INPUT_POST, 'post_menu_order', FILTER_VALIDATE_INT ); 154 155 if ( 156 ! is_integer( $post_id ) || 157 ! is_integer( $post_menu_order ) || 158 ! current_user_can( 'edit_post', $post_id ) || 159 self::set_post_menu_order( $post_id, $post_menu_order ) instanceof WP_Error 160 ) { 161 wp_send_json_error(); 162 } 163 164 wp_send_json_success(); 165 } 166 167 /** 168 * Set post menu order. 169 * 170 * @param int $post_id Post id. 171 * @param int $post_menu_order Post order. 172 */ 173 private static function set_post_menu_order( int $post_id, int $post_menu_order ) { 174 175 $post = get_post( $post_id ); 176 177 if ( ! $post ) { 178 return new WP_Error(); 179 } 180 181 $post->menu_order = $post_menu_order; 182 183 return wp_update_post( $post, true ); 184 } 185 186 /** 187 * Generate html column order input field. 188 * 189 * @param int $post_id Post id. 190 * @param int $post_menu_order Post order. 191 */ 192 private static function output_menu_order_column( int $post_id, int $post_menu_order ) { 193 /** 194 * NOTE: Is better to use woocommerce_wp_text_input method but to keep the plugin Woo free we create it here. 195 */ 196 // Even all output is XSS secure to prevent bot complaining we cast variables again. 197 print '<div class="smoc-container">'; 198 print '<input id="smoc-' . (int) $post_id . '" type="text" class="smoc-input" value="' . (int) $post_menu_order . '" title="' . (int) $post_menu_order . '" data-wpnonce="' . esc_attr( wp_create_nonce( 'set-post-menu-order' ) ) . '" data-post-id="' . (int) $post_id . '" />'; 199 print '</div>'; 200 } 201 202 /** 203 * Append menu order column to listings pages. 204 * 205 * @param string $column Column name. 206 * @param int $postid Post order. 207 */ 208 public static function manage_posts_custom_column( $column, $postid ) { 209 if ( 'menu_order' === $column ) { 210 $post = get_post( $postid ); 211 212 self::output_menu_order_column( $postid, $post->menu_order ); 213 } 214 } 215 216 /** 217 * Add menu order column. 218 * 219 * @param array $columns Post list columns. 220 * @return array 221 */ 222 public static function manage_edit_columns( $columns ) { 223 $columns['menu_order'] = esc_html__( 'Order', 'simple-menu-order-column' ); 224 225 return $columns; 226 } 227 228 /** 229 * Add menu order column to sortable columns. 230 * 231 * @param array $sortable_columns Post list columns. 232 * @return array 233 */ 234 public static function manage_edit_sortable_columns( $sortable_columns ) { 235 $sortable_columns['menu_order'] = 'menu_order'; 236 return $sortable_columns; 237 } 238 239 /** 240 * Get this as singleton. 241 * 242 * @return SimpleMenuOrderColumn 243 */ 244 public static function instance() { 245 if ( is_null( self::$smoc_instace ) ) { 246 self::$smoc_instace = new self(); 247 } 248 249 return self::$smoc_instace; 250 } 19 final class SimpleMenuOrderColumn 20 { 21 22 /** 23 * The single instance of the class. 24 * 25 * @var SimpleMenuOrderColumn 26 */ 27 private static $smoc_instace; 28 29 /** 30 * Allowed types. 31 * 32 * We allow all WP_Post since has menu_order column and are sortable. 33 * 34 * @var array 35 */ 36 private static $smoc_allowed_types = array( 'post', 'page', 'product', 'attachment' ); 37 38 /** 39 * Construtor. 40 */ 41 public function __construct() 42 { 43 add_action('plugins_loaded', array( $this, 'plugins_loaded' )); 44 } 45 46 /** 47 * After plugins loaded. 48 * 49 * @return void 50 */ 51 public function plugins_loaded() 52 { 53 54 /** 55 * If we are not in admin pages nothing to do. 56 */ 57 if (! is_admin() ) { 58 return; 59 } 60 61 /** 62 * If it's an ajax call add the reorder action and ignore the rest. 63 * 64 * Same as usig $GLOBAL['pagenow'] === 'admin-ajax.php 65 */ 66 if (wp_doing_ajax() ) { 67 add_action('wp_ajax_smoc_reorder', array( __CLASS__, 'ajax_set_post_menu_order' )); 68 69 return; 70 } 71 72 add_action('init', array( $this, 'init' )); 73 add_action('current_screen', array( $this, 'current_screen' )); 74 } 75 76 /** 77 * Initialize plugin. 78 * 79 * @return void 80 */ 81 public function init() 82 { 83 if (function_exists('load_plugin_textdomain') ) { 84 load_plugin_textdomain('simple-menu-order-column', false, dirname(plugin_basename(SMOC_PLUGIN_FILE)) . '/i18n/languages/'); 85 } 86 } 87 88 /** 89 * Manage columns when we are on the screen we want. 90 * 91 * @return void 92 */ 93 public function current_screen() 94 { 95 /** 96 * Add only on listings pages and compatible post types. 97 */ 98 $current_screen = get_current_screen(); 99 100 if (! in_array($current_screen->base, array( 'edit', 'upload' ), true) 101 || ! in_array($current_screen->post_type, self::$smoc_allowed_types, true) 102 ) { 103 return; 104 } 105 106 add_filter('manage_' . $current_screen->id . '_columns', array( __CLASS__, 'manage_edit_columns' )); 107 add_filter('manage_' . $current_screen->id . '_sortable_columns', array( __CLASS__, 'manage_edit_sortable_columns' )); 108 109 if ('upload' === $current_screen->base ) { 110 /** 111 * This filter is called directly. 112 */ 113 add_filter('manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2); 114 } else { 115 add_action('manage_' . $current_screen->post_type . '_posts_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2); 116 } 117 118 add_action('admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' )); 119 } 120 121 /** 122 * Enqueue scripts. 123 */ 124 public function admin_enqueue_scripts() 125 { 126 $wp_scripts_get_suffix = wp_scripts_get_suffix(); 127 128 wp_enqueue_script('simple-menu-order-column', plugins_url('assets/js/simple-menu-order-column' . $wp_scripts_get_suffix . '.js', SMOC_PLUGIN_FILE), array( 'jquery' ), SMOC_PLUGIN_VERSION, true); 129 wp_enqueue_style('simple-menu-order-column', plugins_url('assets/css/simple-menu-order-column' . $wp_scripts_get_suffix . '.css', SMOC_PLUGIN_FILE), array(), SMOC_PLUGIN_VERSION); 130 } 131 132 /** 133 * Allowed post_types. 134 * 135 * @return array 136 */ 137 public static function get_allowed_types() 138 { 139 return self::$smoc_allowed_types; 140 } 141 142 /** 143 * Ajax call to reorder. 144 * 145 * @return void 146 */ 147 public static function ajax_set_post_menu_order() 148 { 149 if (false === check_ajax_referer('set-post-menu-order', '_wpnonce', false) ) { 150 wp_send_json_error(); 151 } 152 153 /** 154 * Check post_type. 155 */ 156 $post_type = filter_input(INPUT_POST, 'post_type', FILTER_SANITIZE_FULL_SPECIAL_CHARS); 157 158 if (! $post_type || ! in_array($post_type, self::$smoc_allowed_types, true) ) { 159 wp_send_json_error(); 160 } 161 162 /** 163 * Get post_id & post_menu_order. 164 */ 165 $post_id = filter_input(INPUT_POST, 'post_id', FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) )); 166 $post_menu_order = filter_input(INPUT_POST, 'post_menu_order', FILTER_VALIDATE_INT); 167 168 if (! is_integer($post_id) 169 || ! is_integer($post_menu_order) 170 || ! current_user_can('edit_post', $post_id) 171 || self::set_post_menu_order($post_id, $post_menu_order) instanceof WP_Error 172 ) { 173 wp_send_json_error(); 174 } 175 176 wp_send_json_success(); 177 } 178 179 /** 180 * Set post menu order. 181 * 182 * @param int $post_id Post id. 183 * @param int $post_menu_order Post order. 184 */ 185 private static function set_post_menu_order( int $post_id, int $post_menu_order ) 186 { 187 188 $post = get_post($post_id); 189 190 if (! $post ) { 191 return new WP_Error(); 192 } 193 194 $post->menu_order = $post_menu_order; 195 196 return wp_update_post($post, true); 197 } 198 199 /** 200 * Generate html column order input field. 201 * 202 * @param int $post_id Post id. 203 * @param int $post_menu_order Post order. 204 */ 205 private static function output_menu_order_column( int $post_id, int $post_menu_order ) 206 { 207 /** 208 * NOTE: Is better to use woocommerce_wp_text_input method but to keep the plugin Woo free we create it here. 209 */ 210 // Even all output is XSS secure to prevent bot complaining we cast variables again. 211 print '<div class="smoc-container">'; 212 print '<input id="smoc-' . (int) $post_id . '" type="text" class="smoc-input" value="' . (int) $post_menu_order . '" title="' . (int) $post_menu_order . '" data-wpnonce="' . esc_attr(wp_create_nonce('set-post-menu-order')) . '" data-post-id="' . (int) $post_id . '" />'; 213 print '</div>'; 214 } 215 216 /** 217 * Append menu order column to listings pages. 218 * 219 * @param string $column Column name. 220 * @param int $postid Post order. 221 */ 222 public static function manage_posts_custom_column( $column, $postid ) 223 { 224 if ('menu_order' === $column ) { 225 $post = get_post($postid); 226 227 self::output_menu_order_column($postid, $post->menu_order); 228 } 229 } 230 231 /** 232 * Add menu order column. 233 * 234 * @param array $columns Post list columns. 235 * @return array 236 */ 237 public static function manage_edit_columns( $columns ) 238 { 239 $columns['menu_order'] = esc_html__('Order', 'simple-menu-order-column'); 240 241 return $columns; 242 } 243 244 /** 245 * Add menu order column to sortable columns. 246 * 247 * @param array $sortable_columns Post list columns. 248 * @return array 249 */ 250 public static function manage_edit_sortable_columns( $sortable_columns ) 251 { 252 $sortable_columns['menu_order'] = 'menu_order'; 253 return $sortable_columns; 254 } 255 256 /** 257 * Get this as singleton. 258 * 259 * @return SimpleMenuOrderColumn 260 */ 261 public static function instance() 262 { 263 if (is_null(self::$smoc_instace) ) { 264 self::$smoc_instace = new self(); 265 } 266 267 return self::$smoc_instace; 268 } 251 269 } -
simple-menu-order-column/tags/1.0.1/simple-menu-order-column.php
r3122104 r3322001 23 23 */ 24 24 25 defined( 'ABSPATH') || exit;25 defined('ABSPATH') || exit; 26 26 27 define( 'SMOC_PLUGIN_PATH', __DIR__);28 define( 'SMOC_PLUGIN_FILE', __FILE__);29 define( 'SMOC_PLUGIN_VERSION', '1.0.1');27 define('SMOC_PLUGIN_PATH', __DIR__); 28 define('SMOC_PLUGIN_FILE', __FILE__); 29 define('SMOC_PLUGIN_VERSION', '1.0.1'); 30 30 31 31 require_once SMOC_PLUGIN_PATH . '/includes/class-simplemenuordercolumn.php'; … … 36 36 * Ensures only one instance is loaded or can be loaded. 37 37 * 38 * @since 1.038 * @since 1.0 39 39 * @static 40 40 * @return SMOC\SimpleMenuOrderColumn Main instance. 41 41 */ 42 function SMOC(): SMOC\SimpleMenuOrderColumn { //phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid 43 return SMOC\SimpleMenuOrderColumn::instance(); 42 function SMOC(): SMOC\SimpleMenuOrderColumn //phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid 43 { 44 return SMOC\SimpleMenuOrderColumn::instance(); 44 45 } 45 46 -
simple-menu-order-column/tags/1.0.2/assets/js/simple-menu-order-column.js
r3259832 r3322001 9 9 */ 10 10 (function ($) { 11 const { __, _x, _n, _nx } = wp.i18n; 12 $.fn.smocDoReorder = function (currentObject) { 13 const reorderCurrentProduct = this; 14 15 if (!reorderCurrentProduct || $(currentObject).prop('disabled')) { 16 return false; 17 } 18 19 function disableInput(errorContainer, errorMessage, disable) { 20 /** Reset input value. */ 21 currentObject.value = currentObject.defaultValue; 22 /** Show error icon. */ 23 errorContainer && errorContainer.css('display', 'inline-block'); 24 /** Disable input field. */ 25 $(currentObject).prop('disabled', disable).prop('title', errorMessage); 26 /** Output message to widow console. */ 27 window.console.warn('[Simple Menu Order Column] ' + errorMessage); 28 }; 29 30 let reorderPostID = $(reorderCurrentProduct).data('post-id'); 31 32 if (!reorderPostID || isNaN(reorderPostID)) { 33 disableInput(null, __( 'The post_id is invalid.', 'simple-menu-order-column' ), true); 34 return false; 35 } 36 37 reorderPostID = parseInt(reorderPostID); 38 39 /** 40 * Create loader and result containers. 41 */ 42 const reorderLoaderID = 'smoc-' + reorderPostID.toString(); 43 44 const reorderResultContainer = $(reorderCurrentProduct).closest('.smoc-container'); 45 46 /** 47 * Create loader. 48 */ 49 let reorderLoaderSelector = $('#' + reorderLoaderID + '-loader'); 50 51 if (!reorderLoaderSelector.length) { 52 reorderLoaderSelector = $('<span>') 53 .attr({ 54 id: reorderLoaderID + '-loader', 55 class: 'smoc-loader dashicons dashicons-update', 56 role: 'img', 57 'aria-label': __( 'Updating menu order...', 'simple-menu-order-column' ), 58 }) 59 .css({ 60 color: '#2ea2cc', 61 animation: 'iconrotation 2s infinite linear', 62 display: 'inline-block', 63 }); 64 } 65 66 let reorderLoaderSelectorContainer = $( 67 '#' + reorderLoaderID + '-loader-container' 68 ); 69 70 if (!reorderLoaderSelectorContainer.length) { 71 reorderLoaderSelectorContainer = $('<div>') 72 .attr({ 73 id: reorderLoaderID + '-loader-container', 74 }) 75 .css({ 76 'padding-top': '5px', 77 display: 'none', 78 }); 79 80 reorderLoaderSelectorContainer.append(reorderLoaderSelector); 81 82 reorderResultContainer.append(reorderLoaderSelectorContainer); 83 } else { 84 reorderLoaderSelectorContainer.css({ display: 'none' }); 85 } 86 87 /** 88 * Create Success result. 89 */ 90 91 let reorderSuccessSelector = $('#' + reorderLoaderID + '-success'); 92 93 if (!reorderSuccessSelector.length) { 94 reorderSuccessSelector = $('<span>') 95 .attr({ 96 id: reorderLoaderID + '-success', 97 class: 'smoc-success dashicons dashicons-yes-alt', 98 role: 'img', 99 'aria-label': __( 'The menu order has been updated successfully.', 'simple-menu-order-column' ), 100 }) 101 .css({ 102 'padding-top': '5px', 103 color: '#7ad03a', 104 display: 'none', 105 }); 106 107 reorderResultContainer.append(reorderSuccessSelector); 108 } else { 109 reorderSuccessSelector.css({ display: 'none' }); 110 } 111 112 /** 113 * Create Error result. 114 */ 115 116 let reorderErrorSelector = $('#' + reorderLoaderID + '-error'); 117 118 if (!reorderErrorSelector.length) { 119 reorderErrorSelector = $('<span>') 120 .attr({ 121 id: reorderLoaderID + '-error', 122 class: 'smoc-error dashicons dashicons-dismiss', 123 role: 'img', 124 'aria-label': __( 'An error ocurred while updating menu order.', 'simple-menu-order-column' ), 125 }) 126 .css({ 127 'padding-top': '5px', 128 color: '#a00', 129 display: 'none', 130 }); 131 132 reorderResultContainer.append(reorderErrorSelector); 133 } else { 134 reorderErrorSelector.css({ display: 'none' }); 135 } 136 137 /** 138 * Check WP configuration. 139 */ 140 if (!typenow || !ajaxurl) { 141 disableInput(reorderErrorSelector, __( 'Invalid WP installation, variables typenow or ajaxurl are not initialized.', 'simple-menu-order-column' ), true); 142 return false; 143 } 144 145 let reorderPostMenuOrder = $(reorderCurrentProduct).val(); 146 147 /** 148 * Populate and validate Product Order 149 */ 150 reorderPostMenuOrder = $(reorderCurrentProduct).val(); 151 152 if (!reorderPostMenuOrder || isNaN(reorderPostMenuOrder)) { 153 disableInput(reorderErrorSelector, __( 'The menu order value is invalid.', 'simple-menu-order-column' ), false); 154 return false; 155 } 156 157 reorderPostMenuOrder = parseInt(reorderPostMenuOrder); 158 159 /** 160 * Populate wpnonce 161 */ 162 let postNonce = $(reorderCurrentProduct).data('wpnonce'); 163 164 if (!postNonce) { 165 disableInput(reorderErrorSelector, __( 'The postNonce is invalid.', 'simple-menu-order-column' ), true); 166 return false; 167 } 168 169 /** 170 * Disable INPUT while doing ajax 171 */ 172 $(currentObject).prop('disabled', true); 173 174 reorderLoaderSelectorContainer.css({ display: 'inline-block' }); 175 176 /** 177 * Format POST URL. 178 */ 179 const searchParams = new URLSearchParams(); 180 181 searchParams.set('action', 'smoc_reorder'); 182 searchParams.set('_wpnonce', postNonce); 183 184 const request = jQuery.ajax({ 185 url: ajaxurl + '?' + searchParams, 186 type: 'POST', 187 data: { 188 post_type: typenow, 189 post_id: reorderPostID, 190 post_menu_order: reorderPostMenuOrder, 191 }, 192 }); 193 194 request.done(function (response) { 195 if (response.success) { 196 reorderSuccessSelector.css('display', 'inline-block'); 197 198 $(currentObject).prop('title', reorderPostMenuOrder); 199 200 currentObject.currentValue = reorderPostMenuOrder; 201 currentObject.defaultValue = reorderPostMenuOrder; 202 // If success go to next product. 203 const currentObjectPosition = $(':input[id^=smoc]').index(currentObject); 204 $(':input[id^=smoc]').eq(currentObjectPosition + 1).trigger('select'); 205 } else { 206 currentObject.value = currentObject.defaultValue; 207 208 reorderErrorSelector.css('display', 'inline-block'); 209 } 210 }); 211 212 request.fail(function () { 213 currentObject.value = currentObject.defaultValue; 214 215 reorderLoaderSelectorContainer.css('display', 'none'); 216 reorderSuccessSelector.css('display', 'none'); 217 reorderErrorSelector.css('display', 'inline-block'); 218 }); 219 220 request.always(function () { 221 reorderLoaderSelectorContainer.css({ display: 'none' }); 222 223 /** Enable INPUT after doing Ajax */ 224 $(currentObject).prop('disabled', false); 225 }); 226 }; 227 228 $('input[id^=smoc]').on('focus', function () { 229 this.currentValue = this.value; 230 231 $(this).prop('title', parseInt(this.value)); 232 233 const reorderLoaderID = 'smoc-' + $(this).data('post-id').toString(); 234 235 $('#' + reorderLoaderID + '-loader-container').css({ display: 'none' }); 236 $('#' + reorderLoaderID + '-success').css({ display: 'none' }); 237 $('#' + reorderLoaderID + '-error').css({ display: 'none' }); 238 }); 239 240 $('input[id^=smoc]').on('focusout', function (e) { 241 if ($(this).prop('disabled')) { 242 return false; 243 } 244 245 if (this.currentValue !== this.value) { 246 if (window.confirm(__( 'Should the menu order value be updated?', 'simple-menu-order-column' ))) { 247 $(this).smocDoReorder(this); 248 } else { 249 this.value = this.defaultValue; 250 } 251 } 252 }); 253 254 $('input[id^=smoc]').on('keypress', function (e) { 255 if (e.key === 'Enter') { 256 e.preventDefault(); 257 258 $(this).smocDoReorder(this); 259 } 260 }); 11 const { __, _x, _n, _nx } = wp.i18n; 12 $.fn.smocDoReorder = function (currentObject) { 13 const reorderCurrentProduct = this; 14 15 if (!reorderCurrentProduct || $(currentObject).prop('disabled')) { 16 return false; 17 } 18 19 function disableInput(errorContainer, errorMessage, disable) 20 { 21 /** 22 * Reset input value. 23 */ 24 currentObject.value = currentObject.defaultValue; 25 /** 26 * Show error icon. 27 */ 28 errorContainer && errorContainer.css('display', 'inline-block'); 29 /** 30 * Disable input field. 31 */ 32 $(currentObject).prop('disabled', disable).prop('title', errorMessage); 33 /** 34 * Output message to widow console. 35 */ 36 window.console.warn('[Simple Menu Order Column] ' + errorMessage); 37 }; 38 39 let reorderPostID = $(reorderCurrentProduct).data('post-id'); 40 41 if (!reorderPostID || isNaN(reorderPostID)) { 42 disableInput(null, __('The post_id is invalid.', 'simple-menu-order-column'), true); 43 return false; 44 } 45 46 reorderPostID = parseInt(reorderPostID); 47 48 /** 49 * Create loader and result containers. 50 */ 51 const reorderLoaderID = 'smoc-' + reorderPostID.toString(); 52 53 const reorderResultContainer = $(reorderCurrentProduct).closest('.smoc-container'); 54 55 /** 56 * Create loader. 57 */ 58 let reorderLoaderSelector = $('#' + reorderLoaderID + '-loader'); 59 60 if (!reorderLoaderSelector.length) { 61 reorderLoaderSelector = $('<span>') 62 .attr( 63 { 64 id: reorderLoaderID + '-loader', 65 class: 'smoc-loader dashicons dashicons-update', 66 role: 'img', 67 'aria-label': __('Updating menu order...', 'simple-menu-order-column'), 68 } 69 ) 70 .css( 71 { 72 color: '#2ea2cc', 73 animation: 'iconrotation 2s infinite linear', 74 display: 'inline-block', 75 } 76 ); 77 } 78 79 let reorderLoaderSelectorContainer = $( 80 '#' + reorderLoaderID + '-loader-container' 81 ); 82 83 if (!reorderLoaderSelectorContainer.length) { 84 reorderLoaderSelectorContainer = $('<div>') 85 .attr( 86 { 87 id: reorderLoaderID + '-loader-container', 88 } 89 ) 90 .css( 91 { 92 'padding-top': '5px', 93 display: 'none', 94 } 95 ); 96 97 reorderLoaderSelectorContainer.append(reorderLoaderSelector); 98 99 reorderResultContainer.append(reorderLoaderSelectorContainer); 100 } else { 101 reorderLoaderSelectorContainer.css({ display: 'none' }); 102 } 103 104 /** 105 * Create Success result. 106 */ 107 108 let reorderSuccessSelector = $('#' + reorderLoaderID + '-success'); 109 110 if (!reorderSuccessSelector.length) { 111 reorderSuccessSelector = $('<span>') 112 .attr( 113 { 114 id: reorderLoaderID + '-success', 115 class: 'smoc-success dashicons dashicons-yes-alt', 116 role: 'img', 117 'aria-label': __('The menu order has been updated successfully.', 'simple-menu-order-column'), 118 } 119 ) 120 .css( 121 { 122 'padding-top': '5px', 123 color: '#7ad03a', 124 display: 'none', 125 } 126 ); 127 128 reorderResultContainer.append(reorderSuccessSelector); 129 } else { 130 reorderSuccessSelector.css({ display: 'none' }); 131 } 132 133 /** 134 * Create Error result. 135 */ 136 137 let reorderErrorSelector = $('#' + reorderLoaderID + '-error'); 138 139 if (!reorderErrorSelector.length) { 140 reorderErrorSelector = $('<span>') 141 .attr( 142 { 143 id: reorderLoaderID + '-error', 144 class: 'smoc-error dashicons dashicons-dismiss', 145 role: 'img', 146 'aria-label': __('An error ocurred while updating menu order.', 'simple-menu-order-column'), 147 } 148 ) 149 .css( 150 { 151 'padding-top': '5px', 152 color: '#a00', 153 display: 'none', 154 } 155 ); 156 157 reorderResultContainer.append(reorderErrorSelector); 158 } else { 159 reorderErrorSelector.css({ display: 'none' }); 160 } 161 162 /** 163 * Check WP configuration. 164 */ 165 if (!typenow || !ajaxurl) { 166 disableInput(reorderErrorSelector, __('Invalid WP installation, variables typenow or ajaxurl are not initialized.', 'simple-menu-order-column'), true); 167 return false; 168 } 169 170 let reorderPostMenuOrder = $(reorderCurrentProduct).val(); 171 172 /** 173 * Populate and validate Product Order 174 */ 175 reorderPostMenuOrder = $(reorderCurrentProduct).val(); 176 177 if (!reorderPostMenuOrder || isNaN(reorderPostMenuOrder)) { 178 disableInput(reorderErrorSelector, __('The menu order value is invalid.', 'simple-menu-order-column'), false); 179 return false; 180 } 181 182 reorderPostMenuOrder = parseInt(reorderPostMenuOrder); 183 184 /** 185 * Populate wpnonce 186 */ 187 let postNonce = $(reorderCurrentProduct).data('wpnonce'); 188 189 if (!postNonce) { 190 disableInput(reorderErrorSelector, __('The postNonce is invalid.', 'simple-menu-order-column'), true); 191 return false; 192 } 193 194 /** 195 * Disable INPUT while doing ajax 196 */ 197 $(currentObject).prop('disabled', true); 198 199 reorderLoaderSelectorContainer.css({ display: 'inline-block' }); 200 201 /** 202 * Format POST URL. 203 */ 204 const searchParams = new URLSearchParams(); 205 206 searchParams.set('action', 'smoc_reorder'); 207 searchParams.set('_wpnonce', postNonce); 208 209 const request = jQuery.ajax( 210 { 211 url: ajaxurl + '?' + searchParams, 212 type: 'POST', 213 data: { 214 post_type: typenow, 215 post_id: reorderPostID, 216 post_menu_order: reorderPostMenuOrder, 217 }, 218 } 219 ); 220 221 request.done( 222 function (response) { 223 if (response.success) { 224 reorderSuccessSelector.css('display', 'inline-block'); 225 226 $(currentObject).prop('title', reorderPostMenuOrder); 227 228 currentObject.currentValue = reorderPostMenuOrder; 229 currentObject.defaultValue = reorderPostMenuOrder; 230 // If success go to next product. 231 const currentObjectPosition = $(':input[id^=smoc]').index(currentObject); 232 $(':input[id^=smoc]').eq(currentObjectPosition + 1).trigger('select'); 233 } else { 234 currentObject.value = currentObject.defaultValue; 235 236 reorderErrorSelector.css('display', 'inline-block'); 237 } 238 } 239 ); 240 241 request.fail( 242 function () { 243 currentObject.value = currentObject.defaultValue; 244 245 reorderLoaderSelectorContainer.css('display', 'none'); 246 reorderSuccessSelector.css('display', 'none'); 247 reorderErrorSelector.css('display', 'inline-block'); 248 } 249 ); 250 251 request.always( 252 function () { 253 reorderLoaderSelectorContainer.css({ display: 'none' }); 254 255 /** 256 * Enable INPUT after doing Ajax 257 */ 258 $(currentObject).prop('disabled', false); 259 } 260 ); 261 }; 262 263 $('input[id^=smoc]').on( 264 'focus', function () { 265 this.currentValue = this.value; 266 267 $(this).prop('title', parseInt(this.value)); 268 269 const reorderLoaderID = 'smoc-' + $(this).data('post-id').toString(); 270 271 $('#' + reorderLoaderID + '-loader-container').css({ display: 'none' }); 272 $('#' + reorderLoaderID + '-success').css({ display: 'none' }); 273 $('#' + reorderLoaderID + '-error').css({ display: 'none' }); 274 } 275 ); 276 277 $('input[id^=smoc]').on( 278 'focusout', function (e) { 279 if ($(this).prop('disabled')) { 280 return false; 281 } 282 283 if (this.currentValue !== this.value) { 284 if (window.confirm(__('Should the menu order value be updated?', 'simple-menu-order-column'))) { 285 $(this).smocDoReorder(this); 286 } else { 287 this.value = this.defaultValue; 288 } 289 } 290 } 291 ); 292 293 $('input[id^=smoc]').on( 294 'keypress', function (e) { 295 if (e.key === 'Enter') { 296 e.preventDefault(); 297 298 $(this).smocDoReorder(this); 299 } 300 } 301 ); 261 302 })(jQuery); -
simple-menu-order-column/tags/1.0.2/includes/class-simplemenuordercolumn.php
r3259832 r3322001 12 12 use WP_Error; 13 13 14 defined( 'ABSPATH') || exit;14 defined('ABSPATH') || exit; 15 15 16 16 /** 17 17 * SMOCWC class. 18 18 */ 19 final class SimpleMenuOrderColumn { 20 21 /** 22 * The single instance of the class. 23 * 24 * @var SimpleMenuOrderColumn 25 */ 26 private static $smoc_instace; 27 28 /** 29 * Allowed types. 30 * 31 * We allow all WP_Post since has menu_order column and are sortable. 32 * 33 * @var array 34 */ 35 private static $smoc_allowed_types = array( 'post', 'page', 'product', 'attachment' ); 36 37 /** 38 * Construtor. 39 */ 40 public function __construct() { 41 add_action( 'plugins_loaded', array( $this, 'plugins_loaded' ) ); 42 } 43 44 /** 45 * After plugins loaded. 46 * 47 * @return void 48 */ 49 public function plugins_loaded() { 50 51 /** If we are not in admin pages nothing to do. */ 52 if ( ! is_admin() ) { 53 return; 54 } 55 56 /** 57 * If it's an ajax call add the reorder action and ignore the rest. 58 * 59 * Same as usig $GLOBAL['pagenow'] === 'admin-ajax.php 60 */ 61 if ( wp_doing_ajax() ) { 62 add_action( 'wp_ajax_smoc_reorder', array( __CLASS__, 'ajax_set_post_menu_order' ) ); 63 64 return; 65 } 66 67 add_action( 'init', array( $this, 'init' ) ); 68 add_action( 'current_screen', array( $this, 'current_screen' ) ); 69 } 70 71 /** 72 * Initialize plugin. 73 * 74 * @return void 75 */ 76 public function init() { 77 if ( function_exists( 'load_plugin_textdomain' ) ) { 78 load_plugin_textdomain( 'simple-menu-order-column', false, dirname( plugin_basename( SMOC_PLUGIN_FILE ) ) . '/i18n/languages/' ); 79 } 80 } 81 82 /** 83 * Manage columns when we are on the screen we want. 84 * 85 * @return void 86 */ 87 public function current_screen() { 88 /** Add only on listings pages and compatible post types. */ 89 $current_screen = get_current_screen(); 90 91 if ( 92 ! in_array( $current_screen->base, array( 'edit', 'upload' ), true ) || 93 ! in_array( $current_screen->post_type, self::$smoc_allowed_types, true ) 94 ) { 95 return; 96 } 97 98 add_filter( 'manage_' . $current_screen->id . '_columns', array( __CLASS__, 'manage_edit_columns' ) ); 99 add_filter( 'manage_' . $current_screen->id . '_sortable_columns', array( __CLASS__, 'manage_edit_sortable_columns' ) ); 100 101 if ( 'upload' === $current_screen->base ) { 102 /** This filter is called directly. */ 103 add_filter( 'manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 ); 104 } else { 105 add_action( 'manage_' . $current_screen->post_type . '_posts_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 ); 106 } 107 108 add_action( 'admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' ) ); 109 } 110 111 /** 112 * Enqueue scripts. 113 */ 114 public function admin_enqueue_scripts() { 115 $wp_scripts_get_suffix = wp_scripts_get_suffix(); 116 117 wp_enqueue_script( 'simple-menu-order-column', plugins_url( 'assets/js/simple-menu-order-column' . $wp_scripts_get_suffix . '.js', SMOC_PLUGIN_FILE ), array( 'jquery', 'wp-i18n' ), SMOC_PLUGIN_VERSION, true ); 118 wp_enqueue_style( 'simple-menu-order-column', plugins_url( 'assets/css/simple-menu-order-column' . $wp_scripts_get_suffix . '.css', SMOC_PLUGIN_FILE ), array(), SMOC_PLUGIN_VERSION ); 119 120 wp_set_script_translations( 'simple-menu-order-column', 'simple-menu-order-column', plugin_dir_path( SMOC_PLUGIN_FILE ) . '/i18n/languages/' ); 121 } 122 123 /** 124 * Allowed post_types. 125 * 126 * @return array 127 */ 128 public static function get_allowed_types() { 129 return self::$smoc_allowed_types; 130 } 131 132 /** 133 * Ajax call to reorder. 134 * 135 * @return void 136 */ 137 public static function ajax_set_post_menu_order() { 138 if ( false === check_ajax_referer( 'set-post-menu-order', '_wpnonce', false ) ) { 139 wp_send_json_error(); 140 } 141 142 /** 143 * Check post_type. 144 */ 145 $post_type = filter_input( INPUT_POST, 'post_type', FILTER_SANITIZE_FULL_SPECIAL_CHARS ); 146 147 if ( ! $post_type || ! in_array( $post_type, self::$smoc_allowed_types, true ) ) { 148 wp_send_json_error(); 149 } 150 151 /** 152 * Get post_id & post_menu_order. 153 */ 154 $post_id = filter_input( INPUT_POST, 'post_id', FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) ) ); 155 $post_menu_order = filter_input( INPUT_POST, 'post_menu_order', FILTER_VALIDATE_INT ); 156 157 if ( 158 ! is_integer( $post_id ) || 159 ! is_integer( $post_menu_order ) || 160 ! current_user_can( 'edit_post', $post_id ) || 161 self::set_post_menu_order( $post_id, $post_menu_order ) instanceof WP_Error 162 ) { 163 wp_send_json_error(); 164 } 165 166 wp_send_json_success(); 167 } 168 169 /** 170 * Set post menu order. 171 * 172 * @param int $post_id Post id. 173 * @param int $post_menu_order Post order. 174 */ 175 private static function set_post_menu_order( int $post_id, int $post_menu_order ) { 176 177 $post = get_post( $post_id ); 178 179 if ( ! $post ) { 180 return new WP_Error(); 181 } 182 183 $post->menu_order = $post_menu_order; 184 185 return wp_update_post( $post, true ); 186 } 187 188 /** 189 * Generate html column order input field. 190 * 191 * @param int $post_id Post id. 192 * @param int $post_menu_order Post order. 193 */ 194 private static function output_menu_order_column( int $post_id, int $post_menu_order ) { 195 /** 196 * NOTE: Is better to use woocommerce_wp_text_input method but to keep the plugin Woo free we create it here. 197 */ 198 // Even all output is XSS secure to prevent bot complaining we cast variables again. 199 print '<div class="smoc-container">'; 200 print '<input id="smoc-' . (int) $post_id . '" type="text" class="smoc-input" value="' . (int) $post_menu_order . '" title="' . (int) $post_menu_order . '" data-wpnonce="' . esc_attr( wp_create_nonce( 'set-post-menu-order' ) ) . '" data-post-id="' . (int) $post_id . '" />'; 201 print '</div>'; 202 } 203 204 /** 205 * Append menu order column to listings pages. 206 * 207 * @param string $column Column name. 208 * @param int $postid Post order. 209 */ 210 public static function manage_posts_custom_column( $column, $postid ) { 211 if ( 'menu_order' === $column ) { 212 $post = get_post( $postid ); 213 214 self::output_menu_order_column( $postid, $post->menu_order ); 215 } 216 } 217 218 /** 219 * Add menu order column. 220 * 221 * @param array $columns Post list columns. 222 * @return array 223 */ 224 public static function manage_edit_columns( $columns ) { 225 $columns['menu_order'] = esc_html__( 'Order', 'simple-menu-order-column' ); 226 227 return $columns; 228 } 229 230 /** 231 * Add menu order column to sortable columns. 232 * 233 * @param array $sortable_columns Post list columns. 234 * @return array 235 */ 236 public static function manage_edit_sortable_columns( $sortable_columns ) { 237 $sortable_columns['menu_order'] = 'menu_order'; 238 return $sortable_columns; 239 } 240 241 /** 242 * Get this as singleton. 243 * 244 * @return SimpleMenuOrderColumn 245 */ 246 public static function instance() { 247 if ( is_null( self::$smoc_instace ) ) { 248 self::$smoc_instace = new self(); 249 } 250 251 return self::$smoc_instace; 252 } 19 final class SimpleMenuOrderColumn 20 { 21 22 /** 23 * The single instance of the class. 24 * 25 * @var SimpleMenuOrderColumn 26 */ 27 private static $smoc_instace; 28 29 /** 30 * Allowed types. 31 * 32 * We allow all WP_Post since has menu_order column and are sortable. 33 * 34 * @var array 35 */ 36 private static $smoc_allowed_types = array( 'post', 'page', 'product', 'attachment' ); 37 38 /** 39 * Construtor. 40 */ 41 public function __construct() 42 { 43 add_action('plugins_loaded', array( $this, 'plugins_loaded' )); 44 } 45 46 /** 47 * After plugins loaded. 48 * 49 * @return void 50 */ 51 public function plugins_loaded() 52 { 53 54 /** 55 * If we are not in admin pages nothing to do. 56 */ 57 if (! is_admin() ) { 58 return; 59 } 60 61 /** 62 * If it's an ajax call add the reorder action and ignore the rest. 63 * 64 * Same as usig $GLOBAL['pagenow'] === 'admin-ajax.php 65 */ 66 if (wp_doing_ajax() ) { 67 add_action('wp_ajax_smoc_reorder', array( __CLASS__, 'ajax_set_post_menu_order' )); 68 69 return; 70 } 71 72 add_action('init', array( $this, 'init' )); 73 add_action('current_screen', array( $this, 'current_screen' )); 74 } 75 76 /** 77 * Initialize plugin. 78 * 79 * @return void 80 */ 81 public function init() 82 { 83 if (function_exists('load_plugin_textdomain') ) { 84 load_plugin_textdomain('simple-menu-order-column', false, dirname(plugin_basename(SMOC_PLUGIN_FILE)) . '/i18n/languages/'); 85 } 86 } 87 88 /** 89 * Manage columns when we are on the screen we want. 90 * 91 * @return void 92 */ 93 public function current_screen() 94 { 95 /** 96 * Add only on listings pages and compatible post types. 97 */ 98 $current_screen = get_current_screen(); 99 100 if (! in_array($current_screen->base, array( 'edit', 'upload' ), true) 101 || ! in_array($current_screen->post_type, self::$smoc_allowed_types, true) 102 ) { 103 return; 104 } 105 106 add_filter('manage_' . $current_screen->id . '_columns', array( __CLASS__, 'manage_edit_columns' )); 107 add_filter('manage_' . $current_screen->id . '_sortable_columns', array( __CLASS__, 'manage_edit_sortable_columns' )); 108 109 if ('upload' === $current_screen->base ) { 110 /** 111 * This filter is called directly. 112 */ 113 add_filter('manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2); 114 } else { 115 add_action('manage_' . $current_screen->post_type . '_posts_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2); 116 } 117 118 add_action('admin_enqueue_scripts', array( $this, 'admin_enqueue_scripts' )); 119 } 120 121 /** 122 * Enqueue scripts. 123 */ 124 public function admin_enqueue_scripts() 125 { 126 $wp_scripts_get_suffix = wp_scripts_get_suffix(); 127 128 wp_enqueue_script('simple-menu-order-column', plugins_url('assets/js/simple-menu-order-column' . $wp_scripts_get_suffix . '.js', SMOC_PLUGIN_FILE), array( 'jquery', 'wp-i18n' ), SMOC_PLUGIN_VERSION, true); 129 wp_enqueue_style('simple-menu-order-column', plugins_url('assets/css/simple-menu-order-column' . $wp_scripts_get_suffix . '.css', SMOC_PLUGIN_FILE), array(), SMOC_PLUGIN_VERSION); 130 131 wp_set_script_translations('simple-menu-order-column', 'simple-menu-order-column', plugin_dir_path(SMOC_PLUGIN_FILE) . '/i18n/languages/'); 132 } 133 134 /** 135 * Allowed post_types. 136 * 137 * @return array 138 */ 139 public static function get_allowed_types() 140 { 141 return self::$smoc_allowed_types; 142 } 143 144 /** 145 * Ajax call to reorder. 146 * 147 * @return void 148 */ 149 public static function ajax_set_post_menu_order() 150 { 151 if (false === check_ajax_referer('set-post-menu-order', '_wpnonce', false) ) { 152 wp_send_json_error(); 153 } 154 155 /** 156 * Check post_type. 157 */ 158 $post_type = filter_input(INPUT_POST, 'post_type', FILTER_SANITIZE_FULL_SPECIAL_CHARS); 159 160 if (! $post_type || ! in_array($post_type, self::$smoc_allowed_types, true) ) { 161 wp_send_json_error(); 162 } 163 164 /** 165 * Get post_id & post_menu_order. 166 */ 167 $post_id = filter_input(INPUT_POST, 'post_id', FILTER_VALIDATE_INT, array( 'options' => array( 'min_range' => 1 ) )); 168 $post_menu_order = filter_input(INPUT_POST, 'post_menu_order', FILTER_VALIDATE_INT); 169 170 if (! is_integer($post_id) 171 || ! is_integer($post_menu_order) 172 || ! current_user_can('edit_post', $post_id) 173 || self::set_post_menu_order($post_id, $post_menu_order) instanceof WP_Error 174 ) { 175 wp_send_json_error(); 176 } 177 178 wp_send_json_success(); 179 } 180 181 /** 182 * Set post menu order. 183 * 184 * @param int $post_id Post id. 185 * @param int $post_menu_order Post order. 186 */ 187 private static function set_post_menu_order( int $post_id, int $post_menu_order ) 188 { 189 190 $post = get_post($post_id); 191 192 if (! $post ) { 193 return new WP_Error(); 194 } 195 196 $post->menu_order = $post_menu_order; 197 198 return wp_update_post($post, true); 199 } 200 201 /** 202 * Generate html column order input field. 203 * 204 * @param int $post_id Post id. 205 * @param int $post_menu_order Post order. 206 */ 207 private static function output_menu_order_column( int $post_id, int $post_menu_order ) 208 { 209 /** 210 * NOTE: Is better to use woocommerce_wp_text_input method but to keep the plugin Woo free we create it here. 211 */ 212 // Even all output is XSS secure to prevent bot complaining we cast variables again. 213 print '<div class="smoc-container">'; 214 print '<input id="smoc-' . (int) $post_id . '" type="text" class="smoc-input" value="' . (int) $post_menu_order . '" title="' . (int) $post_menu_order . '" data-wpnonce="' . esc_attr(wp_create_nonce('set-post-menu-order')) . '" data-post-id="' . (int) $post_id . '" />'; 215 print '</div>'; 216 } 217 218 /** 219 * Append menu order column to listings pages. 220 * 221 * @param string $column Column name. 222 * @param int $postid Post order. 223 */ 224 public static function manage_posts_custom_column( $column, $postid ) 225 { 226 if ('menu_order' === $column ) { 227 $post = get_post($postid); 228 229 self::output_menu_order_column($postid, $post->menu_order); 230 } 231 } 232 233 /** 234 * Add menu order column. 235 * 236 * @param array $columns Post list columns. 237 * @return array 238 */ 239 public static function manage_edit_columns( $columns ) 240 { 241 $columns['menu_order'] = esc_html__('Order', 'simple-menu-order-column'); 242 243 return $columns; 244 } 245 246 /** 247 * Add menu order column to sortable columns. 248 * 249 * @param array $sortable_columns Post list columns. 250 * @return array 251 */ 252 public static function manage_edit_sortable_columns( $sortable_columns ) 253 { 254 $sortable_columns['menu_order'] = 'menu_order'; 255 return $sortable_columns; 256 } 257 258 /** 259 * Get this as singleton. 260 * 261 * @return SimpleMenuOrderColumn 262 */ 263 public static function instance() 264 { 265 if (is_null(self::$smoc_instace) ) { 266 self::$smoc_instace = new self(); 267 } 268 269 return self::$smoc_instace; 270 } 253 271 } -
simple-menu-order-column/tags/1.0.2/simple-menu-order-column.php
r3259832 r3322001 23 23 */ 24 24 25 defined( 'ABSPATH') || exit;25 defined('ABSPATH') || exit; 26 26 27 define( 'SMOC_PLUGIN_PATH', __DIR__);28 define( 'SMOC_PLUGIN_FILE', __FILE__);29 define( 'SMOC_PLUGIN_VERSION', '1.0.2');27 define('SMOC_PLUGIN_PATH', __DIR__); 28 define('SMOC_PLUGIN_FILE', __FILE__); 29 define('SMOC_PLUGIN_VERSION', '1.0.2'); 30 30 31 31 require_once SMOC_PLUGIN_PATH . '/includes/class-simplemenuordercolumn.php'; 32 32 33 if ( class_exists( 'SMOC\SimpleMenuOrderColumn' ) ) { 34 /** 35 * Main Instance. 36 * 37 * Ensures only one instance is loaded or can be loaded. 38 * 39 * @since 1.0 40 * @static 41 * @return SMOC\SimpleMenuOrderColumn Main instance. 42 */ 43 function SMOC(): SMOC\SimpleMenuOrderColumn { //phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid 44 return SMOC\SimpleMenuOrderColumn::instance(); 45 } 33 if (class_exists('SMOC\SimpleMenuOrderColumn') ) { 34 /** 35 * Main Instance. 36 * 37 * Ensures only one instance is loaded or can be loaded. 38 * 39 * @since 1.0 40 * @static 41 * @return SMOC\SimpleMenuOrderColumn Main instance. 42 */ 43 function SMOC(): SMOC\SimpleMenuOrderColumn //phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid 44 { 45 return SMOC\SimpleMenuOrderColumn::instance(); 46 } 46 47 47 /**48 * Initialize the plugin.49 */50 SMOC();48 /** 49 * Initialize the plugin. 50 */ 51 SMOC(); 51 52 } -
simple-menu-order-column/tags/2.0.0/assets/js/simple-menu-order-column.js
r3259831 r3322001 1 1 /*! 2 * Simple Menu Order Column 2 * Simple Menu Order Column - Vanilla JS Version 3 3 * 4 4 * https://github.com/ChillCode/simple-menu-order-column/ 5 5 * 6 * Copyright (C) 20 24ChillCode6 * Copyright (C) 2003-2025 ChillCode 7 7 * 8 8 * @license Released under the General Public License v3.0 https://www.gnu.org/licenses/gpl-3.0.html 9 9 */ 10 (function ($) { 11 const { __, _x, _n, _nx } = wp.i18n; 12 $.fn.smocDoReorder = function (currentObject) { 13 const reorderCurrentProduct = this; 14 15 if (!reorderCurrentProduct || $(currentObject).prop('disabled')) { 16 return false; 17 } 18 19 function disableInput(errorContainer, errorMessage, disable) { 20 /** Reset input value. */ 21 currentObject.value = currentObject.defaultValue; 22 /** Show error icon. */ 23 errorContainer && errorContainer.css('display', 'inline-block'); 24 /** Disable input field. */ 25 $(currentObject).prop('disabled', disable).prop('title', errorMessage); 26 /** Output message to widow console. */ 27 window.console.warn('[Simple Menu Order Column] ' + errorMessage); 28 }; 29 30 let reorderPostID = $(reorderCurrentProduct).data('post-id'); 31 32 if (!reorderPostID || isNaN(reorderPostID)) { 33 disableInput(null, __( 'The post_id is invalid.', 'simple-menu-order-column' ), true); 34 return false; 35 } 36 37 reorderPostID = parseInt(reorderPostID); 38 39 /** 40 * Create loader and result containers. 41 */ 42 const reorderLoaderID = 'smoc-' + reorderPostID.toString(); 43 44 const reorderResultContainer = $(reorderCurrentProduct).closest('.smoc-container'); 45 46 /** 47 * Create loader. 48 */ 49 let reorderLoaderSelector = $('#' + reorderLoaderID + '-loader'); 50 51 if (!reorderLoaderSelector.length) { 52 reorderLoaderSelector = $('<span>') 53 .attr({ 54 id: reorderLoaderID + '-loader', 55 class: 'smoc-loader dashicons dashicons-update', 56 role: 'img', 57 'aria-label': __( 'Updating menu order...', 'simple-menu-order-column' ), 58 }) 59 .css({ 60 color: '#2ea2cc', 61 animation: 'iconrotation 2s infinite linear', 62 display: 'inline-block', 63 }); 64 } 65 66 let reorderLoaderSelectorContainer = $( 67 '#' + reorderLoaderID + '-loader-container' 68 ); 69 70 if (!reorderLoaderSelectorContainer.length) { 71 reorderLoaderSelectorContainer = $('<div>') 72 .attr({ 73 id: reorderLoaderID + '-loader-container', 74 }) 75 .css({ 76 'padding-top': '5px', 77 display: 'none', 78 }); 79 80 reorderLoaderSelectorContainer.append(reorderLoaderSelector); 81 82 reorderResultContainer.append(reorderLoaderSelectorContainer); 83 } else { 84 reorderLoaderSelectorContainer.css({ display: 'none' }); 85 } 86 87 /** 88 * Create Success result. 89 */ 90 91 let reorderSuccessSelector = $('#' + reorderLoaderID + '-success'); 92 93 if (!reorderSuccessSelector.length) { 94 reorderSuccessSelector = $('<span>') 95 .attr({ 96 id: reorderLoaderID + '-success', 97 class: 'smoc-success dashicons dashicons-yes-alt', 98 role: 'img', 99 'aria-label': __( 'The menu order has been updated successfully.', 'simple-menu-order-column' ), 100 }) 101 .css({ 102 'padding-top': '5px', 103 color: '#7ad03a', 104 display: 'none', 105 }); 106 107 reorderResultContainer.append(reorderSuccessSelector); 108 } else { 109 reorderSuccessSelector.css({ display: 'none' }); 110 } 111 112 /** 113 * Create Error result. 114 */ 115 116 let reorderErrorSelector = $('#' + reorderLoaderID + '-error'); 117 118 if (!reorderErrorSelector.length) { 119 reorderErrorSelector = $('<span>') 120 .attr({ 121 id: reorderLoaderID + '-error', 122 class: 'smoc-error dashicons dashicons-dismiss', 123 role: 'img', 124 'aria-label': __( 'An error ocurred while updating menu order.', 'simple-menu-order-column' ), 125 }) 126 .css({ 127 'padding-top': '5px', 128 color: '#a00', 129 display: 'none', 130 }); 131 132 reorderResultContainer.append(reorderErrorSelector); 133 } else { 134 reorderErrorSelector.css({ display: 'none' }); 135 } 136 137 /** 138 * Check WP configuration. 139 */ 140 if (!typenow || !ajaxurl) { 141 disableInput(reorderErrorSelector, __( 'Invalid WP installation, variables typenow or ajaxurl are not initialized.', 'simple-menu-order-column' ), true); 142 return false; 143 } 144 145 let reorderPostMenuOrder = $(reorderCurrentProduct).val(); 146 147 /** 148 * Populate and validate Product Order 149 */ 150 reorderPostMenuOrder = $(reorderCurrentProduct).val(); 151 152 if (!reorderPostMenuOrder || isNaN(reorderPostMenuOrder)) { 153 disableInput(reorderErrorSelector, __( 'The menu order value is invalid.', 'simple-menu-order-column' ), false); 154 return false; 155 } 156 157 reorderPostMenuOrder = parseInt(reorderPostMenuOrder); 158 159 /** 160 * Populate wpnonce 161 */ 162 let postNonce = $(reorderCurrentProduct).data('wpnonce'); 163 164 if (!postNonce) { 165 disableInput(reorderErrorSelector, __( 'The postNonce is invalid.', 'simple-menu-order-column' ), true); 166 return false; 167 } 168 169 /** 170 * Disable INPUT while doing ajax 171 */ 172 $(currentObject).prop('disabled', true); 173 174 reorderLoaderSelectorContainer.css({ display: 'inline-block' }); 175 176 /** 177 * Format POST URL. 178 */ 179 const searchParams = new URLSearchParams(); 180 181 searchParams.set('action', 'smoc_reorder'); 182 searchParams.set('_wpnonce', postNonce); 183 184 const request = jQuery.ajax({ 185 url: ajaxurl + '?' + searchParams, 186 type: 'POST', 187 data: { 188 post_type: typenow, 189 post_id: reorderPostID, 190 post_menu_order: reorderPostMenuOrder, 191 }, 192 }); 193 194 request.done(function (response) { 195 if (response.success) { 196 reorderSuccessSelector.css('display', 'inline-block'); 197 198 $(currentObject).prop('title', reorderPostMenuOrder); 199 200 currentObject.currentValue = reorderPostMenuOrder; 201 currentObject.defaultValue = reorderPostMenuOrder; 202 // If success go to next product. 203 const currentObjectPosition = $(':input[id^=smoc]').index(currentObject); 204 $(':input[id^=smoc]').eq(currentObjectPosition + 1).trigger('select'); 205 } else { 206 currentObject.value = currentObject.defaultValue; 207 208 reorderErrorSelector.css('display', 'inline-block'); 209 } 210 }); 211 212 request.fail(function () { 213 currentObject.value = currentObject.defaultValue; 214 215 reorderLoaderSelectorContainer.css('display', 'none'); 216 reorderSuccessSelector.css('display', 'none'); 217 reorderErrorSelector.css('display', 'inline-block'); 218 }); 219 220 request.always(function () { 221 reorderLoaderSelectorContainer.css({ display: 'none' }); 222 223 /** Enable INPUT after doing Ajax */ 224 $(currentObject).prop('disabled', false); 225 }); 226 }; 227 228 $('input[id^=smoc]').on('focus', function () { 229 this.currentValue = this.value; 230 231 $(this).prop('title', parseInt(this.value)); 232 233 const reorderLoaderID = 'smoc-' + $(this).data('post-id').toString(); 234 235 $('#' + reorderLoaderID + '-loader-container').css({ display: 'none' }); 236 $('#' + reorderLoaderID + '-success').css({ display: 'none' }); 237 $('#' + reorderLoaderID + '-error').css({ display: 'none' }); 238 }); 239 240 $('input[id^=smoc]').on('focusout', function (e) { 241 if ($(this).prop('disabled')) { 242 return false; 243 } 244 245 if (this.currentValue !== this.value) { 246 if (window.confirm(__( 'Should the menu order value be updated?', 'simple-menu-order-column' ))) { 247 $(this).smocDoReorder(this); 248 } else { 249 this.value = this.defaultValue; 250 } 251 } 252 }); 253 254 $('input[id^=smoc]').on('keypress', function (e) { 255 if (e.key === 'Enter') { 256 e.preventDefault(); 257 258 $(this).smocDoReorder(this); 259 } 260 }); 261 })(jQuery); 10 (function () { 11 12 const { __ } = wp.i18n; 13 14 function smocInit() { 15 document.removeEventListener("DOMContentLoaded", smocInit); 16 window.removeEventListener("load", smocInit); 17 18 const smocInputs = document.querySelectorAll('input[id^=smoc]'); 19 20 smocInputs.forEach(smocInput => { 21 smocInput.addEventListener('focus', () => { 22 smocInput.currentValue = smocInput.value; 23 smocInput.title = parseInt(smocInput.value); 24 25 const { postId } = smocInput.dataset; 26 if (!postId) { return; }; 27 28 const hideElement = id => { 29 const smocElement = document.getElementById(id); 30 if (smocElement) { smocElement.style.display = 'none'; } 31 }; 32 33 const smocBaseId = `smoc-${postId}`; 34 35 hideElement(`${smocBaseId}-loader-container`); 36 hideElement(`${smocBaseId}-success`); 37 hideElement(`${smocBaseId}-error`); 38 }); 39 40 smocInput.addEventListener('focusout', () => { 41 if (smocInput.disabled) { return; }; 42 43 if (smocInput.currentValue !== smocInput.value) { 44 if (window.confirm(__('Should the menu order value be updated?', 'simple-menu-order-column'))) { 45 smocDoReorder(smocInput); 46 } else { 47 smocInput.value = smocInput.defaultValue; 48 } 49 } 50 }); 51 52 smocInput.addEventListener('keydown', (smocKeydownEvent) => { 53 const allowedKeys = [ 54 'Backspace', 'Tab', 'ArrowLeft', 'ArrowRight', 55 'ArrowUp', 'ArrowDown', 'Delete', 'Home', 'End', 'Enter' 56 ]; 57 58 // Allow: Ctrl/Cmd + A/C/V/X 59 if ((smocKeydownEvent.ctrlKey || smocKeydownEvent.metaKey) && ['a', 'c', 'v', 'x'].includes(smocKeydownEvent.key.toLowerCase())) { return; } 60 61 // Allow navigation/edit keys 62 if (allowedKeys.includes(smocKeydownEvent.key)) { return; } 63 64 // Block any key that is not a digit (0-9) 65 if (!/^\d$/.test(smocKeydownEvent.key)) { 66 smocKeydownEvent.preventDefault(); 67 } 68 }); 69 70 smocInput.addEventListener('paste', (smocPasteEvent) => { 71 const pasted = smocPasteEvent.clipboardData.getData('text'); 72 if (!/^\d+$/.test(pasted)) { 73 smocPasteEvent.preventDefault(); 74 } 75 }); 76 77 smocInput.addEventListener('keypress', smocKeypressEvent => { 78 if (smocKeypressEvent.key === 'Enter') { 79 smocKeypressEvent.preventDefault(); 80 smocDoReorder(smocInput); 81 } 82 }); 83 }); 84 } 85 86 function smocDoReorder(smocInput) { 87 if (!smocInput || smocInput.disabled) { return; } 88 89 const smocContainer = smocInput.closest('.smoc-container'), postId = parseInt(smocInput.dataset.postId); 90 91 if (!postId || isNaN(postId)) { 92 disableInput(null, __('The post_id is invalid.', 'simple-menu-order-column'), true, smocInput); 93 return; 94 } 95 96 const loaderId = `smoc-${postId}`, menuOrder = parseInt(smocInput.value); 97 98 if (isNaN(menuOrder)) { 99 const errorEl = document.getElementById(`${loaderId}-error`); 100 disableInput(errorEl, __('The menu order value is invalid.', 'simple-menu-order-column'), false, smocInput); 101 return; 102 } 103 104 const nonce = smocInput.dataset.wpnonce; 105 if (!nonce) { 106 const errorEl = document.getElementById(`${loaderId}-error`); 107 disableInput(errorEl, __('The postNonce is invalid.', 'simple-menu-order-column'), true, smocInput); 108 return; 109 } 110 111 if (typeof ajaxurl === 'undefined' || typeof typenow === 'undefined') { 112 const errorEl = document.getElementById(`${loaderId}-error`); 113 disableInput(errorEl, __('Invalid WP installation, variables typenow or ajaxurl are not initialized.', 'simple-menu-order-column'), true, smocInput); 114 return; 115 } 116 117 smocInput.disabled = true; 118 119 const showLoader = () => { 120 let smocLoaderContainer = document.getElementById(`${loaderId}-loader-container`); 121 if (!smocLoaderContainer) { 122 const smocLoader = document.createElement('span'); 123 smocLoader.id = `${loaderId}-loader`; 124 smocLoader.className = 'smoc-loader dashicons dashicons-update'; 125 smocLoader.setAttribute('role', 'img'); 126 smocLoader.setAttribute('aria-label', __('Updating menu order...', 'simple-menu-order-column')); 127 smocLoader.style.cssText = 'color: #2ea2cc; animation: iconrotation 2s infinite linear; display: inline-block;'; 128 129 smocLoaderContainer = document.createElement('div'); 130 smocLoaderContainer.id = `${loaderId}-loader-container`; 131 smocLoaderContainer.style.cssText = 'padding-top: 5px; display: inline-block;'; 132 smocLoaderContainer.appendChild(smocLoader); 133 smocContainer.appendChild(smocLoaderContainer); 134 } else { 135 smocLoaderContainer.style.display = 'inline-block'; 136 } 137 }; 138 139 const showSuccess = () => { 140 let smocSuccess = document.getElementById(`${loaderId}-success`); 141 if (!smocSuccess) { 142 smocSuccess = document.createElement('span'); 143 smocSuccess.id = `${loaderId}-success`; 144 smocSuccess.className = 'smoc-success dashicons dashicons-yes-alt'; 145 smocSuccess.setAttribute('role', 'img'); 146 smocSuccess.setAttribute('aria-label', __('The menu order has been updated successfully.', 'simple-menu-order-column')); 147 smocSuccess.style.cssText = 'padding-top: 5px; color: #7ad03a; display: inline-block;'; 148 smocContainer.appendChild(smocSuccess); 149 } else { 150 smocSuccess.style.display = 'inline-block'; 151 } 152 }; 153 154 const showError = () => { 155 let smocError = document.getElementById(`${loaderId}-error`); 156 if (!smocError) { 157 smocError = document.createElement('span'); 158 smocError.id = `${loaderId}-error`; 159 smocError.className = 'smoc-error dashicons dashicons-dismiss'; 160 smocError.setAttribute('role', 'img'); 161 smocError.setAttribute('aria-label', __('An error ocurred while updating menu order.', 'simple-menu-order-column')); 162 smocError.style.cssText = 'padding-top: 5px; color: #a00; display: inline-block;'; 163 smocContainer.appendChild(smocError); 164 } else { 165 smocError.style.display = 'inline-block'; 166 } 167 }; 168 169 const hideLoader = () => { 170 const smocLoaderContainer = document.getElementById(`${loaderId}-loader-container`); 171 if (smocLoaderContainer) { smocLoaderContainer.style.display = 'none'; } 172 }; 173 174 showLoader(); 175 176 fetch(`${ajaxurl}?action=smoc_reorder&_wpnonce=${encodeURIComponent(nonce)}`, { 177 method: 'POST', 178 headers: { 179 'Content-Type': 'application/x-www-form-urlencoded', 180 }, 181 body: new URLSearchParams({ 182 post_type: typenow, 183 post_id: postId, 184 post_menu_order: menuOrder, 185 }), 186 }) 187 .then(res => res.json()) 188 .then(response => { 189 if (response.success) { 190 showSuccess(); 191 smocInput.title = menuOrder; 192 smocInput.currentValue = menuOrder; 193 smocInput.defaultValue = menuOrder; 194 195 const inputs = Array.from(document.querySelectorAll('input[id^=smoc]')), pos = inputs.indexOf(smocInput) + 1; 196 if (inputs[pos]) { inputs[pos].select(); } 197 } else { 198 smocInput.value = smocInput.defaultValue; 199 showError(); 200 } 201 }) 202 .catch(() => { 203 smocInput.value = smocInput.defaultValue; 204 hideLoader(); 205 showError(); 206 }) 207 .finally(() => { 208 hideLoader(); 209 smocInput.disabled = false; 210 }); 211 } 212 213 function disableInput(errorContainer, message, disable, input) { 214 input.value = input.defaultValue; 215 if (errorContainer) { errorContainer.style.display = 'inline-block'; } 216 input.disabled = disable; 217 input.title = message; 218 console.warn(`[Simple Menu Order Column] ${message}`); 219 } 220 221 /** 222 * Initilize Script 223 */ 224 if (document.readyState === "loading") { 225 document.addEventListener('DOMContentLoaded', smocInit); 226 window.addEventListener("load", smocInit); 227 } else { 228 window.setTimeout(smocInit); 229 } 230 })(); -
simple-menu-order-column/tags/2.0.0/assets/js/simple-menu-order-column.min.js
r3259831 r3322001 1 1 /*! 2 * Simple Menu Order Column 2 * Simple Menu Order Column - Vanilla JS Version 3 3 * 4 4 * https://github.com/ChillCode/simple-menu-order-column/ 5 5 * 6 * Copyright (C) 20 24ChillCode6 * Copyright (C) 2003-2025 ChillCode 7 7 * 8 8 * @license Released under the General Public License v3.0 https://www.gnu.org/licenses/gpl-3.0.html 9 9 */ 10 !function( e){const{__:s,_x:n,_n:i,_nx:o}=wp.i18n;e.fn.smocDoReorder=function(n){const i=this;if(!i||e(n).prop("disabled"))return!1;function o(s,i,o){n.value=n.defaultValue,s&&s.css("display","inline-block"),e(n).prop("disabled",o).prop("title",i),window.console.warn("[Simple Menu Order Column] "+i)}let a=e(i).data("post-id");if(!a||isNaN(a))return o(null,s("The post_id is invalid.","simple-menu-order-column"),!0),!1;a=parseInt(a);const l="smoc-"+a.toString(),r=e(i).closest(".smoc-container");let t=e("#"+l+"-loader");t.length||(t=e("<span>").attr({id:l+"-loader",class:"smoc-loader dashicons dashicons-update",role:"img","aria-label":s("Updating menu order...","simple-menu-order-column")}).css({color:"#2ea2cc",animation:"iconrotation 2s infinite linear",display:"inline-block"}));let d=e("#"+l+"-loader-container");d.length?d.css({display:"none"}):(d=e("<div>").attr({id:l+"-loader-container"}).css({"padding-top":"5px",display:"none"}),d.append(t),r.append(d));let c=e("#"+l+"-success");c.length?c.css({display:"none"}):(c=e("<span>").attr({id:l+"-success",class:"smoc-success dashicons dashicons-yes-alt",role:"img","aria-label":s("The menu order has been updated successfully.","simple-menu-order-column")}).css({"padding-top":"5px",color:"#7ad03a",display:"none"}),r.append(c));let p=e("#"+l+"-error");if(p.length?p.css({display:"none"}):(p=e("<span>").attr({id:l+"-error",class:"smoc-error dashicons dashicons-dismiss",role:"img","aria-label":s("An error ocurred while updating menu order.","simple-menu-order-column")}).css({"padding-top":"5px",color:"#a00",display:"none"}),r.append(p)),!typenow||!ajaxurl)return o(p,s("Invalid WP installation, variables typenow or ajaxurl are not initialized.","simple-menu-order-column"),!0),!1;let u=e(i).val();if(u=e(i).val(),!u||isNaN(u))return o(p,s("The menu order value is invalid.","simple-menu-order-column"),!1),!1;u=parseInt(u);let m=e(i).data("wpnonce");if(!m)return o(p,s("The postNonce is invalid.","simple-menu-order-column"),!0),!1;e(n).prop("disabled",!0),d.css({display:"inline-block"});const h=new URLSearchParams;h.set("action","smoc_reorder"),h.set("_wpnonce",m);const y=jQuery.ajax({url:ajaxurl+"?"+h,type:"POST",data:{post_type:typenow,post_id:a,post_menu_order:u}});y.done((function(s){if(s.success){c.css("display","inline-block"),e(n).prop("title",u),n.currentValue=u,n.defaultValue=u;const s=e(":input[id^=smoc]").index(n);e(":input[id^=smoc]").eq(s+1).trigger("select")}else n.value=n.defaultValue,p.css("display","inline-block")})),y.fail((function(){n.value=n.defaultValue,d.css("display","none"),c.css("display","none"),p.css("display","inline-block")})),y.always((function(){d.css({display:"none"}),e(n).prop("disabled",!1)}))},e("input[id^=smoc]").on("focus",(function(){this.currentValue=this.value,e(this).prop("title",parseInt(this.value));const s="smoc-"+e(this).data("post-id").toString();e("#"+s+"-loader-container").css({display:"none"}),e("#"+s+"-success").css({display:"none"}),e("#"+s+"-error").css({display:"none"})})),e("input[id^=smoc]").on("focusout",(function(n){if(e(this).prop("disabled"))return!1;this.currentValue!==this.value&&(window.confirm(s("Should the menu order value be updated?","simple-menu-order-column"))?e(this).smocDoReorder(this):this.value=this.defaultValue)})),e("input[id^=smoc]").on("keypress",(function(s){"Enter"===s.key&&(s.preventDefault(),e(this).smocDoReorder(this))}))}(jQuery);10 !function(){const{__:e}=wp.i18n;function t(){document.removeEventListener("DOMContentLoaded",t),window.removeEventListener("load",t);document.querySelectorAll("input[id^=smoc]").forEach(t=>{t.addEventListener("focus",()=>{t.currentValue=t.value,t.title=parseInt(t.value);const{postId:e}=t.dataset;if(!e)return;const n=e=>{const t=document.getElementById(e);t&&(t.style.display="none")},o=`smoc-${e}`;n(`${o}-loader-container`),n(`${o}-success`),n(`${o}-error`)}),t.addEventListener("focusout",()=>{t.disabled||t.currentValue!==t.value&&(window.confirm(e("Should the menu order value be updated?","simple-menu-order-column"))?n(t):t.value=t.defaultValue)}),t.addEventListener("keydown",e=>{(e.ctrlKey||e.metaKey)&&["a","c","v","x"].includes(e.key.toLowerCase())||["Backspace","Tab","ArrowLeft","ArrowRight","ArrowUp","ArrowDown","Delete","Home","End","Enter"].includes(e.key)||/^\d$/.test(e.key)||e.preventDefault()}),t.addEventListener("paste",e=>{const t=e.clipboardData.getData("text");/^\d+$/.test(t)||e.preventDefault()}),t.addEventListener("keypress",e=>{"Enter"===e.key&&(e.preventDefault(),n(t))})})}function n(t){if(!t||t.disabled)return;const n=t.closest(".smoc-container"),d=parseInt(t.dataset.postId);if(!d||isNaN(d))return void o(null,e("The post_id is invalid.","simple-menu-order-column"),!0,t);const l=`smoc-${d}`,a=parseInt(t.value);if(isNaN(a)){return void o(document.getElementById(`${l}-error`),e("The menu order value is invalid.","simple-menu-order-column"),!1,t)}const s=t.dataset.wpnonce;if(!s){return void o(document.getElementById(`${l}-error`),e("The postNonce is invalid.","simple-menu-order-column"),!0,t)}if("undefined"==typeof ajaxurl||"undefined"==typeof typenow){return void o(document.getElementById(`${l}-error`),e("Invalid WP installation, variables typenow or ajaxurl are not initialized.","simple-menu-order-column"),!0,t)}t.disabled=!0;const r=()=>{let t=document.getElementById(`${l}-error`);t?t.style.display="inline-block":(t=document.createElement("span"),t.id=`${l}-error`,t.className="smoc-error dashicons dashicons-dismiss",t.setAttribute("role","img"),t.setAttribute("aria-label",e("An error ocurred while updating menu order.","simple-menu-order-column")),t.style.cssText="padding-top: 5px; color: #a00; display: inline-block;",n.appendChild(t))},i=()=>{const e=document.getElementById(`${l}-loader-container`);e&&(e.style.display="none")};(()=>{let t=document.getElementById(`${l}-loader-container`);if(t)t.style.display="inline-block";else{const o=document.createElement("span");o.id=`${l}-loader`,o.className="smoc-loader dashicons dashicons-update",o.setAttribute("role","img"),o.setAttribute("aria-label",e("Updating menu order...","simple-menu-order-column")),o.style.cssText="color: #2ea2cc; animation: iconrotation 2s infinite linear; display: inline-block;",t=document.createElement("div"),t.id=`${l}-loader-container`,t.style.cssText="padding-top: 5px; display: inline-block;",t.appendChild(o),n.appendChild(t)}})(),fetch(`${ajaxurl}?action=smoc_reorder&_wpnonce=${encodeURIComponent(s)}`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({post_type:typenow,post_id:d,post_menu_order:a})}).then(e=>e.json()).then(o=>{if(o.success){(()=>{let t=document.getElementById(`${l}-success`);t?t.style.display="inline-block":(t=document.createElement("span"),t.id=`${l}-success`,t.className="smoc-success dashicons dashicons-yes-alt",t.setAttribute("role","img"),t.setAttribute("aria-label",e("The menu order has been updated successfully.","simple-menu-order-column")),t.style.cssText="padding-top: 5px; color: #7ad03a; display: inline-block;",n.appendChild(t))})(),t.title=a,t.currentValue=a,t.defaultValue=a;const o=Array.from(document.querySelectorAll("input[id^=smoc]")),d=o.indexOf(t)+1;o[d]&&o[d].select()}else t.value=t.defaultValue,r()}).catch(()=>{t.value=t.defaultValue,i(),r()}).finally(()=>{i(),t.disabled=!1})}function o(e,t,n,o){o.value=o.defaultValue,e&&(e.style.display="inline-block"),o.disabled=n,o.title=t,console.warn(`[Simple Menu Order Column] ${t}`)}"loading"===document.readyState?(document.addEventListener("DOMContentLoaded",t),window.addEventListener("load",t)):window.setTimeout(t)}(); -
simple-menu-order-column/tags/2.0.0/changelog.txt
r3259831 r3322001 1 1 == Changelog == 2 3 = 2.0.0 2025-07-02 = 4 5 * Vanilla Javascript, jQuery requeriment removed. 6 * Minor PHPDoc fixes. 7 * Minor code fixes. 2 8 3 9 = 1.0.2 2024-12-16 = -
simple-menu-order-column/tags/2.0.0/includes/class-simplemenuordercolumn.php
r3259831 r3322001 5 5 * @package SimpleMenuOrderColumn 6 6 * 7 * Copyright: (c) 2003-202 2Chillcode7 * Copyright: (c) 2003-2025 Chillcode 8 8 */ 9 9 … … 22 22 * The single instance of the class. 23 23 * 24 * @var SimpleMenuOrderColumn 24 * @var SimpleMenuOrderColumn|null 25 25 */ 26 26 private static $smoc_instace; … … 31 31 * We allow all WP_Post since has menu_order column and are sortable. 32 32 * 33 * @var array 33 * @var array{0: 'post', 1: 'page', 2: 'product', 3: 'attachment'} 34 34 */ 35 35 private static $smoc_allowed_types = array( 'post', 'page', 'product', 'attachment' ); … … 88 88 /** Add only on listings pages and compatible post types. */ 89 89 $current_screen = get_current_screen(); 90 91 if ( null === $current_screen ) { 92 return; 93 } 90 94 91 95 if ( … … 100 104 101 105 if ( 'upload' === $current_screen->base ) { 102 /** This filteris called directly. */103 add_ filter( 'manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 );106 /** This action is called directly. */ 107 add_action( 'manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 ); 104 108 } else { 105 109 add_action( 'manage_' . $current_screen->post_type . '_posts_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 ); … … 111 115 /** 112 116 * Enqueue scripts. 117 * 118 * @return void 113 119 */ 114 120 public function admin_enqueue_scripts() { 115 121 $wp_scripts_get_suffix = wp_scripts_get_suffix(); 116 122 117 wp_enqueue_script( 'simple-menu-order-column', plugins_url( 'assets/js/simple-menu-order-column' . $wp_scripts_get_suffix . '.js', SMOC_PLUGIN_FILE ), array( ' jquery', 'wp-i18n' ), SMOC_PLUGIN_VERSION, true );123 wp_enqueue_script( 'simple-menu-order-column', plugins_url( 'assets/js/simple-menu-order-column' . $wp_scripts_get_suffix . '.js', SMOC_PLUGIN_FILE ), array( 'wp-i18n' ), SMOC_PLUGIN_VERSION, true ); 118 124 wp_enqueue_style( 'simple-menu-order-column', plugins_url( 'assets/css/simple-menu-order-column' . $wp_scripts_get_suffix . '.css', SMOC_PLUGIN_FILE ), array(), SMOC_PLUGIN_VERSION ); 119 125 … … 124 130 * Allowed post_types. 125 131 * 126 * @return array 132 * @return array{0: 'post', 1: 'page', 2: 'product', 3: 'attachment'} 127 133 */ 128 134 public static function get_allowed_types() { … … 172 178 * @param int $post_id Post id. 173 179 * @param int $post_menu_order Post order. 180 * @return WP_Error|int The post ID on success. The value 0 or WP_Error on failure. 174 181 */ 175 182 private static function set_post_menu_order( int $post_id, int $post_menu_order ) { 176 177 $post = get_post( $post_id ); 178 179 if ( ! $post ) { 180 return new WP_Error(); 181 } 182 183 $post->menu_order = $post_menu_order; 184 185 return wp_update_post( $post, true ); 183 return wp_update_post( 184 array( 185 'ID' => $post_id, 186 'menu_order' => $post_menu_order, 187 ), 188 true 189 ); 186 190 } 187 191 … … 191 195 * @param int $post_id Post id. 192 196 * @param int $post_menu_order Post order. 197 * @return void 193 198 */ 194 199 private static function output_menu_order_column( int $post_id, int $post_menu_order ) { … … 207 212 * @param string $column Column name. 208 213 * @param int $postid Post order. 214 * @return void 209 215 */ 210 216 public static function manage_posts_custom_column( $column, $postid ) { … … 212 218 $post = get_post( $postid ); 213 219 220 if ( null === $post ) { 221 return; 222 } 223 214 224 self::output_menu_order_column( $postid, $post->menu_order ); 215 225 } … … 219 229 * Add menu order column. 220 230 * 221 * @param array$columns Post list columns.222 * @return array231 * @param string[] $columns Post list columns. 232 * @return string[] 223 233 */ 224 234 public static function manage_edit_columns( $columns ) { 225 235 $columns['menu_order'] = esc_html__( 'Order', 'simple-menu-order-column' ); 226 227 236 return $columns; 228 237 } … … 231 240 * Add menu order column to sortable columns. 232 241 * 233 * @param array$sortable_columns Post list columns.234 * @return array242 * @param string[] $sortable_columns Post list columns. 243 * @return string[] 235 244 */ 236 245 public static function manage_edit_sortable_columns( $sortable_columns ) { … … 245 254 */ 246 255 public static function instance() { 247 if ( is_null( self::$smoc_instace )) {256 if ( ! self::$smoc_instace instanceof SimpleMenuOrderColumn ) { 248 257 self::$smoc_instace = new self(); 249 258 } -
simple-menu-order-column/tags/2.0.0/readme.txt
r3259831 r3322001 3 3 Tags: menu order, pages, media, posts, products 4 4 Requires at least: 6.0 5 Tested up to: 6. 75 Tested up to: 6.8 6 6 Requires PHP: 7.4 7 Stable tag: 1.0.27 Stable tag: 2.0.0 8 8 License: GPLv3 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.html … … 67 67 == Changelog == 68 68 69 = 1.0.2 2024-12-16=69 = 2.0.0 2025-07-02 = 70 70 71 * Update - Minor code changes.72 * Fix - Localize Javascript.73 * Add - Added products listing screenshot.71 * Vanilla Javascript, no jQuery. 72 * Minor PHPDoc fixes. 73 * Minor code fixes. 74 74 75 75 == Upgrade Notice == … … 81 81 Minor code changes. 82 82 Localize Javascript. 83 84 = 2.0.0 = 85 Vanilla Javascript, no jQuery. 86 Minor PHPDoc fixes. 87 Minor code fixes. 83 88 84 89 == Screenshots == -
simple-menu-order-column/tags/2.0.0/simple-menu-order-column.php
r3259831 r3322001 12 12 * Plugin URI: https://github.com/chillcode/simple-menu-order-column 13 13 * Description: Add a menu order column to your listings. 14 * Version: 1.0.214 * Version: 2.0.0 15 15 * Requires at least: 6.0 16 16 * Requires PHP: 7.4 … … 27 27 define( 'SMOC_PLUGIN_PATH', __DIR__ ); 28 28 define( 'SMOC_PLUGIN_FILE', __FILE__ ); 29 define( 'SMOC_PLUGIN_VERSION', ' 1.0.2' );29 define( 'SMOC_PLUGIN_VERSION', '2.0.0' ); 30 30 31 31 require_once SMOC_PLUGIN_PATH . '/includes/class-simplemenuordercolumn.php'; -
simple-menu-order-column/trunk/assets/js/simple-menu-order-column.js
r3259831 r3322001 1 1 /*! 2 * Simple Menu Order Column 2 * Simple Menu Order Column - Vanilla JS Version 3 3 * 4 4 * https://github.com/ChillCode/simple-menu-order-column/ 5 5 * 6 * Copyright (C) 20 24ChillCode6 * Copyright (C) 2003-2025 ChillCode 7 7 * 8 8 * @license Released under the General Public License v3.0 https://www.gnu.org/licenses/gpl-3.0.html 9 9 */ 10 (function ($) { 11 const { __, _x, _n, _nx } = wp.i18n; 12 $.fn.smocDoReorder = function (currentObject) { 13 const reorderCurrentProduct = this; 14 15 if (!reorderCurrentProduct || $(currentObject).prop('disabled')) { 16 return false; 17 } 18 19 function disableInput(errorContainer, errorMessage, disable) { 20 /** Reset input value. */ 21 currentObject.value = currentObject.defaultValue; 22 /** Show error icon. */ 23 errorContainer && errorContainer.css('display', 'inline-block'); 24 /** Disable input field. */ 25 $(currentObject).prop('disabled', disable).prop('title', errorMessage); 26 /** Output message to widow console. */ 27 window.console.warn('[Simple Menu Order Column] ' + errorMessage); 28 }; 29 30 let reorderPostID = $(reorderCurrentProduct).data('post-id'); 31 32 if (!reorderPostID || isNaN(reorderPostID)) { 33 disableInput(null, __( 'The post_id is invalid.', 'simple-menu-order-column' ), true); 34 return false; 35 } 36 37 reorderPostID = parseInt(reorderPostID); 38 39 /** 40 * Create loader and result containers. 41 */ 42 const reorderLoaderID = 'smoc-' + reorderPostID.toString(); 43 44 const reorderResultContainer = $(reorderCurrentProduct).closest('.smoc-container'); 45 46 /** 47 * Create loader. 48 */ 49 let reorderLoaderSelector = $('#' + reorderLoaderID + '-loader'); 50 51 if (!reorderLoaderSelector.length) { 52 reorderLoaderSelector = $('<span>') 53 .attr({ 54 id: reorderLoaderID + '-loader', 55 class: 'smoc-loader dashicons dashicons-update', 56 role: 'img', 57 'aria-label': __( 'Updating menu order...', 'simple-menu-order-column' ), 58 }) 59 .css({ 60 color: '#2ea2cc', 61 animation: 'iconrotation 2s infinite linear', 62 display: 'inline-block', 63 }); 64 } 65 66 let reorderLoaderSelectorContainer = $( 67 '#' + reorderLoaderID + '-loader-container' 68 ); 69 70 if (!reorderLoaderSelectorContainer.length) { 71 reorderLoaderSelectorContainer = $('<div>') 72 .attr({ 73 id: reorderLoaderID + '-loader-container', 74 }) 75 .css({ 76 'padding-top': '5px', 77 display: 'none', 78 }); 79 80 reorderLoaderSelectorContainer.append(reorderLoaderSelector); 81 82 reorderResultContainer.append(reorderLoaderSelectorContainer); 83 } else { 84 reorderLoaderSelectorContainer.css({ display: 'none' }); 85 } 86 87 /** 88 * Create Success result. 89 */ 90 91 let reorderSuccessSelector = $('#' + reorderLoaderID + '-success'); 92 93 if (!reorderSuccessSelector.length) { 94 reorderSuccessSelector = $('<span>') 95 .attr({ 96 id: reorderLoaderID + '-success', 97 class: 'smoc-success dashicons dashicons-yes-alt', 98 role: 'img', 99 'aria-label': __( 'The menu order has been updated successfully.', 'simple-menu-order-column' ), 100 }) 101 .css({ 102 'padding-top': '5px', 103 color: '#7ad03a', 104 display: 'none', 105 }); 106 107 reorderResultContainer.append(reorderSuccessSelector); 108 } else { 109 reorderSuccessSelector.css({ display: 'none' }); 110 } 111 112 /** 113 * Create Error result. 114 */ 115 116 let reorderErrorSelector = $('#' + reorderLoaderID + '-error'); 117 118 if (!reorderErrorSelector.length) { 119 reorderErrorSelector = $('<span>') 120 .attr({ 121 id: reorderLoaderID + '-error', 122 class: 'smoc-error dashicons dashicons-dismiss', 123 role: 'img', 124 'aria-label': __( 'An error ocurred while updating menu order.', 'simple-menu-order-column' ), 125 }) 126 .css({ 127 'padding-top': '5px', 128 color: '#a00', 129 display: 'none', 130 }); 131 132 reorderResultContainer.append(reorderErrorSelector); 133 } else { 134 reorderErrorSelector.css({ display: 'none' }); 135 } 136 137 /** 138 * Check WP configuration. 139 */ 140 if (!typenow || !ajaxurl) { 141 disableInput(reorderErrorSelector, __( 'Invalid WP installation, variables typenow or ajaxurl are not initialized.', 'simple-menu-order-column' ), true); 142 return false; 143 } 144 145 let reorderPostMenuOrder = $(reorderCurrentProduct).val(); 146 147 /** 148 * Populate and validate Product Order 149 */ 150 reorderPostMenuOrder = $(reorderCurrentProduct).val(); 151 152 if (!reorderPostMenuOrder || isNaN(reorderPostMenuOrder)) { 153 disableInput(reorderErrorSelector, __( 'The menu order value is invalid.', 'simple-menu-order-column' ), false); 154 return false; 155 } 156 157 reorderPostMenuOrder = parseInt(reorderPostMenuOrder); 158 159 /** 160 * Populate wpnonce 161 */ 162 let postNonce = $(reorderCurrentProduct).data('wpnonce'); 163 164 if (!postNonce) { 165 disableInput(reorderErrorSelector, __( 'The postNonce is invalid.', 'simple-menu-order-column' ), true); 166 return false; 167 } 168 169 /** 170 * Disable INPUT while doing ajax 171 */ 172 $(currentObject).prop('disabled', true); 173 174 reorderLoaderSelectorContainer.css({ display: 'inline-block' }); 175 176 /** 177 * Format POST URL. 178 */ 179 const searchParams = new URLSearchParams(); 180 181 searchParams.set('action', 'smoc_reorder'); 182 searchParams.set('_wpnonce', postNonce); 183 184 const request = jQuery.ajax({ 185 url: ajaxurl + '?' + searchParams, 186 type: 'POST', 187 data: { 188 post_type: typenow, 189 post_id: reorderPostID, 190 post_menu_order: reorderPostMenuOrder, 191 }, 192 }); 193 194 request.done(function (response) { 195 if (response.success) { 196 reorderSuccessSelector.css('display', 'inline-block'); 197 198 $(currentObject).prop('title', reorderPostMenuOrder); 199 200 currentObject.currentValue = reorderPostMenuOrder; 201 currentObject.defaultValue = reorderPostMenuOrder; 202 // If success go to next product. 203 const currentObjectPosition = $(':input[id^=smoc]').index(currentObject); 204 $(':input[id^=smoc]').eq(currentObjectPosition + 1).trigger('select'); 205 } else { 206 currentObject.value = currentObject.defaultValue; 207 208 reorderErrorSelector.css('display', 'inline-block'); 209 } 210 }); 211 212 request.fail(function () { 213 currentObject.value = currentObject.defaultValue; 214 215 reorderLoaderSelectorContainer.css('display', 'none'); 216 reorderSuccessSelector.css('display', 'none'); 217 reorderErrorSelector.css('display', 'inline-block'); 218 }); 219 220 request.always(function () { 221 reorderLoaderSelectorContainer.css({ display: 'none' }); 222 223 /** Enable INPUT after doing Ajax */ 224 $(currentObject).prop('disabled', false); 225 }); 226 }; 227 228 $('input[id^=smoc]').on('focus', function () { 229 this.currentValue = this.value; 230 231 $(this).prop('title', parseInt(this.value)); 232 233 const reorderLoaderID = 'smoc-' + $(this).data('post-id').toString(); 234 235 $('#' + reorderLoaderID + '-loader-container').css({ display: 'none' }); 236 $('#' + reorderLoaderID + '-success').css({ display: 'none' }); 237 $('#' + reorderLoaderID + '-error').css({ display: 'none' }); 238 }); 239 240 $('input[id^=smoc]').on('focusout', function (e) { 241 if ($(this).prop('disabled')) { 242 return false; 243 } 244 245 if (this.currentValue !== this.value) { 246 if (window.confirm(__( 'Should the menu order value be updated?', 'simple-menu-order-column' ))) { 247 $(this).smocDoReorder(this); 248 } else { 249 this.value = this.defaultValue; 250 } 251 } 252 }); 253 254 $('input[id^=smoc]').on('keypress', function (e) { 255 if (e.key === 'Enter') { 256 e.preventDefault(); 257 258 $(this).smocDoReorder(this); 259 } 260 }); 261 })(jQuery); 10 (function () { 11 12 const { __ } = wp.i18n; 13 14 function smocInit() { 15 document.removeEventListener("DOMContentLoaded", smocInit); 16 window.removeEventListener("load", smocInit); 17 18 const smocInputs = document.querySelectorAll('input[id^=smoc]'); 19 20 smocInputs.forEach(smocInput => { 21 smocInput.addEventListener('focus', () => { 22 smocInput.currentValue = smocInput.value; 23 smocInput.title = parseInt(smocInput.value); 24 25 const { postId } = smocInput.dataset; 26 if (!postId) { return; }; 27 28 const hideElement = id => { 29 const smocElement = document.getElementById(id); 30 if (smocElement) { smocElement.style.display = 'none'; } 31 }; 32 33 const smocBaseId = `smoc-${postId}`; 34 35 hideElement(`${smocBaseId}-loader-container`); 36 hideElement(`${smocBaseId}-success`); 37 hideElement(`${smocBaseId}-error`); 38 }); 39 40 smocInput.addEventListener('focusout', () => { 41 if (smocInput.disabled) { return; }; 42 43 if (smocInput.currentValue !== smocInput.value) { 44 if (window.confirm(__('Should the menu order value be updated?', 'simple-menu-order-column'))) { 45 smocDoReorder(smocInput); 46 } else { 47 smocInput.value = smocInput.defaultValue; 48 } 49 } 50 }); 51 52 smocInput.addEventListener('keydown', (smocKeydownEvent) => { 53 const allowedKeys = [ 54 'Backspace', 'Tab', 'ArrowLeft', 'ArrowRight', 55 'ArrowUp', 'ArrowDown', 'Delete', 'Home', 'End', 'Enter' 56 ]; 57 58 // Allow: Ctrl/Cmd + A/C/V/X 59 if ((smocKeydownEvent.ctrlKey || smocKeydownEvent.metaKey) && ['a', 'c', 'v', 'x'].includes(smocKeydownEvent.key.toLowerCase())) { return; } 60 61 // Allow navigation/edit keys 62 if (allowedKeys.includes(smocKeydownEvent.key)) { return; } 63 64 // Block any key that is not a digit (0-9) 65 if (!/^\d$/.test(smocKeydownEvent.key)) { 66 smocKeydownEvent.preventDefault(); 67 } 68 }); 69 70 smocInput.addEventListener('paste', (smocPasteEvent) => { 71 const pasted = smocPasteEvent.clipboardData.getData('text'); 72 if (!/^\d+$/.test(pasted)) { 73 smocPasteEvent.preventDefault(); 74 } 75 }); 76 77 smocInput.addEventListener('keypress', smocKeypressEvent => { 78 if (smocKeypressEvent.key === 'Enter') { 79 smocKeypressEvent.preventDefault(); 80 smocDoReorder(smocInput); 81 } 82 }); 83 }); 84 } 85 86 function smocDoReorder(smocInput) { 87 if (!smocInput || smocInput.disabled) { return; } 88 89 const smocContainer = smocInput.closest('.smoc-container'), postId = parseInt(smocInput.dataset.postId); 90 91 if (!postId || isNaN(postId)) { 92 disableInput(null, __('The post_id is invalid.', 'simple-menu-order-column'), true, smocInput); 93 return; 94 } 95 96 const loaderId = `smoc-${postId}`, menuOrder = parseInt(smocInput.value); 97 98 if (isNaN(menuOrder)) { 99 const errorEl = document.getElementById(`${loaderId}-error`); 100 disableInput(errorEl, __('The menu order value is invalid.', 'simple-menu-order-column'), false, smocInput); 101 return; 102 } 103 104 const nonce = smocInput.dataset.wpnonce; 105 if (!nonce) { 106 const errorEl = document.getElementById(`${loaderId}-error`); 107 disableInput(errorEl, __('The postNonce is invalid.', 'simple-menu-order-column'), true, smocInput); 108 return; 109 } 110 111 if (typeof ajaxurl === 'undefined' || typeof typenow === 'undefined') { 112 const errorEl = document.getElementById(`${loaderId}-error`); 113 disableInput(errorEl, __('Invalid WP installation, variables typenow or ajaxurl are not initialized.', 'simple-menu-order-column'), true, smocInput); 114 return; 115 } 116 117 smocInput.disabled = true; 118 119 const showLoader = () => { 120 let smocLoaderContainer = document.getElementById(`${loaderId}-loader-container`); 121 if (!smocLoaderContainer) { 122 const smocLoader = document.createElement('span'); 123 smocLoader.id = `${loaderId}-loader`; 124 smocLoader.className = 'smoc-loader dashicons dashicons-update'; 125 smocLoader.setAttribute('role', 'img'); 126 smocLoader.setAttribute('aria-label', __('Updating menu order...', 'simple-menu-order-column')); 127 smocLoader.style.cssText = 'color: #2ea2cc; animation: iconrotation 2s infinite linear; display: inline-block;'; 128 129 smocLoaderContainer = document.createElement('div'); 130 smocLoaderContainer.id = `${loaderId}-loader-container`; 131 smocLoaderContainer.style.cssText = 'padding-top: 5px; display: inline-block;'; 132 smocLoaderContainer.appendChild(smocLoader); 133 smocContainer.appendChild(smocLoaderContainer); 134 } else { 135 smocLoaderContainer.style.display = 'inline-block'; 136 } 137 }; 138 139 const showSuccess = () => { 140 let smocSuccess = document.getElementById(`${loaderId}-success`); 141 if (!smocSuccess) { 142 smocSuccess = document.createElement('span'); 143 smocSuccess.id = `${loaderId}-success`; 144 smocSuccess.className = 'smoc-success dashicons dashicons-yes-alt'; 145 smocSuccess.setAttribute('role', 'img'); 146 smocSuccess.setAttribute('aria-label', __('The menu order has been updated successfully.', 'simple-menu-order-column')); 147 smocSuccess.style.cssText = 'padding-top: 5px; color: #7ad03a; display: inline-block;'; 148 smocContainer.appendChild(smocSuccess); 149 } else { 150 smocSuccess.style.display = 'inline-block'; 151 } 152 }; 153 154 const showError = () => { 155 let smocError = document.getElementById(`${loaderId}-error`); 156 if (!smocError) { 157 smocError = document.createElement('span'); 158 smocError.id = `${loaderId}-error`; 159 smocError.className = 'smoc-error dashicons dashicons-dismiss'; 160 smocError.setAttribute('role', 'img'); 161 smocError.setAttribute('aria-label', __('An error ocurred while updating menu order.', 'simple-menu-order-column')); 162 smocError.style.cssText = 'padding-top: 5px; color: #a00; display: inline-block;'; 163 smocContainer.appendChild(smocError); 164 } else { 165 smocError.style.display = 'inline-block'; 166 } 167 }; 168 169 const hideLoader = () => { 170 const smocLoaderContainer = document.getElementById(`${loaderId}-loader-container`); 171 if (smocLoaderContainer) { smocLoaderContainer.style.display = 'none'; } 172 }; 173 174 showLoader(); 175 176 fetch(`${ajaxurl}?action=smoc_reorder&_wpnonce=${encodeURIComponent(nonce)}`, { 177 method: 'POST', 178 headers: { 179 'Content-Type': 'application/x-www-form-urlencoded', 180 }, 181 body: new URLSearchParams({ 182 post_type: typenow, 183 post_id: postId, 184 post_menu_order: menuOrder, 185 }), 186 }) 187 .then(res => res.json()) 188 .then(response => { 189 if (response.success) { 190 showSuccess(); 191 smocInput.title = menuOrder; 192 smocInput.currentValue = menuOrder; 193 smocInput.defaultValue = menuOrder; 194 195 const inputs = Array.from(document.querySelectorAll('input[id^=smoc]')), pos = inputs.indexOf(smocInput) + 1; 196 if (inputs[pos]) { inputs[pos].select(); } 197 } else { 198 smocInput.value = smocInput.defaultValue; 199 showError(); 200 } 201 }) 202 .catch(() => { 203 smocInput.value = smocInput.defaultValue; 204 hideLoader(); 205 showError(); 206 }) 207 .finally(() => { 208 hideLoader(); 209 smocInput.disabled = false; 210 }); 211 } 212 213 function disableInput(errorContainer, message, disable, input) { 214 input.value = input.defaultValue; 215 if (errorContainer) { errorContainer.style.display = 'inline-block'; } 216 input.disabled = disable; 217 input.title = message; 218 console.warn(`[Simple Menu Order Column] ${message}`); 219 } 220 221 /** 222 * Initilize Script 223 */ 224 if (document.readyState === "loading") { 225 document.addEventListener('DOMContentLoaded', smocInit); 226 window.addEventListener("load", smocInit); 227 } else { 228 window.setTimeout(smocInit); 229 } 230 })(); -
simple-menu-order-column/trunk/assets/js/simple-menu-order-column.min.js
r3259831 r3322001 1 1 /*! 2 * Simple Menu Order Column 2 * Simple Menu Order Column - Vanilla JS Version 3 3 * 4 4 * https://github.com/ChillCode/simple-menu-order-column/ 5 5 * 6 * Copyright (C) 20 24ChillCode6 * Copyright (C) 2003-2025 ChillCode 7 7 * 8 8 * @license Released under the General Public License v3.0 https://www.gnu.org/licenses/gpl-3.0.html 9 9 */ 10 !function( e){const{__:s,_x:n,_n:i,_nx:o}=wp.i18n;e.fn.smocDoReorder=function(n){const i=this;if(!i||e(n).prop("disabled"))return!1;function o(s,i,o){n.value=n.defaultValue,s&&s.css("display","inline-block"),e(n).prop("disabled",o).prop("title",i),window.console.warn("[Simple Menu Order Column] "+i)}let a=e(i).data("post-id");if(!a||isNaN(a))return o(null,s("The post_id is invalid.","simple-menu-order-column"),!0),!1;a=parseInt(a);const l="smoc-"+a.toString(),r=e(i).closest(".smoc-container");let t=e("#"+l+"-loader");t.length||(t=e("<span>").attr({id:l+"-loader",class:"smoc-loader dashicons dashicons-update",role:"img","aria-label":s("Updating menu order...","simple-menu-order-column")}).css({color:"#2ea2cc",animation:"iconrotation 2s infinite linear",display:"inline-block"}));let d=e("#"+l+"-loader-container");d.length?d.css({display:"none"}):(d=e("<div>").attr({id:l+"-loader-container"}).css({"padding-top":"5px",display:"none"}),d.append(t),r.append(d));let c=e("#"+l+"-success");c.length?c.css({display:"none"}):(c=e("<span>").attr({id:l+"-success",class:"smoc-success dashicons dashicons-yes-alt",role:"img","aria-label":s("The menu order has been updated successfully.","simple-menu-order-column")}).css({"padding-top":"5px",color:"#7ad03a",display:"none"}),r.append(c));let p=e("#"+l+"-error");if(p.length?p.css({display:"none"}):(p=e("<span>").attr({id:l+"-error",class:"smoc-error dashicons dashicons-dismiss",role:"img","aria-label":s("An error ocurred while updating menu order.","simple-menu-order-column")}).css({"padding-top":"5px",color:"#a00",display:"none"}),r.append(p)),!typenow||!ajaxurl)return o(p,s("Invalid WP installation, variables typenow or ajaxurl are not initialized.","simple-menu-order-column"),!0),!1;let u=e(i).val();if(u=e(i).val(),!u||isNaN(u))return o(p,s("The menu order value is invalid.","simple-menu-order-column"),!1),!1;u=parseInt(u);let m=e(i).data("wpnonce");if(!m)return o(p,s("The postNonce is invalid.","simple-menu-order-column"),!0),!1;e(n).prop("disabled",!0),d.css({display:"inline-block"});const h=new URLSearchParams;h.set("action","smoc_reorder"),h.set("_wpnonce",m);const y=jQuery.ajax({url:ajaxurl+"?"+h,type:"POST",data:{post_type:typenow,post_id:a,post_menu_order:u}});y.done((function(s){if(s.success){c.css("display","inline-block"),e(n).prop("title",u),n.currentValue=u,n.defaultValue=u;const s=e(":input[id^=smoc]").index(n);e(":input[id^=smoc]").eq(s+1).trigger("select")}else n.value=n.defaultValue,p.css("display","inline-block")})),y.fail((function(){n.value=n.defaultValue,d.css("display","none"),c.css("display","none"),p.css("display","inline-block")})),y.always((function(){d.css({display:"none"}),e(n).prop("disabled",!1)}))},e("input[id^=smoc]").on("focus",(function(){this.currentValue=this.value,e(this).prop("title",parseInt(this.value));const s="smoc-"+e(this).data("post-id").toString();e("#"+s+"-loader-container").css({display:"none"}),e("#"+s+"-success").css({display:"none"}),e("#"+s+"-error").css({display:"none"})})),e("input[id^=smoc]").on("focusout",(function(n){if(e(this).prop("disabled"))return!1;this.currentValue!==this.value&&(window.confirm(s("Should the menu order value be updated?","simple-menu-order-column"))?e(this).smocDoReorder(this):this.value=this.defaultValue)})),e("input[id^=smoc]").on("keypress",(function(s){"Enter"===s.key&&(s.preventDefault(),e(this).smocDoReorder(this))}))}(jQuery);10 !function(){const{__:e}=wp.i18n;function t(){document.removeEventListener("DOMContentLoaded",t),window.removeEventListener("load",t);document.querySelectorAll("input[id^=smoc]").forEach(t=>{t.addEventListener("focus",()=>{t.currentValue=t.value,t.title=parseInt(t.value);const{postId:e}=t.dataset;if(!e)return;const n=e=>{const t=document.getElementById(e);t&&(t.style.display="none")},o=`smoc-${e}`;n(`${o}-loader-container`),n(`${o}-success`),n(`${o}-error`)}),t.addEventListener("focusout",()=>{t.disabled||t.currentValue!==t.value&&(window.confirm(e("Should the menu order value be updated?","simple-menu-order-column"))?n(t):t.value=t.defaultValue)}),t.addEventListener("keydown",e=>{(e.ctrlKey||e.metaKey)&&["a","c","v","x"].includes(e.key.toLowerCase())||["Backspace","Tab","ArrowLeft","ArrowRight","ArrowUp","ArrowDown","Delete","Home","End","Enter"].includes(e.key)||/^\d$/.test(e.key)||e.preventDefault()}),t.addEventListener("paste",e=>{const t=e.clipboardData.getData("text");/^\d+$/.test(t)||e.preventDefault()}),t.addEventListener("keypress",e=>{"Enter"===e.key&&(e.preventDefault(),n(t))})})}function n(t){if(!t||t.disabled)return;const n=t.closest(".smoc-container"),d=parseInt(t.dataset.postId);if(!d||isNaN(d))return void o(null,e("The post_id is invalid.","simple-menu-order-column"),!0,t);const l=`smoc-${d}`,a=parseInt(t.value);if(isNaN(a)){return void o(document.getElementById(`${l}-error`),e("The menu order value is invalid.","simple-menu-order-column"),!1,t)}const s=t.dataset.wpnonce;if(!s){return void o(document.getElementById(`${l}-error`),e("The postNonce is invalid.","simple-menu-order-column"),!0,t)}if("undefined"==typeof ajaxurl||"undefined"==typeof typenow){return void o(document.getElementById(`${l}-error`),e("Invalid WP installation, variables typenow or ajaxurl are not initialized.","simple-menu-order-column"),!0,t)}t.disabled=!0;const r=()=>{let t=document.getElementById(`${l}-error`);t?t.style.display="inline-block":(t=document.createElement("span"),t.id=`${l}-error`,t.className="smoc-error dashicons dashicons-dismiss",t.setAttribute("role","img"),t.setAttribute("aria-label",e("An error ocurred while updating menu order.","simple-menu-order-column")),t.style.cssText="padding-top: 5px; color: #a00; display: inline-block;",n.appendChild(t))},i=()=>{const e=document.getElementById(`${l}-loader-container`);e&&(e.style.display="none")};(()=>{let t=document.getElementById(`${l}-loader-container`);if(t)t.style.display="inline-block";else{const o=document.createElement("span");o.id=`${l}-loader`,o.className="smoc-loader dashicons dashicons-update",o.setAttribute("role","img"),o.setAttribute("aria-label",e("Updating menu order...","simple-menu-order-column")),o.style.cssText="color: #2ea2cc; animation: iconrotation 2s infinite linear; display: inline-block;",t=document.createElement("div"),t.id=`${l}-loader-container`,t.style.cssText="padding-top: 5px; display: inline-block;",t.appendChild(o),n.appendChild(t)}})(),fetch(`${ajaxurl}?action=smoc_reorder&_wpnonce=${encodeURIComponent(s)}`,{method:"POST",headers:{"Content-Type":"application/x-www-form-urlencoded"},body:new URLSearchParams({post_type:typenow,post_id:d,post_menu_order:a})}).then(e=>e.json()).then(o=>{if(o.success){(()=>{let t=document.getElementById(`${l}-success`);t?t.style.display="inline-block":(t=document.createElement("span"),t.id=`${l}-success`,t.className="smoc-success dashicons dashicons-yes-alt",t.setAttribute("role","img"),t.setAttribute("aria-label",e("The menu order has been updated successfully.","simple-menu-order-column")),t.style.cssText="padding-top: 5px; color: #7ad03a; display: inline-block;",n.appendChild(t))})(),t.title=a,t.currentValue=a,t.defaultValue=a;const o=Array.from(document.querySelectorAll("input[id^=smoc]")),d=o.indexOf(t)+1;o[d]&&o[d].select()}else t.value=t.defaultValue,r()}).catch(()=>{t.value=t.defaultValue,i(),r()}).finally(()=>{i(),t.disabled=!1})}function o(e,t,n,o){o.value=o.defaultValue,e&&(e.style.display="inline-block"),o.disabled=n,o.title=t,console.warn(`[Simple Menu Order Column] ${t}`)}"loading"===document.readyState?(document.addEventListener("DOMContentLoaded",t),window.addEventListener("load",t)):window.setTimeout(t)}(); -
simple-menu-order-column/trunk/changelog.txt
r3259831 r3322001 1 1 == Changelog == 2 3 = 2.0.0 2025-07-02 = 4 5 * Vanilla Javascript, jQuery requeriment removed. 6 * Minor PHPDoc fixes. 7 * Minor code fixes. 2 8 3 9 = 1.0.2 2024-12-16 = -
simple-menu-order-column/trunk/includes/class-simplemenuordercolumn.php
r3259831 r3322001 5 5 * @package SimpleMenuOrderColumn 6 6 * 7 * Copyright: (c) 2003-202 2Chillcode7 * Copyright: (c) 2003-2025 Chillcode 8 8 */ 9 9 … … 22 22 * The single instance of the class. 23 23 * 24 * @var SimpleMenuOrderColumn 24 * @var SimpleMenuOrderColumn|null 25 25 */ 26 26 private static $smoc_instace; … … 31 31 * We allow all WP_Post since has menu_order column and are sortable. 32 32 * 33 * @var array 33 * @var array{0: 'post', 1: 'page', 2: 'product', 3: 'attachment'} 34 34 */ 35 35 private static $smoc_allowed_types = array( 'post', 'page', 'product', 'attachment' ); … … 88 88 /** Add only on listings pages and compatible post types. */ 89 89 $current_screen = get_current_screen(); 90 91 if ( null === $current_screen ) { 92 return; 93 } 90 94 91 95 if ( … … 100 104 101 105 if ( 'upload' === $current_screen->base ) { 102 /** This filteris called directly. */103 add_ filter( 'manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 );106 /** This action is called directly. */ 107 add_action( 'manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 ); 104 108 } else { 105 109 add_action( 'manage_' . $current_screen->post_type . '_posts_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 ); … … 111 115 /** 112 116 * Enqueue scripts. 117 * 118 * @return void 113 119 */ 114 120 public function admin_enqueue_scripts() { 115 121 $wp_scripts_get_suffix = wp_scripts_get_suffix(); 116 122 117 wp_enqueue_script( 'simple-menu-order-column', plugins_url( 'assets/js/simple-menu-order-column' . $wp_scripts_get_suffix . '.js', SMOC_PLUGIN_FILE ), array( ' jquery', 'wp-i18n' ), SMOC_PLUGIN_VERSION, true );123 wp_enqueue_script( 'simple-menu-order-column', plugins_url( 'assets/js/simple-menu-order-column' . $wp_scripts_get_suffix . '.js', SMOC_PLUGIN_FILE ), array( 'wp-i18n' ), SMOC_PLUGIN_VERSION, true ); 118 124 wp_enqueue_style( 'simple-menu-order-column', plugins_url( 'assets/css/simple-menu-order-column' . $wp_scripts_get_suffix . '.css', SMOC_PLUGIN_FILE ), array(), SMOC_PLUGIN_VERSION ); 119 125 … … 124 130 * Allowed post_types. 125 131 * 126 * @return array 132 * @return array{0: 'post', 1: 'page', 2: 'product', 3: 'attachment'} 127 133 */ 128 134 public static function get_allowed_types() { … … 172 178 * @param int $post_id Post id. 173 179 * @param int $post_menu_order Post order. 180 * @return WP_Error|int The post ID on success. The value 0 or WP_Error on failure. 174 181 */ 175 182 private static function set_post_menu_order( int $post_id, int $post_menu_order ) { 176 177 $post = get_post( $post_id ); 178 179 if ( ! $post ) { 180 return new WP_Error(); 181 } 182 183 $post->menu_order = $post_menu_order; 184 185 return wp_update_post( $post, true ); 183 return wp_update_post( 184 array( 185 'ID' => $post_id, 186 'menu_order' => $post_menu_order, 187 ), 188 true 189 ); 186 190 } 187 191 … … 191 195 * @param int $post_id Post id. 192 196 * @param int $post_menu_order Post order. 197 * @return void 193 198 */ 194 199 private static function output_menu_order_column( int $post_id, int $post_menu_order ) { … … 207 212 * @param string $column Column name. 208 213 * @param int $postid Post order. 214 * @return void 209 215 */ 210 216 public static function manage_posts_custom_column( $column, $postid ) { … … 212 218 $post = get_post( $postid ); 213 219 220 if ( null === $post ) { 221 return; 222 } 223 214 224 self::output_menu_order_column( $postid, $post->menu_order ); 215 225 } … … 219 229 * Add menu order column. 220 230 * 221 * @param array$columns Post list columns.222 * @return array231 * @param string[] $columns Post list columns. 232 * @return string[] 223 233 */ 224 234 public static function manage_edit_columns( $columns ) { 225 235 $columns['menu_order'] = esc_html__( 'Order', 'simple-menu-order-column' ); 226 227 236 return $columns; 228 237 } … … 231 240 * Add menu order column to sortable columns. 232 241 * 233 * @param array$sortable_columns Post list columns.234 * @return array242 * @param string[] $sortable_columns Post list columns. 243 * @return string[] 235 244 */ 236 245 public static function manage_edit_sortable_columns( $sortable_columns ) { … … 245 254 */ 246 255 public static function instance() { 247 if ( is_null( self::$smoc_instace )) {256 if ( ! self::$smoc_instace instanceof SimpleMenuOrderColumn ) { 248 257 self::$smoc_instace = new self(); 249 258 } -
simple-menu-order-column/trunk/readme.txt
r3259831 r3322001 3 3 Tags: menu order, pages, media, posts, products 4 4 Requires at least: 6.0 5 Tested up to: 6. 75 Tested up to: 6.8 6 6 Requires PHP: 7.4 7 Stable tag: 1.0.27 Stable tag: 2.0.0 8 8 License: GPLv3 9 9 License URI: https://www.gnu.org/licenses/gpl-3.0.html … … 67 67 == Changelog == 68 68 69 = 1.0.2 2024-12-16=69 = 2.0.0 2025-07-02 = 70 70 71 * Update - Minor code changes.72 * Fix - Localize Javascript.73 * Add - Added products listing screenshot.71 * Vanilla Javascript, no jQuery. 72 * Minor PHPDoc fixes. 73 * Minor code fixes. 74 74 75 75 == Upgrade Notice == … … 81 81 Minor code changes. 82 82 Localize Javascript. 83 84 = 2.0.0 = 85 Vanilla Javascript, no jQuery. 86 Minor PHPDoc fixes. 87 Minor code fixes. 83 88 84 89 == Screenshots == -
simple-menu-order-column/trunk/simple-menu-order-column.php
r3259831 r3322001 12 12 * Plugin URI: https://github.com/chillcode/simple-menu-order-column 13 13 * Description: Add a menu order column to your listings. 14 * Version: 1.0.214 * Version: 2.0.0 15 15 * Requires at least: 6.0 16 16 * Requires PHP: 7.4 … … 27 27 define( 'SMOC_PLUGIN_PATH', __DIR__ ); 28 28 define( 'SMOC_PLUGIN_FILE', __FILE__ ); 29 define( 'SMOC_PLUGIN_VERSION', ' 1.0.2' );29 define( 'SMOC_PLUGIN_VERSION', '2.0.0' ); 30 30 31 31 require_once SMOC_PLUGIN_PATH . '/includes/class-simplemenuordercolumn.php';
Note: See TracChangeset
for help on using the changeset viewer.