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 static function handle_request_extra()
 {
     if (isset($_REQUEST[_SUPPORT_HUB_LINK_REQUEST_EXTRA]) && !empty($_REQUEST[_SUPPORT_HUB_LINK_REQUEST_EXTRA])) {
         // 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);
         // verify this extra link is valid.
         $bits = explode(':', $_REQUEST[_SUPPORT_HUB_LINK_REQUEST_EXTRA]);
         if (count($bits) == 5) {
             $network = $bits[0];
             $account_id = (int) $bits[1];
             $message_id = (int) $bits[2];
             $extra_ids = explode(',', $bits[3]);
             $legit_hash = self::build_message_hash($network, $account_id, $message_id, $extra_ids);
             if ($legit_hash == $_REQUEST[_SUPPORT_HUB_LINK_REQUEST_EXTRA]) {
                 // woo we have a legit hash. continue.
                 if (!session_id()) {
                     if (headers_sent()) {
                         echo "Warning: session headers already sent, unable to proceed, please report this error.";
                         exit;
                     }
                     session_start();
                 }
                 // user has landed on this page from a tweet or item comment
                 // we have to verify their identify first in order to provide the form and then do futher stuff.
                 // pass this off in an action to grab any login/verification from the various networks.
                 $login_status = false;
                 $SupportHub = SupportHub::getInstance();
                 ob_start();
                 include $SupportHub->get_template('shub_external_header.php');
                 if (isset($SupportHub->message_managers[$network])) {
                     // todo: offer them another way to login to the system.
                     // e.g. someone might want to login using Facebook to access their Envato feed
                     // if the email matches between accounts this should be possible
                     // if no match is found then we can just show a not found error.
                     // but for now we only allow login from the network we started with.
                     // ooooooooooo maybe we can have the generic extra_process_login method show a list of available login methods? and the individual networks can override this if needed
                     // hmm.. ideas ideas..
                     $login_status = $SupportHub->message_managers[$network]->extra_process_login($network, $account_id, $message_id, $extra_ids);
                 } else {
                     die('Invalid message manager');
                 }
                 if ($login_status) {
                     // the user is logged in and their identity has been verified by one of the 3rd party plugins.
                     // we can now safely accept their additoinal information and append it to this ticket.
                     $extras = self::get_all_extras();
                     $extra_previous_data = isset($_POST['extra']) && is_array($_POST['extra']) ? $_POST['extra'] : array();
                     $extra_previous_data_errors = array();
                     $extra_previous_data_validated = array();
                     $extra_previous_notes = isset($_POST['extra_notes']) ? $_POST['extra_notes'] : '';
                     // check if the user is submitting extra information:
                     $has_data_error = false;
                     $missing_required_information = false;
                     // todo, work out which fields are required, maybe mark them in the original request along with $extra_ids ?
                     foreach ($extras as $extra_id => $extra) {
                         if (!in_array($extra->get('shub_extra_id'), $extra_ids)) {
                             unset($extras[$extra_id]);
                         } else {
                             // this extra is to be shown on the page. load in any existing data for this extra item.
                             // todo: hmm nah, dont re-show that information here in the form, the form is only for adding new information.
                             // only show information that was an error and needs to be corrected again before submission/save.
                             //$extra_data = $extra->get_data($network, $account_id, $message_id);
                             if (isset($extra_previous_data[$extra_id]) && is_string($extra_previous_data[$extra_id])) {
                                 $status = array('success' => true);
                                 // validate the data, we have to filter it because Twitter might want to validate a Purchase code against the envato plugin.
                                 $status = apply_filters('shub_extra_validate_data', $status, $extra, $extra_previous_data[$extra_id], $network, $account_id, $message_id);
                                 if ($status && $status['success']) {
                                     // all good ready to save!
                                     $extra_previous_data_validated[$extra_id] = !empty($status['data']) ? $status : $extra_previous_data[$extra_id];
                                     // doing it this way so we can save additional details such as license code verification
                                 } else {
                                     $has_data_error = true;
                                     $extra_previous_data_errors[$extra_id] = isset($status['message']) ? $status['message'] : 'Error';
                                 }
                             } else {
                                 // todo: figureo ut if this field ismissing?
                                 if (!empty($extra_previous_notes) || !empty($extra_previous_data)) {
                                     $missing_required_information = true;
                                 }
                             }
                         }
                     }
                     if (!$has_data_error && (!empty($extra_previous_notes) || !empty($extra_previous_data_validated))) {
                         // user has input something
                         // build up the private message to store in the system
                         $message = '';
                         foreach ($extras as $extra_id => $extra) {
                             if (isset($extra_previous_data_validated[$extra_id])) {
                                 $message .= $extra->add_message_segment($extra_previous_data_validated[$extra_id]);
                             }
                         }
                         if (!empty($extra_previous_notes)) {
                             $message .= '<p>' . shub_forum_text($extra_previous_notes) . '</p>';
                             $extra_previous_notes = false;
                         }
                         // pass it through to the message managers to store this information!
                         // (e.g. envato module will validate the 'purchase_code' and return a possible error)
                         foreach ($extras as $extra_id => $extra) {
                             if (isset($extra_previous_data_validated[$extra_id])) {
                                 $status = $SupportHub->message_managers[$network]->extra_save_data($extra, $extra_previous_data_validated[$extra_id], $network, $account_id, $message_id);
                                 unset($extra_previous_data[$extra_id]);
                             }
                         }
                         // all done! save our message in the db
                         $SupportHub->message_managers[$network]->extra_send_message($message, $network, $account_id, $message_id);
                         // redirect browser to a done page.
                         header("Location: " . $_SERVER['REQUEST_URI'] . '&done');
                         exit;
                     }
                     include $SupportHub->get_template('shub_extra_request_form.php');
                 } else {
                     // we display the login form during request_extra_login()
                 }
                 include $SupportHub->get_template('shub_external_footer.php');
                 echo ob_get_clean();
                 exit;
             }
         }
     }
 }