Plugin Directory

Changeset 3341829


Ignore:
Timestamp:
08/08/2025 09:23:21 PM (8 months ago)
Author:
inboundhorizons
Message:

Refactored and tightened sanitation checks.

Location:
bulk-sort-attributes-for-woocommerce
Files:
3 added
1 deleted
2 edited

Legend:

Unmodified
Added
Removed
  • bulk-sort-attributes-for-woocommerce/trunk/bulk-sort-attributes-for-woocommerce.php

    r3063975 r3341829  
    33        Plugin Name: Bulk Sort Attributes for WooCommerce
    44        Description: Bulk sort WooCommerce attributes when they are too numerous for custom sorting by hand.
    5         Version: 1.1.5
     5        Version: 1.2
    66        Author: Inbound Horizons
    77        Author URI: https://www.inboundhorizons.com
     8        License: GPLv2 or later
     9        Requires Plugins: woocommerce
    810    */
    911
     
    2527       
    2628        private function __construct() {
    27             add_action('woocommerce_init', array($this, 'InitHooks'));
    28         }
    29        
    30         public function InitHooks() {
    31            
    32             // Set a hook for every attribute taxonomy
    33                 $attribute_taxonomies = wc_get_attribute_taxonomies();
    34                 if (!empty($attribute_taxonomies)) {
    35                     foreach ($attribute_taxonomies as $attribute) {
    36                         add_action('views_edit-' . 'pa_' . $attribute->attribute_name, function() use ($attribute) {
    37                             $this->OutputSortingButtonsHtml($attribute);
    38                         });
    39                     }
     29            if (is_admin()) {
     30                add_action('woocommerce_init', array($this, 'WooCommerceHooks'));
     31            }
     32        }
     33       
     34        public function WooCommerceHooks() {
     35           
     36            // Ensure that the user has permission to be here
     37            if (!current_user_can('manage_product_terms') && !current_user_can('manage_woocommerce')) {
     38                return;
     39            }
     40           
     41            // Get all attribute taxonomies
     42            $attribute_taxonomies = wc_get_attribute_taxonomies();
     43           
     44            // Check to make sure the $attribute_taxonomies is an array
     45            if (!empty($attribute_taxonomies) && is_array($attribute_taxonomies)) {
     46           
     47                // Loop over the attributes
     48                foreach ($attribute_taxonomies as $attribute) {
     49               
     50                    // Set a hook for every attribute taxonomy to display the sorting dropdown
     51                    add_filter('views_edit-' . 'pa_' . $attribute->attribute_name, function($views) use ($attribute) {
     52                        $this->OutputSortingButtonsHtml($attribute);
     53                        return $views;
     54                    });
    4055                }
    41            
    42             // Listen for AJAX trigger
    43                 add_action('wp_ajax_WBSA_SORT_ATTRIBUTES', array($this, 'AJAXCustomOrderWooAttributes'));
    44            
     56            }
     57           
     58            // Listen for POST data
     59            add_action('admin_post_BSAFW_SORT_ATTRIBUTES', array($this, 'AJAX_CustomOrderWooCommerceAttributes'));
     60        }
     61       
     62        public function AJAX_CustomOrderWooCommerceAttributes() {
     63       
     64            // Ensure that the user has permission to be here
     65            if (!current_user_can('manage_product_terms') && !current_user_can('manage_woocommerce')) {
     66                wp_die(esc_html__('Insufficient permissions.', 'bulk-sort-attributes-for-woocommerce'), 403);
     67            }
     68           
     69            // Validate the nonce
     70            check_admin_referer('bsafw-sort-attributes', 'bsafw_nonce');
     71           
     72            // Get the redirect URL
     73            $redirect_url = wp_get_referer();
     74           
     75            // Get and sanitize POSTed data
     76            $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field(wp_unslash($_POST['taxonomy'])) : '';
     77            $bsafw_sorting = isset($_POST['bsafw_sorting']) ? sanitize_text_field(wp_unslash($_POST['bsafw_sorting'])) : '';
     78
     79            // Split the POSTed data into a sort by name and direction
     80            $sorting_array = explode(" ", $bsafw_sorting);
     81
     82            // Get the sort by and direction
     83            $sort_by = isset($sorting_array[0]) ? strtolower($sorting_array[0]) : '';
     84            $direction = isset($sorting_array[1]) ? strtolower($sorting_array[1]) : '';
     85
     86            // Sort the attribute terms
     87            $this->CustomOrderWooAttributes($taxonomy, $sort_by, $direction);
     88           
     89            wp_safe_redirect($redirect_url);
     90            exit;
    4591        }
    4692       
    4793        public function OutputSortingButtonsHtml($attribute) {
    4894       
    49             $nonce = wp_create_nonce('wbsa-sort-attributes');
    50            
     95            // Get information about the attribute
    5196            $att_orderby = $attribute->attribute_orderby;
    5297            $taxonomy = 'pa_' . $attribute->attribute_name;
    5398           
    54             $is_custom_order_by = ($att_orderby == 'menu_order');
    55            
    56            
    57             $select_disabled = 'disabled';
    58             $btn_disabled = 'button-disabled';
    59             $warning_disabled = '';
    60             if ($is_custom_order_by) {
    61                 $warning_disabled = 'display:none;';
    62                 $btn_disabled = '';
    63                 $select_disabled = '';
    64             }
    65            
    66             echo '
    67                 <div style="margin-top:5px;">
    68                     <select id="wbsa_sorting" '.esc_attr($select_disabled).'>
    69                         <option value="" selected disabled>- Bulk Sort Attributes for WooCommerce -</option>
    70                            
    71                         <optgroup label="Sort By: ID">
    72                             <option value="id asc">ID - ASC</option>
    73                             <option value="id desc">ID - DESC</option>
    74                         </optgroup>
     99            // Check if we can customize the ordering of attribute terms
     100            $is_custom_ordering = ($att_orderby === 'menu_order');
     101           
     102            // Wrap everything in a <div>
     103            echo '<div>';
     104           
     105            if ($is_custom_ordering) {
     106           
     107                echo '
     108                    <form method="post" action="'.esc_url(admin_url('admin-post.php')).'">
     109                ';
     110               
     111                // Output the nonce field
     112                wp_nonce_field('bsafw-sort-attributes', 'bsafw_nonce', true);
     113               
     114                echo '
     115                        <input type="hidden" name="action" value="BSAFW_SORT_ATTRIBUTES" />
     116                        <input type="hidden" name="taxonomy" value="'.esc_attr($taxonomy).'" />
    75117                       
    76                         <optgroup label="Sort By: Name">
    77                             <option value="name asc">Name - ASC</option>
    78                             <option value="name desc">Name - DESC</option>
    79                         </optgroup>
    80                            
    81                         <optgroup label="Sort By: Name (Numeric)">
    82                             <option value="name_num asc">Name (Numeric) - ASC</option>
    83                             <option value="name_num desc">Name (Numeric) - DESC</option>
    84                         </optgroup>
    85                     </select>
    86                    
    87                     <button type="button" class="button button-secondary wbsa '.esc_attr($btn_disabled).'">
    88                         Bulk Sort Attributes
    89                     </button>
    90                    
    91                    
    92                     <b style="'.esc_attr($warning_disabled).'"><span class="dashicons dashicons-warning" style="line-height:1.5;" title="Please edit the attribute and change the default sort order to &quot;Custom ordering&quot;"></span></b>
    93                    
    94                     <span id="wbsa-spinner" class="dashicons dashicons-update" style="display:none; animation:wbsa-spin 2s linear infinite"></span>
    95                 </div>
    96                
    97                 <style>
    98                     @keyframes wbsa-spin {
    99                         0% { transform: rotate(0deg); }
    100                         100% { transform: rotate(360deg); }
    101                     }
    102                 </style>
    103                
    104                
    105                 <script>
    106                
    107                     jQuery(document).ready(function() {
    108                         jQuery(document).on("click", ".wbsa:not(.button-disabled)", function() {
    109                            
    110                             var val = jQuery("#wbsa_sorting").val();
    111                            
    112                             if (val && (val != "")) {
    113                                
    114                                 var sort_direction = val.split(" ");
    115                            
    116                                 var sort = sort_direction[0];
    117                                 var direction = sort_direction[1];
    118                                 var taxonomy = "'.esc_html($taxonomy).'";
    119                                
    120                                 var data = {
    121                                     "action": "WBSA_SORT_ATTRIBUTES",
    122                                     "sort": sort,
    123                                     "direction": direction,
    124                                     "taxonomy": taxonomy,
    125                                     "nonce": "'.esc_attr($nonce).'",
    126                                 };
    127                                
    128                                 jQuery("#wbsa-spinner").show();
    129                                 jQuery.post(ajaxurl, data, function(response) {
    130                                     location.reload();  // Reload the page
    131                                 });
    132                                
    133                             }
    134                             else {
    135                                 alert("Please select how to sort the attributes.");
    136                                 jQuery("#wbsa_sorting").focus();
    137                             }
    138                            
    139                         });
    140                     });
    141                
    142                 </script>
    143                
    144             ';
    145         }
    146        
    147         public function AJAXCustomOrderWooAttributes() {
    148             $ok = 0;
    149            
    150    
    151             $nonce_verified = wp_verify_nonce($_POST['nonce'], 'wbsa-sort-attributes');
    152            
    153             if ($nonce_verified) {
    154                 $ok = 1;
    155            
    156                 // Get and sanitize POSTed data
    157                 $sort = isset($_POST['sort']) ? sanitize_text_field($_POST['sort']) : '';
    158                 $direction = isset($_POST['direction']) ? sanitize_text_field($_POST['direction']) : '';
    159                 $taxonomy = isset($_POST['taxonomy']) ? sanitize_text_field($_POST['taxonomy']) : '';
    160 
    161                 // Convert the strings to lower case
    162                 $sort = strtolower($sort);
    163                 $direction = strtolower($direction);
    164 
    165                 // Sort the attribute terms
    166                 $this->CustomOrderWooAttributes($taxonomy, $sort, $direction);
    167                
    168             }
    169                
    170             $json = array(
    171                 'ok' => $ok,
    172             );
    173            
    174             header('Content-Type: application/json');
    175             echo json_encode($json);
    176             wp_die(); // This is required to terminate immediately and return a proper response
    177         }
    178 
    179         public function CustomOrderWooAttributes($taxonomy, $sort, $direction = 'asc') {
    180            
    181             $possible_sorts = array(
     118                        <select name="bsafw_sorting" required>
     119                            <option value="" selected disabled>- Bulk Sort Attributes for WooCommerce -</option>
     120                           
     121                            <optgroup label="Sort By: ID">
     122                                <option value="id asc">ID - ASC</option>
     123                                <option value="id desc">ID - DESC</option>
     124                            </optgroup>
     125                           
     126                            <optgroup label="Sort By: Name">
     127                                <option value="name asc">Name - ASC</option>
     128                                <option value="name desc">Name - DESC</option>
     129                            </optgroup>
     130                           
     131                            <optgroup label="Sort By: Name (Numeric)">
     132                                <option value="name_num asc">Name (Numeric) - ASC</option>
     133                                <option value="name_num desc">Name (Numeric) - DESC</option>
     134                            </optgroup>
     135                        </select>
     136                       
     137                        <button type="submit" class="button button-secondary">
     138                            Bulk Sort Attributes
     139                        </button>
     140                    </form>
     141                ';
     142            }
     143            else {
     144                echo '
     145                    <div class="notice notice-warning is-dismissible inline">
     146                        <p>
     147                            <strong>'.esc_html__('Bulk Sort Attributes is Unavailable', 'bulk-sort-attributes-for-woocommerce').'</strong>:
     148                            Edit this attribute and set the default sort order to <code>' . esc_html__('Custom ordering', 'bulk-sort-attributes-for-woocommerce') . '</code> to enable bulk sorting.
     149                           
     150                        </p>
     151                    </div>
     152                ';
     153            }
     154           
     155            // End the wrapping <div>
     156            echo '</div>';
     157        }
     158       
     159        public function CustomOrderWooAttributes($taxonomy, $sort = '', $direction = 'asc') {
     160           
     161            // Normalize the sort and direction
     162            $sort = strtolower($sort);
     163            $direction = strtolower($direction);
     164           
     165            // Define the white-listed sorts and directions
     166            $valid_sorts = array(
    182167                'id',
    183168                'name',
     
    185170            );
    186171           
    187             $possible_sort_directions = array(
     172            $valid_sort_directions = array(
    188173                'asc',
    189174                'desc',
    190175            );
    191176           
    192            
    193            
    194             // 1.) Get the terms in an array
     177            // Check if the sort and direction are valid
     178            if (in_array($sort, $valid_sorts, true) && in_array($direction, $valid_sort_directions, true)) {
     179               
     180                // Ensure that the taxonomy is a product attribute
     181                if (!taxonomy_exists($taxonomy) || strpos($taxonomy, 'pa_') !== 0) {
     182                    return;
     183                }
     184           
     185                // Get the terms in an array
    195186                $terms = get_terms(array(
    196187                    'taxonomy' => $taxonomy,
     
    198189                ));
    199190               
    200            
    201             // 2.) Sort the array of terms
    202            
    203                 if (in_array($sort, $possible_sorts) && in_array($direction, $possible_sort_directions)) {
    204                    
    205                     if ($sort == 'name') {
    206                         if ($direction == 'desc') {
    207                             usort($terms, function($a, $b) {
    208                                 return strcmp($b->name, $a->name);
    209                             });
     191                // Check if the terms are valid
     192                if (!is_wp_error($terms) && !empty($terms)) {
     193           
     194                    // Sort the attribute terms
     195                    usort($terms, function($a, $b) use($sort, $direction) {
     196                   
     197                        // Default to assuming we are sorting by 'id' (term ID)
     198                        $comparator = (intval($a->term_id) < intval($b->term_id)) ? -1 : 1;
     199                        if (intval($a->term_id) === intval($b->term_id)) {
     200                            $comparator = 0;
    210201                        }
    211                         else {  // Assume 'asc'
    212                             usort($terms, function($a, $b) {
    213                                 return strcmp($a->name, $b->name);
    214                             });
     202                   
     203                        if ($sort === 'name') {
     204                            $comparator = strcmp($a->name, $b->name);
    215205                        }
     206                        else if ($sort === 'name_num') {
     207                            $comparator = (intval($a->name) < intval($b->name)) ? -1 : 1;
     208                            if (intval($a->name) === intval($b->name)) {
     209                                $comparator = 0;
     210                            }
     211                        }
     212                       
     213                        // Return the results based on the direction
     214                        return ($direction === 'desc') ? -$comparator : $comparator;
     215                    });
     216                   
     217                    // Commit the new order to the database
     218                    foreach ($terms as $index => $term) {
     219                        $term_id = intval($term->term_id);
     220                        wc_set_term_order($term_id, $index, $taxonomy);
    216221                    }
    217                     else if ($sort == 'name_num') {
    218                         if ($direction == 'desc') {
    219                             usort($terms, function($a, $b) {
    220                                 return intval($b->name) > intval($a->name);
    221                             });
    222                         }
    223                         else {  // Assume 'asc'
    224                             usort($terms, function($a, $b) {
    225                                 return intval($a->name) > intval($b->name);
    226                             });
    227                         }
    228                     }
    229                     else {  // Assume 'id'
    230                         if ($direction == 'desc') {
    231                             usort($terms, function($a, $b) {
    232                                 return intval($b->term_id) > intval($a->term_id);
    233                             });
    234                         }
    235                         else {  // Assume 'asc'
    236                             usort($terms, function($a, $b) {
    237                                 return intval($a->term_id) > intval($b->term_id);
    238                             });
    239                         }
    240                     }
    241            
    242                    
    243                     // 3.) Commit the new order to the database
    244                         for ($i = 0; $i < count($terms); $i++) {
    245                             $term_id = intval($terms[$i]->term_id);
    246                             wc_set_term_order($term_id, $i, $taxonomy);
    247                         }
    248                    
    249222                   
    250223                }
     224            }
     225           
    251226           
    252227        }
  • bulk-sort-attributes-for-woocommerce/trunk/readme.txt

    r3063975 r3341829  
    22Contributors: inboundhorizons, cliffbailey
    33Plugin Name: Bulk Sort Attributes for WooCommerce
    4 Tags: woocommerce, attribute, bulk, custom, sorting, order by, orderby
     4Tags: woocommerce, attribute, bulk, sorting, order by
    55Requires at least: 3.3
    6 Tested up to: 6.5
    7 Stable tag: 1.1.5
     6Tested up to: 6.8
     7Stable tag: 1.2
    88Requires PHP: 5.4
    99License: GPLv2 or later
     
    3434== Changelog ==
    3535
    36 = 1.1.5 - 034/03/2024 =
     36= 1.2 - 08/08/2025 =
     37* Refactored and tightened sanitation checks.
     38
     39= 1.1.5 - 04/03/2024 =
    3740* Added icon and banner
    3841* Confirmed compatibility with WP v6.5
Note: See TracChangeset for help on using the changeset viewer.