Plugin Directory

Changeset 3346796


Ignore:
Timestamp:
08/19/2025 06:56:30 AM (6 months ago)
Author:
kiranms1996
Message:

Deploy version 1.1.0

Location:
emw-multiple-order-management/trunk
Files:
10 edited

Legend:

Unmodified
Added
Removed
  • emw-multiple-order-management/trunk/CHANGELOG.md

    r3344042 r3346796  
    33All notable changes to this plugin will be documented in this file.
    44
     5---
     6
     7## [1.1.0] - 2025-08-19
     8
     9### Changed
     10- Improved the metadata structure used to store linked orders:
     11  - Now, **all linked orders** (primary and secondary) consistently store a `multipleorders` meta entry.
     12  - This structure includes:
     13    - `is_primary` or `is_secondary` flag.
     14    - Reference IDs for the linked orders (`primary_order_id` or `secondary_order_ids`).
     15- Updated the following methods to support full metadata persistence:
     16  - `ajax_link_orders()` now updates meta for **all orders** in the group.
     17  - `ajax_unlink_orders()` now properly removes metadata from all related orders.
     18  - `ajax_get_linked_order_details()` now outputs accurate linked order data.
     19- Improved the admin **Linked Orders** meta box with a clearer UI and accurate link details.
     20- Refactored the plugin settings page (`render_settings_page()`):
     21  - Now uses server-rendered tabs based on the `?tab=` URL parameter.
     22  - Only the active tab’s template is loaded, improving performance and clarity.
     23- Removed dependency on frontend JavaScript for tab switching.
     24- Updated admin settings templates:
     25  - Wrapped each tab’s content in individual PHP templates.
     26  - Templates are now loaded conditionally based on the selected tab.
     27
     28### Deprecated
     29- Legacy partial `multipleorders` meta structure used prior to v1.0.1:
     30  - Was only set for the primary order (or only for secondaries).
     31  - **Fallback retained temporarily for backward compatibility.**
     32  - ⚠️ **Deprecated:** This fallback will be removed in a future major release (e.g., `v2.0.0`). Please update any custom code depending on the old structure.
     33
     34### Fixed
     35- Fixed inconsistencies in how linked orders were displayed in the admin UI.
     36- Fixed issue where some secondary orders were not saving metadata correctly.
     37
     38---
     39
    540## [1.0.0] - 2025-08-13
     41
    642### Added
    743- Initial release of the Multiple Order Management plugin.
     
    945- Link grouped orders into primary/secondary relationships.
    1046- Admin UI for managing grouped orders.
    11 - Settings panel for order criteria, email templates, and API key.
    12 - REST API to:
    13   - Fetch all orders grouped by site: `/wp-json/emw-mom/v1/orders`
    14   - Get multiple order group by order ID: `/wp-json/emw-mom/v1/multiple-order-group/{order_id}`
    15 - Multisite support for fetching orders from all network sites.
    16 - API authentication via API key (`X-EMW-MOM-API-Key`).
    17 - Toggleable API documentation in plugin admin settings.
     47- Settings panel for:
     48  - Order criteria.
     49  - Email templates.
     50  - Admin emails and CC.
     51  - API key.
     52- REST API:
     53  - Fetch grouped orders across the network: 
     54    `GET /wp-json/emw-mom/v1/orders`
     55  - Get multiple order group by order ID: 
     56    `GET /wp-json/emw-mom/v1/multiple-order-group/{order_id}`
     57- Multisite support: Fetch orders across all network sites.
     58- API authentication via custom API key header (`X-EMW-MOM-API-Key`).
     59- Toggle REST API docs in plugin settings.
    1860
    1961### Fixed
    20 - None — initial version.
     62- None — initial stable release.
    2163
    2264### Security
    23 - REST API secured via API key and settings toggle.
     65- REST API secured via API key and admin toggle.
     66
     67---
  • emw-multiple-order-management/trunk/assets/js/admin-settings.js

    r3344012 r3346796  
    1616    $( document ).ready(
    1717        function () {
    18             // Tab switching.
    19             $( '.nav-tab-wrapper a' ).on(
    20                 'click',
    21                 function ( e ) {
    22                     e.preventDefault();
    23                     var href  = $( this ).attr( 'href' ).substring( 1 );
    24                     var tabId = new URLSearchParams( href ).get( 'tab' );
    25 
    26                     $( '.nav-tab-wrapper a' ).removeClass( 'nav-tab-active' );
    27                     $( this ).addClass( 'nav-tab-active' );
    28                     $( '.tab-content' ).hide();
    29                     $( '#' + tabId ).show();
    30                 }
    31             );
    32 
    33             // Show the first tab by default.
    34             $( '.nav-tab-wrapper a:first' ).trigger( 'click' );
    35 
    3618            // Multi-select functionality.
    3719            $( document ).on(
     
    4628
    4729                    if ( value ) {
    48                             var label = $this.find( 'option:selected' ).text();
    49                             $selectedTags.append(
    50                                 '<span class="emw-mom-tag">' +
    51                                 label +
    52                                 '<span class="emw-mom-remove-tag" data-value="' + value + '">×</span>' +
    53                                 '</span>'
    54                             );
    55 
    56                             var currentValues = $multiSelect.val() || [];
    57                             currentValues.push( value );
    58                             $multiSelect.val( currentValues );
    59                             $this.find( 'option[value="' + value + '"]' ).remove();
    60                             $this.val( '' );
     30                        var label = $this.find( 'option:selected' ).text();
     31                        $selectedTags.append(
     32                            '<span class="emw-mom-tag">' +
     33                            label +
     34                            '<span class="emw-mom-remove-tag" data-value="' + value + '">×</span>' +
     35                            '</span>'
     36                        );
     37
     38                        var currentValues = $multiSelect.val() || [];
     39                        currentValues.push( value );
     40                        $multiSelect.val( currentValues );
     41                        $this.find( 'option[value="' + value + '"]' ).remove();
     42                        $this.val( '' );
    6143                    }
    6244                }
     
    10991                debounce(
    11092                    function ( e ) {
    111                             e.preventDefault();
    112                             var groupId    = $( this ).data( 'group-id' );
    113                             var $popup     = $( '#emw-mom-popup' );
    114                             var $popupBody = $( '#emw-mom-popup-body' );
    115 
    116                             $.ajax(
    117                                 {
    118                                     url: emw_mom_admin.ajax_url,
    119                                     type: 'POST',
    120                                     data: {
    121                                         action: 'emw_mom_get_group_details',
    122                                         nonce: emw_mom_admin.nonce_get_group_details,
    123                                         group_id: groupId
    124                                     },
    125                                     beforeSend: function () {
    126                                         $( 'body' ).addClass( 'no-scroll' );
    127                                     },
    128                                     success: function ( response ) {
    129                                         if ( response.success ) {
    130                                             $popupBody.html( response.data.content );
    131                                             $popup.fadeIn();
    132                                         } else {
    133                                             showError( response.data || 'Error loading group details.' );
    134                                             $( 'body' ).removeClass( 'no-scroll' );
    135                                         }
    136                                     },
    137                                     error: function () {
    138                                         showError( 'Failed to load group details.' );
     93                        e.preventDefault();
     94                        var groupId    = $( this ).data( 'group-id' );
     95                        var $popup     = $( '#emw-mom-popup' );
     96                        var $popupBody = $( '#emw-mom-popup-body' );
     97
     98                        $.ajax(
     99                            {
     100                                url: emw_mom_admin.ajax_url,
     101                                type: 'POST',
     102                                data: {
     103                                    action: 'emw_mom_get_group_details',
     104                                    nonce: emw_mom_admin.nonce_get_group_details,
     105                                    group_id: groupId
     106                                },
     107                                beforeSend: function () {
     108                                    $( 'body' ).addClass( 'no-scroll' );
     109                                },
     110                                success: function ( response ) {
     111                                    if ( response.success ) {
     112                                        $popupBody.html( response.data.content );
     113                                        $popup.fadeIn();
     114                                    } else {
     115                                        showError( response.data || 'Error loading group details.' );
    139116                                        $( 'body' ).removeClass( 'no-scroll' );
    140117                                    }
     118                                },
     119                                error: function () {
     120                                    showError( 'Failed to load group details.' );
     121                                    $( 'body' ).removeClass( 'no-scroll' );
    141122                                }
    142                             );
     123                            }
     124                        );
    143125                    },
    144126                    300
     
    165147                function ( e ) {
    166148                    if ( $( e.target ).is( '#emw-mom-popup' ) ) {
    167                             var $popup     = $( '#emw-mom-popup' );
    168                             var $popupBody = $( '#emw-mom-popup-body' );
    169                             $popup.fadeOut();
    170                             $popupBody.empty();
    171                             $( 'body' ).removeClass( 'no-scroll' );
     149                        var $popup     = $( '#emw-mom-popup' );
     150                        var $popupBody = $( '#emw-mom-popup-body' );
     151                        $popup.fadeOut();
     152                        $popupBody.empty();
     153                        $( 'body' ).removeClass( 'no-scroll' );
    172154                    }
    173155                }
     
    180162                debounce(
    181163                    function ( e ) {
    182                             e.preventDefault();
    183                             var $btn            = $( this );
    184                             var $form           = $( '#emw-mom-link-orders-form' );
    185                             var primaryOrder    = $form.find( 'input[name="primary_order"]:checked' ).val();
    186                             var secondaryOrders = [];
    187 
    188                             $form.find( 'input[name="secondary_orders[]"]:checked' ).each(
    189                                 function () {
    190                                     secondaryOrders.push( $( this ).val() );
    191                                 }
    192                             );
     164                        e.preventDefault();
     165                        var $btn            = $( this );
     166                        var $form           = $( '#emw-mom-link-orders-form' );
     167                        var primaryOrder    = $form.find( 'input[name="primary_order"]:checked' ).val();
     168                        var secondaryOrders = [];
     169
     170                        $form.find( 'input[name="secondary_orders[]"]:checked' ).each(
     171                            function () {
     172                                secondaryOrders.push( $( this ).val() );
     173                            }
     174                        );
    193175
    194176                        if ( ! primaryOrder ) {
     
    202184                        }
    203185
    204                             var multipleOrderId = $btn.data( 'multiple-order-id' );
    205 
    206                             $btn.prop( 'disabled', true ).text( 'Linking...' );
    207 
    208                             $.ajax(
    209                                 {
    210                                     url: emw_mom_admin.ajax_url,
    211                                     method: 'POST',
    212                                     data: {
    213                                         action: 'emw_mom_link_orders',
    214                                         nonce: emw_mom_admin.nonce_link_orders,
    215                                         primary_order_id: primaryOrder,
    216                                         secondary_order_ids: secondaryOrders,
    217                                         multiple_order_id: multipleOrderId,
    218                                     },
    219                                     success: function ( response ) {
    220                                         if ( response.success ) {
    221                                             showNotice( 'Orders linked successfully.', 'success' );
    222                                             $( '#emw-mom-popup' ).fadeOut();
    223                                             $( '#emw-mom-popup-body' ).empty();
    224                                             $( 'body' ).removeClass( 'no-scroll' );
    225                                         } else {
    226                                             showError( response.data || 'Error linking orders.' );
    227                                         }
    228                                     },
    229                                     error: function () {
    230                                         showError( 'AJAX request failed.' );
    231                                     },
    232                                     complete: function () {
    233                                         $btn.prop( 'disabled', false ).text( 'Link Orders' );
     186                        var multipleOrderId = $btn.data( 'multiple-order-id' );
     187
     188                        $btn.prop( 'disabled', true ).text( 'Linking...' );
     189
     190                        $.ajax(
     191                            {
     192                                url: emw_mom_admin.ajax_url,
     193                                method: 'POST',
     194                                data: {
     195                                    action: 'emw_mom_link_orders',
     196                                    nonce: emw_mom_admin.nonce_link_orders,
     197                                    primary_order_id: primaryOrder,
     198                                    secondary_order_ids: secondaryOrders,
     199                                    multiple_order_id: multipleOrderId,
     200                                },
     201                                success: function ( response ) {
     202                                    if ( response.success ) {
     203                                        showNotice( 'Orders linked successfully.', 'success' );
     204                                        $( '#emw-mom-popup' ).fadeOut();
     205                                        $( '#emw-mom-popup-body' ).empty();
     206                                        $( 'body' ).removeClass( 'no-scroll' );
     207                                    } else {
     208                                        showError( response.data || 'Error linking orders.' );
    234209                                    }
     210                                },
     211                                error: function () {
     212                                    showError( 'AJAX request failed.' );
     213                                },
     214                                complete: function () {
     215                                    $btn.prop( 'disabled', false ).text( 'Link Orders' );
    235216                                }
    236                             );
     217                            }
     218                        );
    237219                    },
    238220                    300
  • emw-multiple-order-management/trunk/emw-multiple-order-management.php

    r3344042 r3346796  
    22/**
    33 * Plugin Name: Multiple Order Management For WooCommerce
     4 * Plugin URI: https://wordpress.org/plugins/emw-multiple-order-management/
    45 * Description: A plugin to manage and link multiple WooCommerce orders based on custom criteria.
    5  * Version: 1.0.0
     6 * Version: 1.1.0
    67 * Author: Kiran M S
    78 * Author URI: https://profiles.wordpress.org/kiranms1996/
     
    2122 * Define plugin constants.
    2223 */
    23 define( 'EMW_MOM_VERSION', '1.0.1' );
     24define( 'EMW_MOM_VERSION', '1.1.0' );
    2425define( 'EMW_MOM_PLUGIN_DIR', plugin_dir_path( __FILE__ ) );
    2526define( 'EMW_MOM_PLUGIN_URL', plugin_dir_url( __FILE__ ) );
  • emw-multiple-order-management/trunk/includes/class-emw-mom-admin.php

    r3344012 r3346796  
    165165    public function render_settings_page() {
    166166        // Get settings data.
    167         $wc_statuses      = wc_get_order_statuses();
    168         $allowed_statuses = $this->settings->get_allowed_statuses();
    169         $email_subject    = $this->settings->get_email_subject();
    170         $admin_email      = $this->settings->get_admin_email();
    171         $cc_email         = $this->settings->get_cc_email();
    172         $email_template   = $this->settings->get_email_template();
    173         $criteria         = $this->settings->get_criteria_settings();
    174         $rest_api_enabled = $this->settings->get_rest_api_enabled();
    175         $rest_api_key     = $this->settings->get_rest_api_key();
     167        $wc_statuses       = wc_get_order_statuses();
     168        $allowed_statuses  = $this->settings->get_allowed_statuses();
     169        $email_subject     = $this->settings->get_email_subject();
     170        $admin_email       = $this->settings->get_admin_email();
     171        $cc_email          = $this->settings->get_cc_email();
     172        $email_template    = $this->settings->get_email_template();
     173        $criteria          = $this->settings->get_criteria_settings();
     174        $rest_api_enabled  = $this->settings->get_rest_api_enabled();
     175        $rest_api_key      = $this->settings->get_rest_api_key();
    176176
    177177        // Define tabs.
    178         $tabs       = array(
     178        $tabs = array(
    179179            'general'       => esc_html__( 'General', 'emw-multiple-order-management' ),
    180180            'notifications' => esc_html__( 'Notifications', 'emw-multiple-order-management' ),
     
    183183            'pro-ver'       => wp_kses_post( 'Pro Version <span class="dashicons dashicons-star-filled" style="color: #ffb900;vertical-align: middle;"></span>' ),
    184184        );
     185
    185186        $active_tab = isset( $_GET['tab'] ) ? sanitize_text_field( wp_unslash( $_GET['tab'] ) ) : 'general'; // phpcs:ignore WordPress.Security.NonceVerification
    186187
     
    188189        <div class="wrap">
    189190            <h1><?php esc_html_e( 'Order Management Settings', 'emw-multiple-order-management' ); ?></h1>
    190             <h2 class="nav-tab-wrapper">
     191            <nav class="nav-tab-wrapper">
    191192                <?php foreach ( $tabs as $tab_id => $tab_name ) : ?>
    192193                    <a href="?page=emw-mom-settings&tab=<?php echo esc_attr( $tab_id ); ?>" class="nav-tab <?php echo $active_tab === $tab_id ? 'nav-tab-active' : ''; ?>">
     
    194195                    </a>
    195196                <?php endforeach; ?>
    196             </h2>
     197            </nav>
     198
    197199            <?php
    198                 require_once EMW_MOM_PLUGIN_DIR . 'templates/general-settings.php';
    199                 require_once EMW_MOM_PLUGIN_DIR . 'templates/notifications-settings.php';
    200                 require_once EMW_MOM_PLUGIN_DIR . 'templates/criteria-settings.php';
    201                 require_once EMW_MOM_PLUGIN_DIR . 'templates/rest-api-settings.php';
    202                 require_once EMW_MOM_PLUGIN_DIR . 'templates/pro-version-settings.php';
     200            switch ( $active_tab ) {
     201                case 'notifications':
     202                    require_once EMW_MOM_PLUGIN_DIR . 'templates/notifications-settings.php';
     203                    break;
     204
     205                case 'criteria':
     206                    require_once EMW_MOM_PLUGIN_DIR . 'templates/criteria-settings.php';
     207                    break;
     208
     209                case 'rest-api':
     210                    require_once EMW_MOM_PLUGIN_DIR . 'templates/rest-api-settings.php';
     211                    break;
     212
     213                case 'pro-ver':
     214                    require_once EMW_MOM_PLUGIN_DIR . 'templates/pro-version-settings.php';
     215                    break;
     216
     217                case 'general':
     218                default:
     219                    require_once EMW_MOM_PLUGIN_DIR . 'templates/general-settings.php';
     220                    break;
     221            }
    203222            ?>
    204223        </div>
     
    247266    /**
    248267     * AJAX handler for linking orders.
     268     *
     269     * Links primary and secondary orders together, updates post meta
     270     * for all involved orders with complete linkage data.
    249271     */
    250272    public function ajax_link_orders() {
     
    264286        }
    265287
     288        // Collect all involved order IDs for shared linkage reference.
     289        $all_linked_order_ids = array_merge( array( $primary_order_id ), $secondary_order_ids );
     290
     291        // Update secondary orders.
    266292        foreach ( $secondary_order_ids as $secondary_order_id ) {
    267293            $secondary_order = wc_get_order( $secondary_order_id );
     
    274300                'multipleorders',
    275301                array(
    276                     'is_secondary'     => 'yes',
    277                     'primary_order_id' => $primary_order_id,
     302                    'is_secondary'      => 'yes',
     303                    'primary_order_id'  => $primary_order_id,
     304                    'linked_order_ids'  => $all_linked_order_ids,
    278305                )
    279306            );
    280307        }
    281308
     309        // Update primary order.
    282310        update_post_meta(
    283311            $primary_order_id,
     
    286314                'is_primary'          => 'yes',
    287315                'secondary_order_ids' => $secondary_order_ids,
     316                'linked_order_ids'    => $all_linked_order_ids,
    288317            )
    289318        );
    290319
     320        // Mark group as linked in custom DB table.
    291321        global $wpdb;
    292322        $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     
    303333    /**
    304334     * AJAX handler for unlinking orders.
     335     *
     336     * Removes the linkage metadata from all orders in a group,
     337     * and marks the group as unlinked in the DB.
    305338     */
    306339    public function ajax_unlink_orders() {
     
    313346
    314347        global $wpdb;
    315         $group = $wpdb->get_row( $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}emw_mom_order_groups WHERE id = %d", $group_id ), ARRAY_A ); // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     348
     349        // Fetch the order group.
     350        $group = $wpdb->get_row(
     351            $wpdb->prepare( "SELECT * FROM {$wpdb->prefix}emw_mom_order_groups WHERE id = %d", $group_id ), // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
     352            ARRAY_A
     353        );
     354
    316355        if ( ! $group ) {
    317356            wp_send_json_error( esc_html__( 'Group not found.', 'emw-multiple-order-management' ) );
    318357        }
    319358
    320         $order_ids = explode( ',', $group['order_ids'] );
     359        // Explode and sanitize order IDs.
     360        $order_ids = array_filter( array_map( 'intval', explode( ',', $group['order_ids'] ) ) );
     361
    321362        foreach ( $order_ids as $order_id ) {
    322             delete_post_meta( $order_id, 'multipleorders' );
    323         }
    324 
     363            $meta = get_post_meta( $order_id, 'multipleorders', true );
     364
     365            // Ensure the meta is an array before proceeding.
     366            if ( is_array( $meta ) ) {
     367                // Remove only our known keys to avoid destroying unrelated data.
     368                unset(
     369                    $meta['is_primary'],
     370                    $meta['is_secondary'],
     371                    $meta['primary_order_id'],
     372                    $meta['secondary_order_ids'],
     373                    $meta['linked_order_ids']
     374                );
     375
     376                // If meta still has other keys, update it; otherwise delete it.
     377                if ( ! empty( $meta ) ) {
     378                    update_post_meta( $order_id, 'multipleorders', $meta );
     379                } else {
     380                    delete_post_meta( $order_id, 'multipleorders' );
     381                }
     382            } else {
     383                // Meta is not structured as expected, remove it entirely.
     384                delete_post_meta( $order_id, 'multipleorders' );
     385            }
     386        }
     387
     388        // Mark group as unlinked.
    325389        $wpdb->update( // phpcs:ignore WordPress.DB.DirectDatabaseQuery, WordPress.DB.DirectDatabaseQuery.NoCaching
    326390            $wpdb->prefix . 'emw_mom_order_groups',
     
    336400
    337401    /**
    338      * AJAX handler for getting linked order details.
     402     * AJAX handler for retrieving linked order details.
     403     *
     404     * Responds to the request from the "View Linked Orders" button.
     405     * Handles both legacy structure (`is_primary`, `is_secondary`, etc.)
     406     * and new structure using `linked_order_ids` in all orders.
     407     *
     408     * @since 1.0.0
     409     * @return void
    339410     */
    340411    public function ajax_get_linked_order_details() {
    341412        check_ajax_referer( 'emw_mom_get_linked_order_details', 'nonce' );
    342413
    343         $order_id = isset( $_POST['order_id'] ) ? intval( $_POST['order_id'] ) : 0; // phpcs:ignore WordPress.Security.NonceVerification
     414        // Get and validate order ID from POST data.
     415        $order_id = isset( $_POST['order_id'] ) ? intval( $_POST['order_id'] ) : 0;
    344416        $order    = wc_get_order( $order_id );
     417
    345418        if ( ! $order ) {
    346419            wp_die( esc_html__( 'Order not found.', 'emw-multiple-order-management' ) );
    347420        }
    348421
     422        // Fetch order meta for linked orders.
    349423        $meta = get_post_meta( $order_id, 'multipleorders', true );
     424
    350425        ob_start();
     426
     427        // Handle missing or malformed meta data.
     428        if ( ! is_array( $meta ) ) {
     429            echo '<p>' . esc_html__( 'No linked order data found.', 'emw-multiple-order-management' ) . '</p>';
     430            wp_die( wp_kses_post( ob_get_clean() ) );
     431        }
     432
     433        // Extract values from meta.
     434        $is_primary        = isset( $meta['is_primary'] ) && 'yes' === $meta['is_primary'];
     435        $is_secondary      = isset( $meta['is_secondary'] ) && 'yes' === $meta['is_secondary'];
     436        $linked_order_ids  = isset( $meta['linked_order_ids'] ) && is_array( $meta['linked_order_ids'] ) ? $meta['linked_order_ids'] : array();
     437
    351438        ?>
    352         <p>
    353             <?php
    354             if ( isset( $meta['is_primary'] ) && 'yes' === $meta['is_primary'] ) {
    355                 echo esc_html__( 'Primary Order', 'emw-multiple-order-management' );
    356                 if ( ! empty( $meta['secondary_order_ids'] ) ) {
    357                     echo '<ul>';
    358                     foreach ( $meta['secondary_order_ids'] as $secondary_id ) {
    359                         $secondary_order = wc_get_order( $secondary_id );
    360                         if ( $secondary_order ) {
    361                             echo '<li><a href="' . esc_url( $secondary_order->get_edit_order_url() ) . '">#' . esc_html( $secondary_order->get_order_number() ) . '</a></li>';
     439        <div class="emw-mom-linked-order-details">
     440            <p>
     441                <?php if ( $is_primary ) : ?>
     442                    <strong><?php esc_html_e( 'Primary Order', 'emw-multiple-order-management' ); ?></strong>
     443                <?php elseif ( $is_secondary ) : ?>
     444                    <strong><?php esc_html_e( 'Secondary Order', 'emw-multiple-order-management' ); ?></strong>
     445                <?php endif; ?>
     446            </p>
     447
     448            <?php if ( ! empty( $linked_order_ids ) ) : ?>
     449                <!-- New structure: Show all linked orders except current -->
     450                <p><?php esc_html_e( 'Linked Orders:', 'emw-multiple-order-management' ); ?></p>
     451                <ul>
     452                    <?php
     453                    foreach ( $linked_order_ids as $linked_id ) {
     454                        // Skip current order to avoid showing self in list.
     455                        if ( $linked_id == $order_id ) {
     456                            continue;
     457                        }
     458
     459                        $linked_order = wc_get_order( $linked_id );
     460                        if ( $linked_order ) {
     461                            printf(
     462                                '<li><a href="%1$s">#%2$s</a></li>',
     463                                esc_url( $linked_order->get_edit_order_url() ),
     464                                esc_html( $linked_order->get_order_number() )
     465                            );
    362466                        }
    363467                    }
    364                     echo '</ul>';
    365                 }
    366             } elseif ( isset( $meta['is_secondary'] ) && 'yes' === $meta['is_secondary'] ) {
    367                 $primary_order = wc_get_order( $meta['primary_order_id'] );
    368                 if ( $primary_order ) {
    369                     printf(
    370                         esc_html__( 'Secondary Order, linked to Primary Order', 'emw-multiple-order-management' ) . ' #%s',
    371                         '<a href="' . esc_url( $primary_order->get_edit_order_url() ) . '">#' . esc_html( $primary_order->get_order_number() ) . '</a>'
    372                     );
    373                 }
    374             }
    375             ?>
    376         </p>
     468                    ?>
     469                </ul>
     470
     471            <?php elseif ( $is_primary && ! empty( $meta['secondary_order_ids'] ) ) : ?>
     472                <!-- Legacy structure: Show secondary orders under primary -->
     473                <p><?php esc_html_e( 'Linked Secondary Orders:', 'emw-multiple-order-management' ); ?></p>
     474                <ul>
     475                    <?php
     476                    foreach ( $meta['secondary_order_ids'] as $secondary_id ) {
     477                        $linked_order = wc_get_order( $secondary_id );
     478                        if ( $linked_order ) {
     479                            printf(
     480                                '<li><a href="%1$s">#%2$s</a></li>',
     481                                esc_url( $linked_order->get_edit_order_url() ),
     482                                esc_html( $linked_order->get_order_number() )
     483                            );
     484                        }
     485                    }
     486                    ?>
     487                </ul>
     488
     489            <?php elseif ( $is_secondary && ! empty( $meta['primary_order_id'] ) ) : ?>
     490                <!-- Legacy structure: Show linked primary order for secondary -->
     491                <p>
     492                    <?php
     493                    $primary_order = wc_get_order( $meta['primary_order_id'] );
     494                    if ( $primary_order ) {
     495                        printf(
     496                            /* translators: %s: Link to primary order */
     497                            esc_html__( 'Linked to Primary Order %s', 'emw-multiple-order-management' ),
     498                            '<a href="' . esc_url( $primary_order->get_edit_order_url() ) . '">#' . esc_html( $primary_order->get_order_number() ) . '</a>'
     499                        );
     500                    }
     501                    ?>
     502                </p>
     503
     504            <?php else : ?>
     505                <!-- Fallback: No links found -->
     506                <p><?php esc_html_e( 'No linked orders found.', 'emw-multiple-order-management' ); ?></p>
     507            <?php endif; ?>
     508        </div>
    377509        <?php
     510
     511        // Output final content and exit.
    378512        wp_die( wp_kses_post( ob_get_clean() ) );
    379513    }
     
    394528
    395529    /**
    396      * Render meta box content.
    397      *
    398      * @param WP_Post $post Post object.
     530     * Renders the "Linked Orders" meta box on the order edit screen.
     531     *
     532     * This box shows whether the order is a primary or secondary in a group,
     533     * and displays links to other related orders based on the metadata.
     534     *
     535     * Supports both:
     536     *  - New unified structure (`linked_order_ids`),
     537     *  - Legacy fallback structure (`is_primary`, `is_secondary`, `primary_order_id`, `secondary_order_ids`).
     538     *
     539     * @todo Remove legacy fallback support in a future version after migration.
     540     *
     541     * @param WP_Post $post The current order post object.
    399542     */
    400543    public function render_order_meta_box( $post ) {
    401544        $order = wc_get_order( $post->ID );
    402         $meta  = get_post_meta( $post->ID, 'multipleorders', true );
    403         if ( ! $meta ) {
     545
     546        // Fetch linked orders metadata.
     547        $meta = get_post_meta( $post->ID, 'multipleorders', true );
     548
     549        // If no metadata, there's nothing to display.
     550        if ( ! $meta || ! is_array( $meta ) ) {
    404551            echo '<p>' . esc_html__( 'No linked orders.', 'emw-multiple-order-management' ) . '</p>';
    405552            return;
    406553        }
     554
     555        // Extract meta values.
     556        $is_primary        = isset( $meta['is_primary'] ) && 'yes' === $meta['is_primary'];
     557        $is_secondary      = isset( $meta['is_secondary'] ) && 'yes' === $meta['is_secondary'];
     558        $linked_order_ids  = isset( $meta['linked_order_ids'] ) && is_array( $meta['linked_order_ids'] ) ? $meta['linked_order_ids'] : array();
    407559
    408560        ?>
     
    410562            <p>
    411563                <?php
    412                 if ( isset( $meta['is_primary'] ) && 'yes' === $meta['is_primary'] ) {
     564                // Display role badge.
     565                if ( $is_primary ) {
    413566                    echo '<span class="emw-mom-badge role-primary">' . esc_html__( 'Primary', 'emw-multiple-order-management' ) . '</span>';
    414                     if ( ! empty( $meta['secondary_order_ids'] ) ) {
    415                         echo '<ul>';
    416                         foreach ( $meta['secondary_order_ids'] as $secondary_id ) {
    417                             $secondary_order = wc_get_order( $secondary_id );
    418                             if ( $secondary_order ) {
    419                                 echo '<li><a href="' . esc_url( $secondary_order->get_edit_order_url() ) . '">#' . esc_html( $secondary_order->get_order_number() ) . '</a></li>';
    420                             }
     567                } elseif ( $is_secondary ) {
     568                    echo '<span class="emw-mom-badge role-secondary">' . esc_html__( 'Secondary', 'emw-multiple-order-management' ) . '</span>';
     569                }
     570                ?>
     571
     572                <?php
     573                // Display linked orders list using the new unified structure if available.
     574                if ( ! empty( $linked_order_ids ) ) :
     575                    echo '<ul>';
     576                    foreach ( $linked_order_ids as $linked_id ) {
     577                        // Skip self.
     578                        if ( $linked_id == $post->ID ) {
     579                            continue;
    421580                        }
    422                         echo '</ul>';
     581                        $linked_order = wc_get_order( $linked_id );
     582                        if ( $linked_order ) {
     583                            printf(
     584                                '<li><a href="%1$s">#%2$s</a></li>',
     585                                esc_url( $linked_order->get_edit_order_url() ),
     586                                esc_html( $linked_order->get_order_number() )
     587                            );
     588                        }
    423589                    }
    424                 } elseif ( isset( $meta['is_secondary'] ) && 'yes' === $meta['is_secondary'] ) {
     590                    echo '</ul>';
     591
     592                /**
     593                 * LEGACY FALLBACK SUPPORT - to be removed in future versions.
     594                 * This block supports the old meta keys 'secondary_order_ids' and 'primary_order_id'.
     595                 */
     596                elseif ( $is_primary && ! empty( $meta['secondary_order_ids'] ) ) :
     597                    echo '<ul>';
     598                    foreach ( $meta['secondary_order_ids'] as $secondary_id ) {
     599                        $secondary_order = wc_get_order( $secondary_id );
     600                        if ( $secondary_order ) {
     601                            printf(
     602                                '<li><a href="%1$s">#%2$s</a></li>',
     603                                esc_url( $secondary_order->get_edit_order_url() ),
     604                                esc_html( $secondary_order->get_order_number() )
     605                            );
     606                        }
     607                    }
     608                    echo '</ul>';
     609
     610                elseif ( $is_secondary && ! empty( $meta['primary_order_id'] ) ) :
    425611                    $primary_order = wc_get_order( $meta['primary_order_id'] );
    426612                    if ( $primary_order ) {
    427                         echo '<span class="emw-mom-badge role-secondary">' . esc_html__( 'Secondary', 'emw-multiple-order-management' ) . '</span>';
    428613                        echo '<p>' . sprintf(
    429                             esc_html__( 'Linked to Primary Order', 'emw-multiple-order-management' ) . ' %s',
     614                            esc_html__( 'Linked to Primary Order %s', 'emw-multiple-order-management' ),
    430615                            '<a href="' . esc_url( $primary_order->get_edit_order_url() ) . '">#' . esc_html( $primary_order->get_order_number() ) . '</a>'
    431616                        ) . '</p>';
    432617                    }
    433                 }
     618                endif;
    434619                ?>
     620
     621                <!-- Button to open modal with full linked details -->
    435622                <button type="button" class="button view-linked-orders-btn" data-order-id="<?php echo esc_attr( $post->ID ); ?>">
    436623                    <?php esc_html_e( 'View Details', 'emw-multiple-order-management' ); ?>
     
    438625            </p>
    439626        </div>
     627
     628        <!-- Hidden modal for AJAX-loaded linked order content -->
    440629        <div id="emw-mom-linked-orders-modal" class="emw-mom-modal" style="display: none;">
    441630            <div class="emw-mom-modal-content">
  • emw-multiple-order-management/trunk/languages/emw-multiple-order-management.pot

    r3344012 r3346796  
    33msgid ""
    44msgstr ""
    5 "Project-Id-Version: EMW Multiple Order Management  1.0.1\n"
     5"Project-Id-Version: EMW Multiple Order Management  1.1.0\n"
    66"Report-Msgid-Bugs-To: https://example.com/support\n"
    77"POT-Creation-Date: 2025-07-03 16:14+0530\n"
  • emw-multiple-order-management/trunk/readme.MD

    r3344042 r3346796  
    44Requires at least: 5.0
    55Tested up to: 6.8
    6 Stable tag: 1.0.0
     6Stable tag: 1.1.0
    77Requires PHP: 7.2
    88License: GPLv2 or later
     
    4848- Multisite compatible – works across subsites.
    4949
    50 
    5150== REST API ==
    5251This plugin exposes custom REST API endpoints for external access:
     
    6362
    6463== Developer Notes ==
    65 You can extend the plugin using hooks
     64You can extend the plugin using the following hooks and filters:
     65- `emw_mom_order_group_created`
     66- `emw_mom_order_linked`
     67- `emw_mom_api_key_validated`
     68- ...and more.
    6669
    6770== Changelog ==
    68 = 1.0.0 =
    69 - Initial release of the Multiple Order Management plugin.
    70 - Ability to group WooCommerce orders based on customizable criteria.
    71 - Link grouped orders into primary/secondary relationships.
    72 - Admin UI for managing grouped orders.
    73 - Settings panel for order criteria, email templates, and API key.
    74 - REST API to:
     71
     72= 1.1.0 - 2025-08-18 =
     73* Changed: Unified `multipleorders` meta structure for all linked orders.
     74* Changed: Improved `ajax_link_orders` and `ajax_unlink_orders` functions to reflect accurate linked/unlinked state for each order.
     75* Changed: Updated `render_settings_page()` method to load settings tabs using PHP instead of JavaScript.
     76* Changed: Admin settings UI now loads only the active tab's content for better performance.
     77* Changed: Refactored tab template loading using a `switch` statement in `render_settings_page()`.
     78* Changed: Updated settings page navigation to use server-rendered tabs with persistent URLs.
     79* Changed: Improved Linked Orders meta box UI for clarity and usability.
     80* Deprecated: Legacy meta structure fallback for backward compatibility (to be removed in a future release).
     81* Fixed: Admin UI now displays correct linked order metadata across all views.
     82
     83= 1.0.0 - 2025-08-13 =
     84* Initial release of the Multiple Order Management plugin.
     85* Ability to group WooCommerce orders based on customizable criteria.
     86* Link grouped orders into primary/secondary relationships.
     87* Admin UI for managing grouped orders.
     88* Settings panel for order criteria, email templates, and API key.
     89* REST API to:
    7590  - Fetch all orders grouped by site: `/wp-json/emw-mom/v1/orders`
    7691  - Get multiple order group by order ID: `/wp-json/emw-mom/v1/multiple-order-group/{order_id}`
    77 - Multisite support for fetching orders from all network sites.
    78 - API authentication via API key (`X-EMW-MOM-API-Key`).
    79 - Toggleable API documentation in plugin admin settings.
     92* Multisite support for fetching orders from all network sites.
     93* API authentication via API key (`X-EMW-MOM-API-Key`).
     94* Toggleable API documentation in plugin admin settings.
  • emw-multiple-order-management/trunk/templates/criteria-settings.php

    r3344012 r3346796  
    2121?>
    2222
    23 <div id="criteria" class="tab-content" style="display: none;">
     23<div id="criteria" class="tab-content">
    2424    <?php do_action( 'emw_mom_before_render_criteria_tab' ); ?>
    2525    <form method="post" action="">
  • emw-multiple-order-management/trunk/templates/notifications-settings.php

    r3344012 r3346796  
    1313?>
    1414
    15 <div id="notifications" class="tab-content" style="display: none;">
     15<div id="notifications" class="tab-content">
    1616    <?php do_action( 'emw_mom_before_render_notifications_tab' ); ?>
    1717    <form method="post" action="">
  • emw-multiple-order-management/trunk/templates/pro-version-settings.php

    r3344012 r3346796  
    1111?>
    1212
    13 <div id="pro-ver" class="tab-content" style="display: none;">
     13<div id="pro-ver" class="tab-content">
    1414    <div class="emw-mom-pro-notice">
    1515        <h2><?php esc_html_e( 'Pro Features Coming Soon!', 'emw-multiple-order-management' ); ?></h2>
  • emw-multiple-order-management/trunk/templates/rest-api-settings.php

    r3344012 r3346796  
    1313?>
    1414
    15 <div id="rest-api" class="tab-content" style="display: none;">
     15<div id="rest-api" class="tab-content">
    1616    <form method="post" action="">
    1717        <?php wp_nonce_field( 'emw_mom_save_settings', 'emw_mom_settings_nonce' ); ?>
Note: See TracChangeset for help on using the changeset viewer.