Plugin Directory

Changeset 3322001


Ignore:
Timestamp:
07/03/2025 08:27:25 PM (9 months ago)
Author:
chillcode
Message:

Release version 2.0.0

Location:
simple-menu-order-column
Files:
21 edited
1 copied

Legend:

Unmodified
Added
Removed
  • simple-menu-order-column/tags/1.0.0/assets/js/simple-menu-order-column.js

    r3074004 r3322001  
    99 */
    1010(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    );
    261302})(jQuery);
  • simple-menu-order-column/tags/1.0.0/includes/class-simplemenuordercolumn.php

    r3074004 r3322001  
    1212use WP_Error;
    1313
    14 defined( 'ABSPATH' ) || exit;
     14defined('ABSPATH') || exit;
    1515
    1616/**
    1717 * SMOCWC class.
    1818 */
    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     }
     19final 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    }
    251269}
  • simple-menu-order-column/tags/1.0.0/simple-menu-order-column.php

    r3074004 r3322001  
    2626 */
    2727
    28 defined( 'ABSPATH' ) || exit;
     28defined('ABSPATH') || exit;
    2929
    30 define( 'SMOC_PLUGIN_PATH', __DIR__ );
    31 define( 'SMOC_PLUGIN_FILE', __FILE__ );
    32 define( 'SMOC_PLUGIN_VERSION', '1.0.0' );
     30define('SMOC_PLUGIN_PATH', __DIR__);
     31define('SMOC_PLUGIN_FILE', __FILE__);
     32define('SMOC_PLUGIN_VERSION', '1.0.0');
    3333
    3434require_once SMOC_PLUGIN_PATH . '/includes/class-simplemenuordercolumn.php';
     
    3939 * Ensures only one instance is loaded or can be loaded.
    4040 *
    41  * @since 1.0
     41 * @since  1.0
    4242 * @static
    4343 * @return SMOC\SimpleMenuOrderColumn Main instance.
    4444 */
    45 function SMOC(): SMOC\SimpleMenuOrderColumn { //phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
    46     return SMOC\SimpleMenuOrderColumn::instance();
     45function SMOC(): SMOC\SimpleMenuOrderColumn  //phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
     46{
     47    return SMOC\SimpleMenuOrderColumn::instance();
    4748}
    4849
  • simple-menu-order-column/tags/1.0.1/assets/js/simple-menu-order-column.js

    r3074004 r3322001  
    99 */
    1010(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    );
    261302})(jQuery);
  • simple-menu-order-column/tags/1.0.1/includes/class-simplemenuordercolumn.php

    r3074004 r3322001  
    1212use WP_Error;
    1313
    14 defined( 'ABSPATH' ) || exit;
     14defined('ABSPATH') || exit;
    1515
    1616/**
    1717 * SMOCWC class.
    1818 */
    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     }
     19final 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    }
    251269}
  • simple-menu-order-column/tags/1.0.1/simple-menu-order-column.php

    r3122104 r3322001  
    2323 */
    2424
    25 defined( 'ABSPATH' ) || exit;
     25defined('ABSPATH') || exit;
    2626
    27 define( 'SMOC_PLUGIN_PATH', __DIR__ );
    28 define( 'SMOC_PLUGIN_FILE', __FILE__ );
    29 define( 'SMOC_PLUGIN_VERSION', '1.0.1' );
     27define('SMOC_PLUGIN_PATH', __DIR__);
     28define('SMOC_PLUGIN_FILE', __FILE__);
     29define('SMOC_PLUGIN_VERSION', '1.0.1');
    3030
    3131require_once SMOC_PLUGIN_PATH . '/includes/class-simplemenuordercolumn.php';
     
    3636 * Ensures only one instance is loaded or can be loaded.
    3737 *
    38  * @since 1.0
     38 * @since  1.0
    3939 * @static
    4040 * @return SMOC\SimpleMenuOrderColumn Main instance.
    4141 */
    42 function SMOC(): SMOC\SimpleMenuOrderColumn { //phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
    43     return SMOC\SimpleMenuOrderColumn::instance();
     42function SMOC(): SMOC\SimpleMenuOrderColumn  //phpcs:ignore WordPress.NamingConventions.ValidFunctionName.FunctionNameInvalid
     43{
     44    return SMOC\SimpleMenuOrderColumn::instance();
    4445}
    4546
  • simple-menu-order-column/tags/1.0.2/assets/js/simple-menu-order-column.js

    r3259832 r3322001  
    99 */
    1010(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    );
    261302})(jQuery);
  • simple-menu-order-column/tags/1.0.2/includes/class-simplemenuordercolumn.php

    r3259832 r3322001  
    1212use WP_Error;
    1313
    14 defined( 'ABSPATH' ) || exit;
     14defined('ABSPATH') || exit;
    1515
    1616/**
    1717 * SMOCWC class.
    1818 */
    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     }
     19final 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    }
    253271}
  • simple-menu-order-column/tags/1.0.2/simple-menu-order-column.php

    r3259832 r3322001  
    2323 */
    2424
    25 defined( 'ABSPATH' ) || exit;
     25defined('ABSPATH') || exit;
    2626
    27 define( 'SMOC_PLUGIN_PATH', __DIR__ );
    28 define( 'SMOC_PLUGIN_FILE', __FILE__ );
    29 define( 'SMOC_PLUGIN_VERSION', '1.0.2' );
     27define('SMOC_PLUGIN_PATH', __DIR__);
     28define('SMOC_PLUGIN_FILE', __FILE__);
     29define('SMOC_PLUGIN_VERSION', '1.0.2');
    3030
    3131require_once SMOC_PLUGIN_PATH . '/includes/class-simplemenuordercolumn.php';
    3232
    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     }
     33if (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    }
    4647
    47     /**
    48     * Initialize the plugin.
    49     */
    50     SMOC();
     48    /**
     49    * Initialize the plugin.
     50    */
     51    SMOC();
    5152}
  • simple-menu-order-column/tags/2.0.0/assets/js/simple-menu-order-column.js

    r3259831 r3322001  
    11/*!
    2  * Simple Menu Order Column
     2 * Simple Menu Order Column - Vanilla JS Version
    33 *
    44 * https://github.com/ChillCode/simple-menu-order-column/
    55 *
    6  * Copyright (C) 2024 ChillCode
     6 * Copyright (C) 2003-2025 ChillCode
    77 *
    88 * @license Released under the General Public License v3.0 https://www.gnu.org/licenses/gpl-3.0.html
    99 */
    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  
    11/*!
    2  * Simple Menu Order Column
     2 * Simple Menu Order Column - Vanilla JS Version
    33 *
    44 * https://github.com/ChillCode/simple-menu-order-column/
    55 *
    6  * Copyright (C) 2024 ChillCode
     6 * Copyright (C) 2003-2025 ChillCode
    77 *
    88 * @license Released under the General Public License v3.0 https://www.gnu.org/licenses/gpl-3.0.html
    99 */
    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  
    11== Changelog ==
     2
     3= 2.0.0 2025-07-02 =
     4
     5* Vanilla Javascript, jQuery requeriment removed.
     6* Minor PHPDoc fixes.
     7* Minor code fixes.
    28
    39= 1.0.2 2024-12-16 =
  • simple-menu-order-column/tags/2.0.0/includes/class-simplemenuordercolumn.php

    r3259831 r3322001  
    55 * @package SimpleMenuOrderColumn
    66 *
    7  * Copyright: (c) 2003-2022 Chillcode
     7 * Copyright: (c) 2003-2025 Chillcode
    88 */
    99
     
    2222     * The single instance of the class.
    2323     *
    24      * @var SimpleMenuOrderColumn
     24     * @var SimpleMenuOrderColumn|null
    2525     */
    2626    private static $smoc_instace;
     
    3131     * We allow all WP_Post since has menu_order column and are sortable.
    3232     *
    33      * @var array
     33     * @var array{0: 'post', 1: 'page', 2: 'product', 3: 'attachment'}
    3434     */
    3535    private static $smoc_allowed_types = array( 'post', 'page', 'product', 'attachment' );
     
    8888        /** Add only on listings pages and compatible post types. */
    8989        $current_screen = get_current_screen();
     90
     91        if ( null === $current_screen ) {
     92            return;
     93        }
    9094
    9195        if (
     
    100104
    101105        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 );
     106            /** This action is called directly. */
     107            add_action( 'manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 );
    104108        } else {
    105109            add_action( 'manage_' . $current_screen->post_type . '_posts_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 );
     
    111115    /**
    112116     * Enqueue scripts.
     117     *
     118     * @return void
    113119     */
    114120    public function admin_enqueue_scripts() {
    115121        $wp_scripts_get_suffix = wp_scripts_get_suffix();
    116122
    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 );
    118124        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 );
    119125
     
    124130     * Allowed post_types.
    125131     *
    126      * @return array
     132     * @return array{0: 'post', 1: 'page', 2: 'product', 3: 'attachment'}
    127133     */
    128134    public static function get_allowed_types() {
     
    172178     * @param int $post_id Post id.
    173179     * @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.
    174181     */
    175182    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        );
    186190    }
    187191
     
    191195     * @param int $post_id Post id.
    192196     * @param int $post_menu_order Post order.
     197     * @return void
    193198     */
    194199    private static function output_menu_order_column( int $post_id, int $post_menu_order ) {
     
    207212     * @param string $column Column name.
    208213     * @param int    $postid Post order.
     214     * @return void
    209215     */
    210216    public static function manage_posts_custom_column( $column, $postid ) {
     
    212218            $post = get_post( $postid );
    213219
     220            if ( null === $post ) {
     221                return;
     222            }
     223
    214224            self::output_menu_order_column( $postid, $post->menu_order );
    215225        }
     
    219229     * Add menu order column.
    220230     *
    221      * @param array $columns Post list columns.
    222      * @return array
     231     * @param string[] $columns Post list columns.
     232     * @return string[]
    223233     */
    224234    public static function manage_edit_columns( $columns ) {
    225235        $columns['menu_order'] = esc_html__( 'Order', 'simple-menu-order-column' );
    226 
    227236        return $columns;
    228237    }
     
    231240     * Add menu order column to sortable columns.
    232241     *
    233      * @param array $sortable_columns Post list columns.
    234      * @return array
     242     * @param string[] $sortable_columns Post list columns.
     243     * @return string[]
    235244     */
    236245    public static function manage_edit_sortable_columns( $sortable_columns ) {
     
    245254     */
    246255    public static function instance() {
    247         if ( is_null( self::$smoc_instace ) ) {
     256        if ( ! self::$smoc_instace instanceof SimpleMenuOrderColumn ) {
    248257            self::$smoc_instace = new self();
    249258        }
  • simple-menu-order-column/tags/2.0.0/readme.txt

    r3259831 r3322001  
    33Tags: menu order, pages, media, posts, products
    44Requires at least: 6.0
    5 Tested up to: 6.7
     5Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 1.0.2
     7Stable tag: 2.0.0
    88License: GPLv3
    99License URI: https://www.gnu.org/licenses/gpl-3.0.html
     
    6767== Changelog ==
    6868
    69 = 1.0.2 2024-12-16 =
     69= 2.0.0 2025-07-02 =
    7070
    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.
    7474
    7575== Upgrade Notice ==
     
    8181Minor code changes.
    8282Localize Javascript.
     83
     84= 2.0.0 =
     85Vanilla Javascript, no jQuery.
     86Minor PHPDoc fixes.
     87Minor code fixes.
    8388
    8489== Screenshots ==
  • simple-menu-order-column/tags/2.0.0/simple-menu-order-column.php

    r3259831 r3322001  
    1212 * Plugin URI: https://github.com/chillcode/simple-menu-order-column
    1313 * Description: Add a menu order column to your listings.
    14  * Version: 1.0.2
     14 * Version: 2.0.0
    1515 * Requires at least: 6.0
    1616 * Requires PHP: 7.4
     
    2727define( 'SMOC_PLUGIN_PATH', __DIR__ );
    2828define( 'SMOC_PLUGIN_FILE', __FILE__ );
    29 define( 'SMOC_PLUGIN_VERSION', '1.0.2' );
     29define( 'SMOC_PLUGIN_VERSION', '2.0.0' );
    3030
    3131require_once SMOC_PLUGIN_PATH . '/includes/class-simplemenuordercolumn.php';
  • simple-menu-order-column/trunk/assets/js/simple-menu-order-column.js

    r3259831 r3322001  
    11/*!
    2  * Simple Menu Order Column
     2 * Simple Menu Order Column - Vanilla JS Version
    33 *
    44 * https://github.com/ChillCode/simple-menu-order-column/
    55 *
    6  * Copyright (C) 2024 ChillCode
     6 * Copyright (C) 2003-2025 ChillCode
    77 *
    88 * @license Released under the General Public License v3.0 https://www.gnu.org/licenses/gpl-3.0.html
    99 */
    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  
    11/*!
    2  * Simple Menu Order Column
     2 * Simple Menu Order Column - Vanilla JS Version
    33 *
    44 * https://github.com/ChillCode/simple-menu-order-column/
    55 *
    6  * Copyright (C) 2024 ChillCode
     6 * Copyright (C) 2003-2025 ChillCode
    77 *
    88 * @license Released under the General Public License v3.0 https://www.gnu.org/licenses/gpl-3.0.html
    99 */
    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  
    11== Changelog ==
     2
     3= 2.0.0 2025-07-02 =
     4
     5* Vanilla Javascript, jQuery requeriment removed.
     6* Minor PHPDoc fixes.
     7* Minor code fixes.
    28
    39= 1.0.2 2024-12-16 =
  • simple-menu-order-column/trunk/includes/class-simplemenuordercolumn.php

    r3259831 r3322001  
    55 * @package SimpleMenuOrderColumn
    66 *
    7  * Copyright: (c) 2003-2022 Chillcode
     7 * Copyright: (c) 2003-2025 Chillcode
    88 */
    99
     
    2222     * The single instance of the class.
    2323     *
    24      * @var SimpleMenuOrderColumn
     24     * @var SimpleMenuOrderColumn|null
    2525     */
    2626    private static $smoc_instace;
     
    3131     * We allow all WP_Post since has menu_order column and are sortable.
    3232     *
    33      * @var array
     33     * @var array{0: 'post', 1: 'page', 2: 'product', 3: 'attachment'}
    3434     */
    3535    private static $smoc_allowed_types = array( 'post', 'page', 'product', 'attachment' );
     
    8888        /** Add only on listings pages and compatible post types. */
    8989        $current_screen = get_current_screen();
     90
     91        if ( null === $current_screen ) {
     92            return;
     93        }
    9094
    9195        if (
     
    100104
    101105        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 );
     106            /** This action is called directly. */
     107            add_action( 'manage_media_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 );
    104108        } else {
    105109            add_action( 'manage_' . $current_screen->post_type . '_posts_custom_column', array( __CLASS__, 'manage_posts_custom_column' ), 10, 2 );
     
    111115    /**
    112116     * Enqueue scripts.
     117     *
     118     * @return void
    113119     */
    114120    public function admin_enqueue_scripts() {
    115121        $wp_scripts_get_suffix = wp_scripts_get_suffix();
    116122
    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 );
    118124        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 );
    119125
     
    124130     * Allowed post_types.
    125131     *
    126      * @return array
     132     * @return array{0: 'post', 1: 'page', 2: 'product', 3: 'attachment'}
    127133     */
    128134    public static function get_allowed_types() {
     
    172178     * @param int $post_id Post id.
    173179     * @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.
    174181     */
    175182    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        );
    186190    }
    187191
     
    191195     * @param int $post_id Post id.
    192196     * @param int $post_menu_order Post order.
     197     * @return void
    193198     */
    194199    private static function output_menu_order_column( int $post_id, int $post_menu_order ) {
     
    207212     * @param string $column Column name.
    208213     * @param int    $postid Post order.
     214     * @return void
    209215     */
    210216    public static function manage_posts_custom_column( $column, $postid ) {
     
    212218            $post = get_post( $postid );
    213219
     220            if ( null === $post ) {
     221                return;
     222            }
     223
    214224            self::output_menu_order_column( $postid, $post->menu_order );
    215225        }
     
    219229     * Add menu order column.
    220230     *
    221      * @param array $columns Post list columns.
    222      * @return array
     231     * @param string[] $columns Post list columns.
     232     * @return string[]
    223233     */
    224234    public static function manage_edit_columns( $columns ) {
    225235        $columns['menu_order'] = esc_html__( 'Order', 'simple-menu-order-column' );
    226 
    227236        return $columns;
    228237    }
     
    231240     * Add menu order column to sortable columns.
    232241     *
    233      * @param array $sortable_columns Post list columns.
    234      * @return array
     242     * @param string[] $sortable_columns Post list columns.
     243     * @return string[]
    235244     */
    236245    public static function manage_edit_sortable_columns( $sortable_columns ) {
     
    245254     */
    246255    public static function instance() {
    247         if ( is_null( self::$smoc_instace ) ) {
     256        if ( ! self::$smoc_instace instanceof SimpleMenuOrderColumn ) {
    248257            self::$smoc_instace = new self();
    249258        }
  • simple-menu-order-column/trunk/readme.txt

    r3259831 r3322001  
    33Tags: menu order, pages, media, posts, products
    44Requires at least: 6.0
    5 Tested up to: 6.7
     5Tested up to: 6.8
    66Requires PHP: 7.4
    7 Stable tag: 1.0.2
     7Stable tag: 2.0.0
    88License: GPLv3
    99License URI: https://www.gnu.org/licenses/gpl-3.0.html
     
    6767== Changelog ==
    6868
    69 = 1.0.2 2024-12-16 =
     69= 2.0.0 2025-07-02 =
    7070
    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.
    7474
    7575== Upgrade Notice ==
     
    8181Minor code changes.
    8282Localize Javascript.
     83
     84= 2.0.0 =
     85Vanilla Javascript, no jQuery.
     86Minor PHPDoc fixes.
     87Minor code fixes.
    8388
    8489== Screenshots ==
  • simple-menu-order-column/trunk/simple-menu-order-column.php

    r3259831 r3322001  
    1212 * Plugin URI: https://github.com/chillcode/simple-menu-order-column
    1313 * Description: Add a menu order column to your listings.
    14  * Version: 1.0.2
     14 * Version: 2.0.0
    1515 * Requires at least: 6.0
    1616 * Requires PHP: 7.4
     
    2727define( 'SMOC_PLUGIN_PATH', __DIR__ );
    2828define( 'SMOC_PLUGIN_FILE', __FILE__ );
    29 define( 'SMOC_PLUGIN_VERSION', '1.0.2' );
     29define( 'SMOC_PLUGIN_VERSION', '2.0.0' );
    3030
    3131require_once SMOC_PLUGIN_PATH . '/includes/class-simplemenuordercolumn.php';
Note: See TracChangeset for help on using the changeset viewer.