Hoster Admin Interface Changes

Tech Articles | March 9, 2025 | Automation, Coding, Hoster, Plugins, Wordpress

Almost 24 hours after my last article saying hoster changes were coming, Hoster 1.3 was released, and with it came licensing. I have begun working on integrating this and how this works and testing many different workflows and purchasing flows:

These include:

  • A custom Rest-API (Hoster-API)
  • A custom drop-in SDK which allows me to add licencing to my plugins
  • A custom WooCommerce integration called Hoster-Woo
  • A custom integration I call hoster-gateway will enable me to use hoster with WooCommerce, SureCart, and Easy Digital Downloads, all from a simple interface.

Below is a screenshot of the Hoster Gateway Unified Interface

Hoster Gateway Interface screenshot

I have really enjoyed playing with Hoster, and it’s been fun using what I’ve learned from some of my recent projects for SureCart and Woo to customise a solution.

Working on the slot in the licence SDK allowed me to utilise the hoster licence drop in PHP, and I’ve been able to get to get it so there are filters for plugin name, menu name, text domain, menu location and download id. and I now have a single 35 line block that will allow me to licence any plugin for updates in a matter of a few minutes.

I’ve even created activation and deactivation blocks, which can be seen below

Once I started playing, I knew I needed to make some changes to the admin interface. However, there wasn’t quite enough access to quick-peek information. When automating, the Download ID number is vital as you need it to generate the license, so I wanted that to be accessible, too.

The good news is hoster uses custom post types which means all this meta is available and can be displayed with a bit of admin coloum work. below is the default download menu to add a plugin.

Now, currently, for the workflow, the only thing really missing here is the Download ID, and I wanted it to be easy to copy. So that’s exactly what I did as can be seen below.

Now the default licence screen is shown below and I felt this needed more work which is what prompted the project.

The Licence key isn’t easy to copy; it doesn’t show key information like licence type, activations and expiry information, which I think are vital, so I set about adding those functions and a copy button for the licence key itself

This is fully adjustable, including the column width within the code. I have added this code to several of my plugins, each with a unique defined constant name so they can run it for me, but I also have it as a separate snippet for other users.

<?php
/**
 * Add a custom column (Download ID) to the 'downloads' post type list,
 * and display a clipboard icon for copying.
 */

// 1. Add a new column
add_filter( 'manage_edit-downloads_columns', 'my_custom_downloads_columns' );
function my_custom_downloads_columns( $columns ) {
    // Insert a new column titled "Download ID" after the Title column
    $new_columns = array();
    foreach ( $columns as $key => $value ) {
        $new_columns[ $key ] = $value;
        if ( 'title' === $key ) {
            $new_columns['download_id'] = __( 'Download ID', 'text_domain' );
        }
    }
    return $new_columns;
}

// 2. Populate the column with the post ID and a clipboard icon button
add_action( 'manage_downloads_posts_custom_column', 'my_custom_downloads_column_content', 10, 2 );
function my_custom_downloads_column_content( $column, $post_id ) {
    if ( 'download_id' === $column ) {
        // Display the post ID with a button to copy
        echo '<span class="download-id" style="margin-right:10px;">' . esc_html( $post_id ) . '</span>';
        // Use dashicons for the clipboard icon
        echo '<button class="copy-btn" data-clipboard-text="' . esc_attr( $post_id ) . '" title="' . esc_attr__( 'Copy ID', 'text_domain' ) . '">';
        echo '<span class="dashicons dashicons-clipboard"></span>';
        echo '</button>';
    }
}

// 3. Enqueue a small script for copying functionality
add_action( 'admin_footer', 'my_downloads_id_copy_script' );
function my_downloads_id_copy_script() {
    // Only load this script on the "downloads" listing screen
    $screen = get_current_screen();
    if ( isset( $screen->id ) && 'edit-downloads' === $screen->id ) :
        // Make sure Dashicons are available (usually they are in the admin, but just in case):
        wp_enqueue_style( 'dashicons' );
        ?>
        <script type="text/javascript">
        (function() {
            document.addEventListener('DOMContentLoaded', function() {
                document.querySelectorAll('.copy-btn').forEach(function(button) {
                    button.addEventListener('click', function() {
                        var text = button.getAttribute('data-clipboard-text');
                        if (navigator.clipboard) {
                            navigator.clipboard.writeText(text).then(function() {
                                showCopiedState(button);
                            });
                        } else {
                            // Fallback for older browsers
                            var tempInput = document.createElement('input');
                            tempInput.style.position = 'absolute';
                            tempInput.style.left = '-9999px';
                            tempInput.value = text;
                            document.body.appendChild(tempInput);
                            tempInput.select();
                            document.execCommand('copy');
                            document.body.removeChild(tempInput);
                            showCopiedState(button);
                        }
                    });
                });

                function showCopiedState(button) {
                    // Temporarily show "Copied!" text
                    button.innerHTML = 'Copied!';
                    setTimeout(function() {
                        // Revert to the clipboard icon
                        button.innerHTML = '<span class="dashicons dashicons-clipboard"></span>';
                    }, 2000);
                }
            });
        })();
        </script>
        <?php
    endif;
}
/**
 * 1) Remove the default Title column in the 'hoster_license' post type list.
 * 2) Add a new "Licence Key" column (showing the license_key meta) that links to edit
 *    and has a copy button for the post ID.
 * 3) Restore the download, user, and status columns
 * 4) Add Activation Limit and Expiry Date columns.
 * 5) Adjust column widths via CSS.
 */

/**
 * Modify columns for the hoster_license post type.
 */
add_filter( 'manage_edit-hoster_license_columns', 'custom_hoster_license_columns' );
function custom_hoster_license_columns( $columns ) {

    // Store references to the columns we want to keep
    $cb      = isset( $columns['cb'] )      ? $columns['cb']      : '';
    $download= isset( $columns['download'] )? $columns['download'] : '';
    $user    = isset( $columns['user'] )    ? $columns['user']    : '';
    $status  = isset( $columns['status'] )  ? $columns['status']  : '';
    $date    = isset( $columns['date'] )    ? $columns['date']    : '';

    // Build a fresh set of columns
    $new_columns = array();

    // 1. Checkbox (bulk actions)
    if ( $cb ) {
        $new_columns['cb'] = $cb;
    }

    // 2. Our new "Licence Key" column (replaces the default Title)
    $new_columns['licence_key'] = __( 'Licence Key', 'text_domain' );

    // 3. Restore the 'download', 'user', and 'status' columns if they exist
    if ( $download ) {
        $new_columns['download'] = $download;
    }
    if ( $user ) {
        $new_columns['user'] = $user;
    }
    if ( $status ) {
        $new_columns['status'] = $status;
    }

    // 4. Activation Limit
    $new_columns['activation_limit'] = __( 'Activation Limit', 'text_domain' );

    // 5. Expiry Date
    $new_columns['expiry_date'] = __( 'Expiry Date', 'text_domain' );

    // 6. Date (Published)
    if ( $date ) {
        $new_columns['date'] = $date;
    }

    return $new_columns;
}

/**
 * Populate the custom columns, including the new Licence Key column.
 */
add_action( 'manage_hoster_license_posts_custom_column', 'custom_hoster_license_column_content', 10, 2 );
function custom_hoster_license_column_content( $column, $post_id ) {
    switch ( $column ) {

        case 'licence_key':
            // Retrieve the license_key meta
            $license_key = get_post_meta( $post_id, 'license_key', true );

            // Fallback if empty (optional)
            if ( empty( $license_key ) ) {
                $license_key = __( '(No Licence Key)', 'text_domain' );
            }

            // Create a link to edit the post, but show the license_key text
            $edit_link = get_edit_post_link( $post_id );

            // The clickable licence_key linking to edit
            echo '<strong><a class="row-title" href="' . esc_url( $edit_link ) . '">'
                 . esc_html( $license_key ) . '</a></strong>';

            // Add a copy button (with stopPropagation so it doesn't click the link)
            // Instead of copying the post ID, copy the license key
            echo ' <button onclick="event.stopPropagation();" class="copy-btn" 
                data-clipboard-text="' . esc_attr( $license_key ) . '" 
                title="' . esc_attr__( 'Copy Licence Key', 'text_domain' ) . '" 
                style="border:none; background:none; cursor:pointer;">
                <span class="dashicons dashicons-clipboard"></span>
                </button>';
            break;

        case 'activation_limit':
            $limit = get_post_meta( $post_id, 'activation_limit', true );
            echo ( intval( $limit ) < 1 ) ? 'Unlimited' : esc_html( $limit );
            break;

        case 'expiry_date':
            $date = get_post_meta( $post_id, 'expiry_date', true );
            if ( empty( $date ) ) {
                echo 'Lifetime';
            } else {
                // Format date as dd/mm/yyyy (UK format)
                echo date( 'd/m/Y', strtotime( $date ) );
            }
            break;
    }
}

/**
 * Enqueue the script for copy functionality on the hoster_license listing screen.
 */
add_action( 'admin_footer', 'hoster_license_copy_script' );
function hoster_license_copy_script() {
    $screen = get_current_screen();
    if ( isset( $screen->id ) && 'edit-hoster_license' === $screen->id ) :
        // Ensure Dashicons are available
        wp_enqueue_style( 'dashicons' );
        ?>
        <script type="text/javascript">
        (function() {
            document.addEventListener('DOMContentLoaded', function() {
                document.querySelectorAll('.copy-btn').forEach(function(button) {
                    button.addEventListener('click', function(e) {
                        e.preventDefault();
                        var text = button.getAttribute('data-clipboard-text');
                        if (navigator.clipboard) {
                            navigator.clipboard.writeText(text).then(function() {
                                showCopiedState(button);
                            });
                        } else {
                            // Fallback for older browsers
                            var tempInput = document.createElement('input');
                            tempInput.style.position = 'absolute';
                            tempInput.style.left = '-9999px';
                            tempInput.value = text;
                            document.body.appendChild(tempInput);
                            tempInput.select();
                            document.execCommand('copy');
                            document.body.removeChild(tempInput);
                            showCopiedState(button);
                        }
                    });
                });
                function showCopiedState(button) {
                    var original = button.innerHTML;
                    button.innerHTML = 'Copied!';
                    setTimeout(function() {
                        button.innerHTML = original;
                    }, 2000);
                }
            });
        })();
        </script>
        <?php
    endif;
}

/**
 * Adjust column widths on the hoster_license listing screen.
 */
add_action( 'admin_head', 'hoster_license_column_width_css' );
function hoster_license_column_width_css() {
    $screen = get_current_screen();
    if ( 'edit-hoster_license' === $screen->id ) {
        ?>
        <style>
            /* Make our custom Licence Key column wider */
            .wp-list-table .column-licence_key {
                width: 230px; 
            }
            /* Narrower columns for download, user, status (if they exist) */
            .wp-list-table .column-download {
                width: 200px; 
            }
            .wp-list-table .column-user {
                width: 150px; 
            }
            .wp-list-table .column-status {
                width: 150px; 
            }
            /* Activation Limit and Expiry Date narrower as well */
            .wp-list-table .column-activation_limit {
                width: 150px;
            }
            .wp-list-table .column-expiry_date {
                width: 120px; 
            }
        </style>
        <?php
    }
}
PHP

If you’re interested in what else I’ve been doing with Hoster, you can view my API video below, and I am working on a video for Hoster-Woo too!

Support the Author

buy me a coffee
Really Useful Plugin Logo
Appoligies for any spelling and grammer issue. As a dyslexic i need to rely on tools for this they like me are not perfect but I do try my best