public function output_message_list($allow_reply = true)
    {
        $message_id = $this->get('shub_message_id');
        $comments = $this->get_comments();
        $x = 0;
        foreach ($comments as $comment) {
            $x++;
            $from_user = $this->get_user($comment['shub_user_id']);
            $time = isset($comment['time']) ? $comment['time'] : false;
            // is this a queued-to-send message?
            $extra_class = '';
            $comment_status = '';
            $message_error = false;
            if (!empty($comment['shub_outbox_id'])) {
                $shub_outbox = new SupportHubOutbox($comment['shub_outbox_id']);
                if ($shub_outbox->get('shub_outbox_id') != $comment['shub_outbox_id']) {
                    // the outbox entry has been removed but this comment still references it
                    // todo: update this comment entry to not contain an shub_outbox_id
                } else {
                    switch ($shub_outbox->get('shub_status')) {
                        case _SHUB_OUTBOX_STATUS_QUEUED:
                        case _SHUB_OUTBOX_STATUS_SENDING:
                            $extra_class .= ' outbox_queued';
                            $comment_status = 'Currently Sending....' . $shub_outbox->get('shub_status');
                            break;
                        case _SHUB_OUTBOX_STATUS_FAILED:
                            $extra_class .= ' outbox_failed';
                            $comment_status = 'Failed to send message! Please check logs.';
                            $message_error = true;
                            break;
                    }
                }
            }
            if (!empty($comment['private'])) {
                $extra_class .= ' shub_message_private';
            }
            ?>
            <div class="shub_message shub_message_<?php 
            echo $x == 1 ? 'primary' : 'reply';
            echo $extra_class;
            ?>
">
                <div class="shub_message_picture">
                    <img src="<?php 
            echo $from_user->get_image();
            ?>
" />
                </div>
                <div class="shub_message_header">
                    <?php 
            if ($comment_status) {
                ?>
                        <div class="shub_comment_status"><?php 
                echo $comment_status;
                ?>
</div>
                    <?php 
            }
            ?>
                    <?php 
            echo $from_user->get_full_link();
            ?>
                    <span>
                        <?php 
            if ($time) {
                ?>
                            <span class="time" data-time="<?php 
                echo esc_attr($time);
                ?>
" data-date="<?php 
                echo esc_attr(shub_print_date($time, true));
                ?>
"><?php 
                echo shub_pretty_date($time);
                ?>
</span>
                        <?php 
            }
            ?>
                        <span class="wp_user">
                        <?php 
            // todo - better this! don't call on every message, load list in main loop and pass through all results.
            if (isset($envato_data['user_id']) && $envato_data['user_id']) {
                $user_info = get_userdata($envato_data['user_id']);
                echo ' (sent by ' . htmlspecialchars($user_info->display_name) . ')';
            }
            ?>
                        </span>
                        <span class="buyer_status_badges">
                            <?php 
            // work out of this buyer has bought something via the envato module.
            // first we have to find out what item this is:
            //echo "user:"******"<br>product:".$this->get_product_id()."<br>";
            // todo: store these as a flag in the message database so we can run stats on them and display a graph on the dashboard.
            $buyer_status = $this->get_buyer_status($comment['shub_user_id']);
            if (!empty($buyer_status['purchased'])) {
                echo '<span class="buyer_badge purchased">Purchased</span> ';
            }
            if (!empty($buyer_status['supported'])) {
                echo '<span class="buyer_badge supported">Supported</span> ';
            }
            if (!empty($buyer_status['unsupported'])) {
                echo '<span class="buyer_badge unsupported">Unsupported</span> ';
            }
            if (!empty($buyer_status['presale'])) {
                //echo '<span class="buyer_badge presale">Pre-sale</span> ';
            }
            // todo - add a badge for staff reply.
            ?>
                        </span>
                    </span>
                </div>
                <div class="shub_message_body">
                    <div>
                        <?php 
            echo shub_forum_text($comment['message_text']);
            ?>
                    </div>
                </div>
                <div class="shub_message_actions">
                    <?php 
            if ($message_error && !empty($comment['shub_outbox_id'])) {
                ?>
                        <button data-post="<?php 
                echo esc_attr(json_encode(array('action' => "support_hub_resend_outbox_message", 'shub_outbox_id' => $comment['shub_outbox_id'])));
                ?>
" class="btn button shub_message_action_button"><?php 
                _e('Re-Send');
                ?>
</button>
                        <button data-post="<?php 
                echo esc_attr(json_encode(array('action' => "support_hub_delete_outbox_message", 'shub_outbox_id' => $comment['shub_outbox_id'])));
                ?>
" class="btn button shub_message_action_button"><?php 
                _e('Delete Message');
                ?>
</button>
                    <?php 
            }
            ?>
                </div>
            </div>
        <?php 
        }
        if ($allow_reply) {
            ?>
            <div class="shub_message shub_message_reply shub_message_reply_box">
                <?php 
            $reply_shub_user = $this->get_reply_user();
            ?>
                <div class="shub_message_picture">
                      <img src="<?php 
            echo $reply_shub_user->get_image();
            ?>
" />
                </div>
                <div class="shub_message_header">
                     <?php 
            echo $reply_shub_user->get_full_link();
            ?>
                </div>
                <div class="shub_message_body">
                    <textarea placeholder="Write a reply..."></textarea>

                    <div class="shub_message_buttons">
                        <a href="#" class="shub_request_extra btn btn-default btn-xs button"
                           data-modaltitle="<?php 
            _e('Request Extra Details');
            ?>
" data-action="request_extra_details"
                           data-network="<?php 
            echo $this->network;
            ?>
"
                           data-<?php 
            echo $this->network;
            ?>
-message-id="<?php 
            echo $message_id;
            ?>
"><?php 
            _e('Request Details');
            ?>
</a>
                        <!-- <a href="#" class="shub_template_button btn btn-default btn-xs button"
                           data-modaltitle="<?php 
            _e('Send Template Message');
            ?>
" data-action="send_template_message"
                           data-network="<?php 
            echo $this->network;
            ?>
"
                           data-<?php 
            echo $this->network;
            ?>
-message-id="<?php 
            echo $message_id;
            ?>
"><?php 
            _e('Template');
            ?>
</a> -->

                        <button data-post="<?php 
            echo esc_attr(json_encode(array('account-id' => $this->get('shub_account_id'), 'message-id' => $message_id, 'network' => $this->network, 'last_activity' => $this->get('last_active'))));
            ?>
" class="btn button shub_send_message_reply_button shub_hide_when_no_message shub_button_loading"><?php 
            _e('Send');
            ?>
</button>
                    </div
                </div>
                <div class="shub_message_actions shub_hide_when_no_message">
                    <div>
                        <label
                            for="message_reply_archive_<?php 
            echo $message_id;
            ?>
"><?php 
            _e('Archive After Reply', 'shub');
            ?>
</label>
                        <input id="message_reply_archive_<?php 
            echo $message_id;
            ?>
" type="checkbox" name="archive"
                               data-reply="yes" value="1" checked>
                    </div>
                    <div>
                        <label
                            for="message_reply_private_<?php 
            echo $message_id;
            ?>
"><?php 
            _e('Mark As Private', 'shub');
            ?>
</label>
                        <input id="message_reply_private_<?php 
            echo $message_id;
            ?>
" type="checkbox" name="private"
                               data-reply="yes" value="1">
                    </div>
                    <div>
                        <label
                            for="message_reply_notify_email_<?php 
            echo $message_id;
            ?>
"><?php 
            _e('Notify Via Email', 'shub');
            ?>
</label>
                        <input id="message_reply_notify_email_<?php 
            echo $message_id;
            ?>
" type="checkbox" name="notify_email"
                               data-reply="yes" value="1">
                    </div>
                    <div>
                        <label
                            for="message_reply_debug_<?php 
            echo $message_id;
            ?>
"><?php 
            _e('Enable Debug Mode', 'shub');
            ?>
</label>
                        <input id="message_reply_debug_<?php 
            echo $message_id;
            ?>
" type="checkbox" name="debug"
                               data-reply="yes" value="1">
                    </div>
                    <?php 
            $this->reply_actions();
            ?>
                </div>
            </div>
            <?php 
        }
    }
    public function admin_ajax()
    {
        if (check_ajax_referer('support-hub-nonce', 'wp_nonce')) {
            // todo: don't overwrite default superglobals, run stripslashes every time before we use the content, because another plugin might be stripslashing already
            $_POST = stripslashes_deep($_POST);
            $_GET = stripslashes_deep($_GET);
            $_REQUEST = stripslashes_deep($_REQUEST);
            $action = isset($_REQUEST['action']) ? str_replace('support_hub_', '', $_REQUEST['action']) : false;
            switch ($action) {
                case 'modal':
                    // open a modal popup with the message in it (similar to pages/message.php)
                    if (isset($_REQUEST['network']) && isset($_REQUEST['message_id']) && (int) $_REQUEST['message_id'] > 0) {
                        $network = isset($_GET['network']) ? $_GET['network'] : false;
                        $message_id = isset($_GET['message_id']) ? (int) $_GET['message_id'] : false;
                        $message_comment_id = isset($_GET['message_comment_id']) ? (int) $_GET['message_comment_id'] : false;
                        if ($network && isset($this->message_managers[$network]) && $message_id > 0) {
                            $shub_extension_message = $this->message_managers[$network]->get_message(false, false, $message_id);
                            if ($shub_extension_message->get('shub_message_id') == $message_id) {
                                extract(array("shub_account_id" => $shub_extension_message->get('account')->get('shub_account_id'), "shub_message_id" => $message_id, "shub_message_comment_id" => $message_comment_id));
                                include trailingslashit(SupportHub::getInstance()->dir) . 'extensions/' . $network . '/' . $network . '_message.php';
                            } else {
                                echo 'Failed to load message from database';
                            }
                        } else {
                            echo 'Failed network message ID';
                        }
                    } else {
                        echo 'Failed network params';
                    }
                    break;
                case 'next-continuous-message':
                    if (!empty($_SESSION['_shub_search_rules'])) {
                        //$_SESSION['_shub_search_rules'] = array($this_search, $order, $message_ids);
                        $this_search = $_SESSION['_shub_search_rules'][0];
                        $message_ids = $_SESSION['_shub_search_rules'][2];
                        $this_search['not_in'] = $message_ids;
                        SupportHub::getInstance()->load_all_messages($this_search, $_SESSION['_shub_search_rules'][1], 5);
                        $all_messages = SupportHub::getInstance()->all_messages;
                        foreach ($all_messages as $all_message) {
                            $message_ids[] = $all_message['shub_message_id'];
                        }
                        // this is used in class-support-hub.php to load the next batch of messages.
                        $_SESSION['_shub_search_rules'][2] = $message_ids;
                        $myListTable = new SupportHubMessageList(array('screen' => 'shub_inbox'));
                        $myListTable->set_layout_type('continuous');
                        $myListTable->set_data($all_messages);
                        $myListTable->prepare_items();
                        if ($myListTable->has_items()) {
                            $myListTable->display_rows();
                        } else {
                            echo '<div class="no-items" style="text-align:center">';
                            $myListTable->no_items();
                            echo '</div>';
                        }
                    }
                    break;
                case 'set-answered':
                    if (isset($_REQUEST['network']) && isset($this->message_managers[$_REQUEST['network']]) && !empty($_REQUEST['shub_message_id'])) {
                        $shub_extension_message = $this->message_managers[$_REQUEST['network']]->get_message(false, false, $_REQUEST['shub_message_id']);
                        if ($shub_extension_message->get('shub_message_id') == $_REQUEST['shub_message_id']) {
                            if (!headers_sent()) {
                                header('Content-type: text/javascript');
                            }
                            // we hide the element and provide an 'undo' placeholder in its place.
                            // if it's a row we just hide it, if it's a div we slide it up nicely.
                            if (isset($_REQUEST['last_active']) && $_REQUEST['last_active'] != $shub_extension_message->get('last_active')) {
                                // a new message was received without updating the page.
                                // todo: ajax the shit out of live message updates instead of waiting for action.
                                // todo: do this check on bulk actions as well.
                                ?>
                                alert('There is an update to this message. Please refresh the page to see.');
                                <?php 
                            } else {
                                $shub_extension_message->update('shub_status', _shub_MESSAGE_STATUS_ANSWERED);
                                ?>
                                var element = jQuery('.shub_extension_message[data-message-id=<?php 
                                echo (int) $_REQUEST['shub_message_id'];
                                ?>
]');
                                var element_action = element.prev('.shub_extension_message_action').first();
                                element_action.find('.action_content').html('Message Archived. <a href="#" class="shub_message_action" data-action="set-unanswered" data-post="<?php 
                                echo esc_attr(json_encode(array('network' => $_REQUEST['network'], 'shub_message_id' => (int) $_REQUEST['shub_message_id'])));
                                ?>
">Undo</a>');
                                if(element.is('div')){
                                element.slideUp(function(){element.remove();});
                                element_action.slideDown();
                                }else{
                                element.remove();
                                element_action.show();
                                }
                                element_action.data('undo-type','answered');
                                <?php 
                            }
                        }
                    }
                    break;
                case 'set-unanswered':
                    if (isset($_REQUEST['network']) && isset($this->message_managers[$_REQUEST['network']]) && !empty($_REQUEST['shub_message_id'])) {
                        $shub_extension_message = $this->message_managers[$_REQUEST['network']]->get_message(false, false, $_REQUEST['shub_message_id']);
                        if ($shub_extension_message->get('shub_message_id') == $_REQUEST['shub_message_id']) {
                            $shub_extension_message->update('shub_status', _shub_MESSAGE_STATUS_UNANSWERED);
                            if (!headers_sent()) {
                                header('Content-type: text/javascript');
                            }
                            // we hide the element and provide an 'undo' placeholder in its place.
                            // if it's a row we just hide it, if it's a div we slide it up nicely.
                            ?>
                            var element = jQuery('.shub_extension_message[data-message-id=<?php 
                            echo (int) $_REQUEST['shub_message_id'];
                            ?>
]');
                            var element_action = element.prev('.shub_extension_message_action').first();
                            element_action.find('.action_content').html('Message Moved to Inbox. <a href="#" class="shub_message_action" data-action="set-answered" data-post="<?php 
                            echo esc_attr(json_encode(array('network' => $_REQUEST['network'], 'shub_message_id' => (int) $_REQUEST['shub_message_id'])));
                            ?>
">Undo</a>');
                            if(element.is('div')){
                            element.slideUp(function(){element.remove();});
                            element_action.slideDown();
                            }else{
                            element.remove();
                            element_action.show();
                            }
                            element_action.data('undo-type','unanswered');
                            <?php 
                        }
                    }
                    break;
                case 'send-message-reply':
                    /*
                    sample post data:
                        action:support_hub_send-message-reply
                        wp_nonce:dfd377374d
                        message:test
                        account-id:1
                        message-id:246
                        network:envato
                        debug:1
                    */
                    if (isset($_REQUEST['network']) && isset($this->message_managers[$_REQUEST['network']]) && !empty($_REQUEST['account-id']) && !empty($_REQUEST['message-id'])) {
                        $shub_extension_message = $this->message_managers[$_REQUEST['network']]->get_message(false, false, $_REQUEST['message-id']);
                        if ($shub_extension_message->get('shub_message_id') == $_REQUEST['message-id']) {
                            $return = array('message' => '', 'error' => false, 'shub_outbox_id' => false);
                            if (isset($_REQUEST['last_active']) && $_REQUEST['last_active'] != $shub_extension_message->get('last_active')) {
                                $return['error'] = true;
                                $return['message'] = 'There is an update to this message. Please refresh the page to see.';
                            } else {
                                $message = isset($_POST['message']) && $_POST['message'] ? $_POST['message'] : '';
                                $account_id = $_REQUEST['account-id'];
                                $debug = isset($_POST['debug']) && (int) $_POST['debug'] > 0 ? true : false;
                                if ($message) {
                                    // we have a message and a message manager.
                                    // time to queue this baby into the outbox and send it swimming
                                    // what the hell did I just write? I need sleep!
                                    $outbox = new SupportHubOutbox();
                                    $outbox->create_new();
                                    if ($outbox->get('shub_outbox_id')) {
                                        if ($debug) {
                                            ob_start();
                                        }
                                        $extra_data = array();
                                        foreach ($_POST as $key => $val) {
                                            if (strpos($key, 'extra-') !== false) {
                                                $extra_data[substr($key, 6)] = $val;
                                            }
                                        }
                                        $outbox->update_outbox_data(array('debug' => $debug, 'extra' => $extra_data));
                                        $message_comment_id = $shub_extension_message->queue_reply($account_id, $message, $debug, $extra_data, $outbox->get('shub_outbox_id'));
                                        if (!$message_comment_id) {
                                            $return['message'] .= 'Failed to queue comment reply in database.';
                                            $return['error'] = true;
                                        } else {
                                            // successfully queued. do we archive?
                                            if (!empty($_POST['archive'])) {
                                                $shub_extension_message->update('shub_status', _shub_MESSAGE_STATUS_ANSWERED);
                                            }
                                        }
                                        $outbox->update(array('shub_extension' => $_REQUEST['network'], 'shub_account_id' => $account_id, 'shub_message_id' => $_REQUEST['message-id'], 'shub_message_comment_id' => $message_comment_id));
                                        if ($debug) {
                                            // send the message straight away and show any debug output
                                            echo $outbox->send_queued(true);
                                            $return['message'] .= ob_get_clean();
                                            // dont send an shub_outbox_id in debug mode
                                            // this will keep the 'message' window open and not shrink it down so we can better display debug messages.
                                        } else {
                                            //set_message( _l( 'message sent and conversation archived.' ) );
                                            $return['shub_outbox_id'] = $outbox->get('shub_outbox_id');
                                        }
                                    }
                                }
                            }
                            if (!headers_sent()) {
                                header('Content-type: text/javascript');
                            }
                            echo json_encode($return);
                            exit;
                        }
                    }
                    break;
                case 'queue-watch':
                    // find out how many pending messages exist and display that result back to the browser.
                    // along with outbox_ids so we can update the UI when it is sent
                    $this->send_outbox_messages();
                    $pending = SupportHubOutbox::get_pending();
                    $failed = SupportHubOutbox::get_failed();
                    $return = array();
                    if (!headers_sent()) {
                        header('Content-type: text/javascript');
                    }
                    $return['outbox_ids'] = array();
                    foreach ($pending as $message) {
                        $return['outbox_ids'][] = array('shub_outbox_id' => $message['shub_outbox_id'], 'shub_status' => $message['shub_status']);
                    }
                    foreach ($failed as $message) {
                        $return['outbox_ids'][] = array('shub_outbox_id' => $message['shub_outbox_id'], 'shub_status' => $message['shub_status']);
                    }
                    echo json_encode($return);
                    break;
                case 'resend_outbox_message':
                    $shub_outbox_id = !empty($_REQUEST['shub_outbox_id']) ? (int) $_REQUEST['shub_outbox_id'] : false;
                    if ($shub_outbox_id) {
                        if (!headers_sent()) {
                            header('Content-type: text/javascript');
                        }
                        $pending = new SupportHubOutbox($shub_outbox_id);
                        if ($pending->get('shub_outbox_id') == $shub_outbox_id) {
                            ob_start();
                            echo $pending->send_queued(true);
                            $return = array('message' => 'Message Resent. Please refresh the page. ' . ob_get_clean());
                            echo json_encode($return);
                        }
                        exit;
                    }
                    break;
                case 'delete_outbox_message':
                    $shub_outbox_id = !empty($_REQUEST['shub_outbox_id']) ? (int) $_REQUEST['shub_outbox_id'] : false;
                    if ($shub_outbox_id) {
                        if (!headers_sent()) {
                            header('Content-type: text/javascript');
                        }
                        // remove the comment from the database.
                        $pending = new SupportHubOutbox($shub_outbox_id);
                        if ($pending->get('shub_outbox_id') == $shub_outbox_id) {
                            shub_delete_from_db('shub_message_comment', 'shub_message_comment_id', $pending->get('shub_message_comment_id'));
                            $pending->delete();
                            $return = array('message' => 'Deleted Successfully. Please re-load the page.');
                            echo json_encode($return);
                        }
                        exit;
                    }
                    break;
                case 'request_extra_details':
                    if (!empty($_REQUEST['network']) && isset($this->message_managers[$_REQUEST['network']])) {
                        if (!headers_sent()) {
                            header('Content-type: text/javascript');
                        }
                        $debug = isset($_POST['debug']) && $_POST['debug'] ? $_POST['debug'] : false;
                        $response = array();
                        $extra_ids = isset($_REQUEST['extra_ids']) && is_array($_REQUEST['extra_ids']) ? $_REQUEST['extra_ids'] : array();
                        $account_id = isset($_REQUEST['accountId']) ? (int) $_REQUEST['accountId'] : (isset($_REQUEST['account-id']) ? (int) $_REQUEST['account-id'] : false);
                        $message_id = isset($_REQUEST['messageId']) ? (int) $_REQUEST['messageId'] : (isset($_REQUEST['message-id']) ? (int) $_REQUEST['message-id'] : false);
                        if (empty($extra_ids)) {
                            $response['message'] = 'Please request at least one Extra Detail';
                        } else {
                            $shub_message = new shub_message(false, false, $message_id);
                            if ($message_id && $shub_message->get('shub_message_id') == $message_id) {
                                // build the message up
                                $message = SupportHubExtra::build_message(array('network' => $_REQUEST['network'], 'account_id' => $account_id, 'message_id' => $message_id, 'extra_ids' => $extra_ids));
                                $response['message'] = $message;
                                //							if($debug)ob_start();
                                //							$shub_message->send_reply( $shub_message->get('envato_id'), $message, $debug );
                                //							if($debug){
                                //								$response['message'] = ob_get_clean();
                                //							}else {
                                //								$response['redirect'] = 'admin.php?page=support_hub_main';
                                //							}
                            }
                        }
                        echo json_encode($response);
                        exit;
                    }
                    break;
            }
            // pass off the ajax handling to our media managers:
            foreach ($this->message_managers as $name => $message_manager) {
                if ($message_manager->handle_ajax($action, $this)) {
                    // success!
                }
            }
        }
        exit;
    }