Beispiel #1
0
function shd_save_reply()
{
    global $txt, $modSettings, $sourcedir, $context, $scripturl;
    global $user_info, $options, $smcFunc, $memberContext;
    $_REQUEST['msg'] = !empty($_REQUEST['msg']) ? (int) $_REQUEST['msg'] : 0;
    // We're replying so there must be an existing ticket or sumthin's WRONG.
    $ticketinfo = shd_load_ticket();
    $reply = array();
    $new_reply = $_REQUEST['msg'] == 0;
    // So, at this point, we can see it, but no guarantee we can reply to it.
    // Can we reply to any? If so, just go right along. If not, we need to do more work.
    if ($new_reply) {
        if (!shd_allowed_to('shd_reply_ticket_any', $ticketinfo['dept'])) {
            if (shd_allowed_to('shd_reply_ticket_own', $ticketinfo['dept'])) {
                if (!$ticketinfo['is_own']) {
                    fatal_lang_error('shd_cannot_reply_any_but_own', false);
                }
            } else {
                fatal_lang_error('shd_cannot_reply_any', false);
            }
            // can't do nuthin'
        }
    } else {
        $query = shd_db_query('', '
			SELECT id_msg, id_member, body, modified_time, modified_member, modified_name
			FROM {db_prefix}helpdesk_ticket_replies
			WHERE id_msg = {int:msg}
				AND id_ticket = {int:ticket}', array('msg' => $_REQUEST['msg'], 'ticket' => $context['ticket_id']));
        if ($smcFunc['db_num_rows']($query) == 0) {
            $smcFunc['db_free_result']($query);
            fatal_lang_error('shd_no_ticket', false);
        }
        $reply = $smcFunc['db_fetch_assoc']($query);
        if (!shd_allowed_to('shd_edit_reply_any', $ticketinfo['dept'])) {
            if (shd_allowed_to('shd_edit_reply_own', $ticketinfo['dept'])) {
                if ($reply['id_member'] != $user_info['id']) {
                    fatal_lang_error('shd_cannot_edit_reply_any_but_own', false);
                }
            } else {
                fatal_lang_error('shd_cannot_edit_reply_any', false);
            }
        }
    }
    $context['ticket_form'] = array('dept' => $ticketinfo['dept'], 'form_title' => $new_reply ? $txt['shd_reply_ticket'] : $txt['shd_ticket_edit_reply'], 'form_action' => $scripturl . '?action=helpdesk;sa=savereply', 'first_msg' => $new_reply ? 0 : $ticketinfo['id_first_msg'], 'message' => shd_format_text($ticketinfo['body'], $ticketinfo['smileys_enabled'], $new_reply ? '' : 'shd_reply_' . $ticketinfo['id_first_msg']), 'subject' => $ticketinfo['subject'], 'ticket' => $context['ticket_id'], 'msg' => $_REQUEST['msg'], 'display_id' => str_pad($context['ticket_id'], $modSettings['shd_zerofill'], '0', STR_PAD_LEFT), 'urgency' => array('setting' => $ticketinfo['urgency']), 'private' => array('setting' => $ticketinfo['private'], 'can_change' => false, 'options' => array(0 => 'shd_ticket_notprivate', 1 => 'shd_ticket_private')), 'status' => $ticketinfo['status'], 'member' => array('name' => $ticketinfo['starter_name'], 'id' => $ticketinfo['starter_id'], 'link' => shd_profile_link($ticketinfo['starter_name'], $ticketinfo['starter_id'])), 'assigned' => array('id' => $ticketinfo['assigned_id'], 'name' => !empty($ticketinfo['assigned_id']) ? $ticketinfo['assigned_name'] : $txt['shd_unassigned'], 'link' => !empty($ticketinfo['assigned_id']) ? shd_profile_link($ticketinfo['assigned_name'], $ticketinfo['assigned_id']) : '<span class="error">' . $txt['shd_unassigned'] . '</span>'), 'num_replies' => $ticketinfo['num_replies'], 'do_attach' => shd_allowed_to('shd_post_attachment', $ticketinfo['dept']), 'reply' => $_POST['shd_message'], 'return_to_ticket' => isset($_REQUEST['goback']), 'disable_smileys' => !empty($_REQUEST['no_smileys']));
    $context['can_solve'] = shd_allowed_to('shd_resolve_ticket_any', $ticketinfo['dept']) || shd_allowed_to('shd_resolve_ticket_own', $ticketinfo['dept']) && $ticketinfo['starter_id'] == $user_info['id'];
    $context['can_silent_update'] = $new_reply && shd_allowed_to('shd_silent_update', $ticketinfo['dept']);
    $context['log_action'] = $new_reply ? 'newreply' : 'editreply';
    $context['log_params']['subject'] = $context['ticket_form']['subject'];
    shd_posting_additional_options();
    shd_load_canned_replies();
    $context['can_ping'] = $new_reply && shd_allowed_to('shd_singleton_email', $context['ticket_form']['dept']);
    // Ticket privacy
    if (empty($modSettings['shd_privacy_display']) || $modSettings['shd_privacy_display'] == 'smart') {
        $context['display_private'] = shd_allowed_to('shd_view_ticket_private_any', $ticketinfo['dept']) || shd_allowed_to('shd_alter_privacy_own', $ticketinfo['dept']) || shd_allowed_to('shd_alter_privacy_any', $ticketinfo['dept']) || $context['ticket_form']['private']['setting'];
    } else {
        $context['display_private'] = true;
    }
    loadMemberData($ticketinfo['starter_id']);
    if (loadMemberContext($ticketinfo['starter_id'])) {
        $context['ticket_form']['member']['avatar'] = $memberContext[$ticketinfo['starter_id']]['avatar'];
    }
    if (!empty($ticketinfo['modified_time'])) {
        $context['ticket_form'] += array('modified' => array('name' => $ticketinfo['modified_name'], 'id' => $ticketinfo['modified_id'], 'time' => timeformat($ticketinfo['modified_time']), 'link' => shd_profile_link($ticketinfo['modified_name'], $ticketinfo['modified_id'])));
    }
    if (isset($_REQUEST['preview'])) {
        $context['ticket_form']['preview'] = array('title' => $txt['shd_previewing_reply'] . ': ' . (empty($context['ticket_form']['subject']) ? '<em>' . $txt['no_subject'] . '</em>' : $context['ticket_form']['subject']), 'body' => shd_format_text($_POST['shd_message']));
    }
    shd_load_attachments();
    shd_get_urgency_options($ticketinfo['is_own'], $ticketinfo['dept']);
    $context['ticket_form']['urgency']['can_change'] = false;
    if (!empty($ticketinfo['num_replies'])) {
        shd_setup_replies($ticketinfo['id_first_msg']);
    }
    // A few basic checks
    if ($context['ticket_form']['status'] == TICKET_STATUS_CLOSED) {
        fatal_lang_error('shd_cannot_edit_closed', false);
    } elseif ($context['ticket_form']['status'] == TICKET_STATUS_DELETED) {
        fatal_lang_error('shd_cannon_edit_deleted', false);
    }
    // Have there been any new replies that we missed?
    if (empty($options['no_new_reply_warning']) && isset($_REQUEST['num_replies'])) {
        $_REQUEST['num_replies'] = (int) $_REQUEST['num_replies'];
        $newReplies = $context['ticket_form']['num_replies'] > $_REQUEST['num_replies'] ? $context['ticket_form']['num_replies'] - $_REQUEST['num_replies'] : 0;
        if (!empty($newReplies)) {
            loadLanguage('Post');
            if ($newReplies > 1) {
                $txt['error_new_replies'] = sprintf($txt['error_new_replies'], $newReplies);
            }
            $context['shd_errors'][] = $newReplies == 1 ? 'new_reply' : 'new_replies';
        }
    }
    // OK, does the user want to close this ticket? Are there any problems with that?
    if (!empty($context['can_solve']) && !empty($_POST['resolve_ticket'])) {
        $string = shd_check_dependencies();
        if (!empty($string)) {
            $context['shd_errors'][] = $string;
        }
    }
    // Custom fields?
    shd_load_custom_fields(false, $context['ticket_form']['msg'], $context['ticket_form']['dept']);
    list($missing_fields, $invalid_fields) = shd_validate_custom_fields($context['ticket_form']['msg'], $context['ticket_form']['dept']);
    $context['can_override_fields'] = shd_allowed_to('shd_override_cf', $context['ticket_form']['dept']);
    $context['overriding_fields'] = $context['can_override_fields'] && isset($_POST['override_cf']);
    // Did any custom fields fail validation?
    if (!empty($invalid_fields)) {
        $context['shd_errors'][] = 'invalid_fields';
        $txt['error_invalid_fields'] = sprintf($txt['error_invalid_fields'], implode(', ', $invalid_fields));
    }
    // Any flat-out missing?
    if (!empty($missing_fields) && !$context['overriding_fields']) {
        $context['shd_errors'][] = 'missing_fields';
        $txt['error_missing_fields'] = sprintf($txt['error_missing_fields'], implode(', ', $missing_fields));
    }
    if ($context['can_override_fields'] && !empty($missing_fields)) {
        $context['ticket_form']['additional_opts']['override_cf'] = array('show' => true, 'checked' => false, 'text' => $txt['shd_override_cf']);
    }
    if (!empty($context['shd_errors']) || !empty($context['ticket_form']['preview'])) {
        checkSubmitOnce('free');
        // Anything else for redisplaying the form
        $context['page_title'] = $txt['shd_helpdesk'];
        $context['sub_template'] = 'reply_post';
        // Oh, yeah, we need to look at the existing notifications pinging stuff, if appropriate, and reset that.
        if ($context['can_ping'] && !empty($_POST['notify']) && is_array($_POST['notify'])) {
            $items = array();
            foreach ($_POST['notify'] as $item) {
                if (!empty($item) && is_numeric($item)) {
                    $items[] = $item;
                }
            }
            if (!empty($items)) {
                $context['notification_ping_list'] = implode(',', $items);
            }
        }
        shd_check_attachments();
        // Set up the fancy editor
        shd_postbox('shd_message', un_preparsecode($_POST['shd_message']), array('post_button' => $new_reply ? $txt['shd_reply_ticket'] : $txt['shd_ticket_edit_reply']));
        // Build the link tree and navigation
        $context['linktree'][] = array('name' => $txt['shd_reply_ticket']);
        checkSubmitOnce('register');
    } else {
        // It all worked, w00t, so let's get ready to rumble
        $attachIDs = shd_handle_attachments();
        if ($new_reply) {
            // So... what is the new status?
            $new_status = shd_determine_status('reply', $ticketinfo['starter_id'], $user_info['id'], -1, $context['ticket_form']['dept']);
            // We explicitly don't care about how many replies - but it must be non-zero. Default in function spec is -1.
            // Now to add the ticket details
            $posterOptions = array('id' => $user_info['id']);
            $msgOptions = array('body' => $_POST['shd_message'], 'id' => $context['ticket_form']['msg'], 'smileys_enabled' => empty($context['ticket_form']['disable_smileys']), 'attachments' => $attachIDs, 'custom_fields' => !empty($context['ticket_form']['custom_fields'][$context['ticket_form']['msg']]) ? $context['ticket_form']['custom_fields'][$context['ticket_form']['msg']] : array());
            $ticketOptions = array('id' => $context['ticket_form']['ticket'], 'mark_as_read' => true, 'status' => $new_status);
            if (!empty($context['can_solve']) && !empty($_POST['resolve_ticket'])) {
                $ticketOptions['status'] = TICKET_STATUS_CLOSED;
                shd_log_action('resolve', array('ticket' => $context['ticket_id'], 'subject' => $ticketinfo['subject']));
            }
            shd_create_ticket_post($msgOptions, $ticketOptions, $posterOptions);
            // Handle notifications
            require_once $sourcedir . '/sd_source/SimpleDesk-Notifications.php';
            shd_notifications_notify_newreply($msgOptions, $ticketOptions, $posterOptions);
        } else {
            // Only add what has actually changed
            // Now to add the ticket details
            $posterOptions = array();
            $msgOptions = array('id' => $context['ticket_form']['msg'], 'attachments' => $attachIDs, 'custom_fields' => !empty($context['ticket_form']['custom_fields'][$context['ticket_form']['msg']]) ? $context['ticket_form']['custom_fields'][$context['ticket_form']['msg']] : array());
            $ticketOptions = array('id' => $context['ticket_form']['ticket']);
            if ((bool) $ticketinfo['smileys_enabled'] == $context['ticket_form']['disable_smileys']) {
                // since one is enabled, one is 'now disable'...
                $msgOptions['smileys_enabled'] = !$context['ticket_form']['disable_smileys'];
            }
            if ($reply['body'] != $context['ticket_form']['reply']) {
                $msgOptions['body'] = $context['ticket_form']['reply'];
            }
            if (isset($msgOptions['body'])) {
                $msgOptions['modified'] = array('id' => $user_info['id'], 'name' => $user_info['name'], 'time' => time());
            }
            if (!empty($context['can_solve']) && !empty($_POST['resolve_ticket'])) {
                $ticketOptions['status'] = TICKET_STATUS_CLOSED;
                shd_log_action('resolve', array('ticket' => $context['ticket_id'], 'subject' => $ticketinfo['subject']));
            }
            // DOOOOOOOO EEEEEEEEEEET NAO!
            shd_modify_ticket_post($msgOptions, $ticketOptions, $posterOptions);
            // OK, did we get any custom fields back?
            foreach ($context['custom_fields_updated'] as $field) {
                $action = 'cf_' . ($field['scope'] == CFIELD_TICKET ? 'tkt' : 'rpl') . (empty($field['default']) ? 'change_' : 'chgdef_') . ($field['visible'][0] ? 'user' : '') . ($field['visible'][1] ? 'staff' : '') . 'admin';
                unset($field['default'], $field['scope'], $field['visible']);
                $field['subject'] = $ticketinfo['subject'];
                shd_log_action($action, $field);
            }
        }
        $context['ticket_form']['msg'] = $msgOptions['id'];
        shd_done_posting();
    }
}
/**
 *	Loads all the data and sets all the options for displaying a ticket.
 *
 *	This function does a lot of work in setting up a ticket to be displayed:
 *	<ul>
 *		<li>Invokes shd_load_ticket() to get the principle data</li>
 *		<li>Creates $context['ticket'] to hold the data block, some of which is derived from the return of shd_load_ticket():
 *			<ul>
 *				<li>id: regular numeric ticket id</li>
 *				<li>display_id: zero padded ticket id (e.g. 00001)</li>
 *				<li>subject: censored version of the subject</li>
 *				<li>first_msg: id of the opening post that forms the ticket body</li>
 *				<li>body: formatted (parsed for smileys and bbcode) version of the ticket post</li>
 *				<li>id_member: user id of the ticket's poster</li>
 *				<li>id_member_assigned: user id of the ticket's assigned user</li>
 *				<li>member: hash array of the ticket poster's details:
 *					<ul>
 *						<li>id: their user id</li>
 *						<li>name: the name stated in the ticket post for that use</li>
 *						<li>link: link to the profile of the user</li>
 *					</ul>
 *				</li>
 *				<li>assigned: hash array of the assignee of the ticket:
 *					<ul>
 *						<li>id: their user id</li>
 *						<li>name: name of the assignee, or 'Unassigned'</li>
 *						<li>link: a full HTML link to their profile, or 'Unassigned' in red text</li>
 *					</ul>
 *				</li>
 *				<li>assigned_self: boolean, whether the ticket is assigned to the current user or not</li>
 *				<li>ticket_opener: boolean, whether the current user is the user who opened this ticket</li>
 *				<li>urgency: hash array
 *					<ul>
 *						<li>level: numeric identifier of current ticket urgency</li>
 *						<li>label: the HTML label of the urgency, including being in red for "Very High" or above</li>
 *						<li>increase: Boolean, whether the current ticket urgency can be increased given the current ticket state and user permissions</li>
 *						<li>decrease: Boolean, whether the current ticket urgency can be increased given the current ticket state and user permissions</li>
 *					</ul>
 *				</li>
 *				<li>status: hash array
 *					<ul>
 *						<li>level: numeric, current status identifier</li>
 *						<li>label: string representing the current status</li>
 *					</ul>
 *				<li>num_replies: the number of replies to the ticket so far</li>
 *				<li>deleted_replies: how many deleted replies in this ticket</li>
 *				<li>poster_time: formatted string containing the time the ticket was opened</li>
 *				<li>privacy: hash array
 *					<ul>
 *						<li>label: current label to be used with the privacy item</li>
 *						<li>can_change: Boolean, whether the user's permission with this ticket allows us to edit the ticket privacy</li>
 *					</ul>
 *				</li>
 *				<li>closed: Boolean, represents whether this ticket is closed (used a lot with the navigation menu)</li>
 *				<li>deleted: Boolean, represents whether this ticket is deleted (used a lot with the navigation menu)</li>
 *				<li>ip_address: IP address logged at the time the ticket was opened; if moderate_forum_members permission is available, this will be a link to the track IP area</li>
 *				<li>modified: if the ticket has been modified, also get the modified details:
 *					<ul>
 *						<li>id: user id who edited the ticket (not always available)</li>
 *						<li>time: formatted string of the time the post was edited</li>
 *						<li>timestamp: raw timestamp of the time the post was edited</li>
 *						<li>name: user name of the editing user; if we have a definite user id, this should contain the current name, falling back to the previously stored name</li>
 *						<li>link: if we have a known, valid user id for the post's editor, this will contain a link to their profile, with the link text using their current display name; alternatively it will contain a regular string which is the username stored with the edit.</li>
 *					</ul>
 *				</li>
 *				<li>display_recycle: Either holds the $txt identifier of the message to apply as a warning, or false if displaying of recycling stuff in this ticket isn't appropriate (either for permissions or just because of no deleted replies, or we're just in regular ticket view)</li>
 *			</ul>
 *		</li>
 *		<li>define the page index with SMF's constructPageIndex</li>
 *		<li>query for all the ids of messages we might display, followed by querying for the message details themselves, pushing that query resource to $reply_request so we can use it in shd_view_replies() later</li>
 *		<li>load details of all the users applicable for posts in this page</li>
 *		<li>request all the visible attachments from {@link shd_display_load_attachments()}</li>
 *		<li>since we are viewing this ticket, mark it read</li>
 *		<li>set up the breadcrumb trail</li>
 *		<li>set up the ticket navigation menu</li>
 *		<li>call in the editor component from SimpleDesk-Post.php and friends, ready for Quick Reply</li>
 *		<li>invoke the different Javascript objects that are applicable on the page:
 *			<ul>
 *				<li>privacy changer</li>
 *				<li>urgency changer</li>
 *				<li>quick reply / quote / go advanced</li>
 *			</ul>
 *		</li>
 *	</ul>
 *
 *	@see shd_prepare_ticket_context()
 *	@since 1.0
*/
function shd_view_ticket()
{
    global $context, $txt, $scripturl, $settings, $reply_request, $smcFunc, $modSettings, $memberContext, $sourcedir, $user_info, $options;
    loadTemplate('sd_template/SimpleDesk-Display');
    $context['template_layers'][] = 'shd_display_nojs';
    $ticketinfo = shd_load_ticket();
    // How much are we sticking on each page?
    $context['messages_per_page'] = empty($modSettings['disableCustomPerPage']) && !empty($options['messages_per_page']) && !WIRELESS ? $options['messages_per_page'] : $modSettings['defaultMaxMessages'];
    censorText($ticketinfo['subject']);
    censorText($ticketinfo['body']);
    $context['user_list'] = array();
    // as we go along, build a list of users who are relevant
    $context['ticket'] = array('id' => $context['ticket_id'], 'dept' => $ticketinfo['dept'], 'dept_name' => $ticketinfo['dept_name'], 'display_id' => str_pad($context['ticket_id'], $modSettings['shd_zerofill'], '0', STR_PAD_LEFT), 'subject' => $ticketinfo['subject'], 'first_msg' => $ticketinfo['id_first_msg'], 'body' => shd_format_text($ticketinfo['body'], $ticketinfo['smileys_enabled'], 'shd_reply_' . $ticketinfo['id_first_msg']), 'id_member' => $ticketinfo['id_member'], 'id_member_assigned' => $ticketinfo['assigned_id'], 'member' => array('id' => $ticketinfo['starter_id'], 'name' => $ticketinfo['starter_name'], 'link' => shd_profile_link($ticketinfo['starter_name'], $ticketinfo['starter_id'])), 'assigned' => array('id' => $ticketinfo['assigned_id'], 'name' => $ticketinfo['assigned_id'] > 0 ? $ticketinfo['assigned_name'] : $txt['shd_unassigned'], 'link' => $ticketinfo['assigned_id'] > 0 ? shd_profile_link($ticketinfo['assigned_name'], $ticketinfo['assigned_id']) : '<span class="error">' . $txt['shd_unassigned'] . '</span>'), 'assigned_self' => $ticketinfo['assigned_id'] == $user_info['id'], 'ticket_opener' => $ticketinfo['starter_id'] == $user_info['id'], 'urgency' => array('level' => $ticketinfo['urgency'], 'label' => $ticketinfo['urgency'] > TICKET_URGENCY_HIGH ? '<span class="error">' . $txt['shd_urgency_' . $ticketinfo['urgency']] . '</span>' : $txt['shd_urgency_' . $ticketinfo['urgency']]), 'status' => array('level' => $ticketinfo['status'], 'label' => $txt['shd_status_' . $ticketinfo['status']]), 'num_replies' => $ticketinfo['num_replies'], 'deleted_replies' => $ticketinfo['deleted_replies'], 'poster_time' => timeformat($ticketinfo['poster_time']), 'privacy' => array('label' => $ticketinfo['private'] ? $txt['shd_ticket_private'] : $txt['shd_ticket_notprivate'], 'can_change' => shd_allowed_to('shd_alter_privacy_any', $ticketinfo['dept']) || shd_allowed_to('shd_alter_privacy_own', $ticketinfo['dept']) && $ticketinfo['id_member'] == $user_info['id']), 'closed' => $ticketinfo['closed'], 'deleted' => $ticketinfo['deleted']);
    // Fix the departmental link since we know we're inside a department now.
    if ($context['shd_multi_dept']) {
        $context['shd_department'] = $context['ticket']['dept'];
        $context['shd_dept_link'] = ';dept=' . $context['ticket']['dept'];
    }
    // IP address next
    $context['link_ip_address'] = allowedTo('moderate_forum');
    // for trackip access
    if (shd_allowed_to('shd_view_ip_any', $context['ticket']['dept']) || $context['ticket']['ticket_opener'] && shd_allowed_to('shd_view_ip_own', $context['ticket']['dept'])) {
        $context['ticket']['ip_address'] = $context['link_ip_address'] ? '<a href="' . $scripturl . '?action=trackip;searchip=' . $ticketinfo['starter_ip'] . '">' . $ticketinfo['starter_ip'] . '</a>' : $ticketinfo['starter_ip'];
    }
    // Stuff concerning whether the ticket is deleted or not
    // Display recycling stuff if: ticket is deleted (if we can see it, we can see the bin) OR ticket has deleted replies and we can see the bin and we requested to see them
    $context['ticket']['display_recycle_replies'] = true;
    if ($context['ticket']['deleted']) {
        $context['ticket']['display_recycle'] = $txt['shd_ticket_has_been_deleted'];
    } elseif ($context['ticket']['deleted_replies'] > 0) {
        if (shd_allowed_to('shd_access_recyclebin', $context['ticket']['dept'])) {
            $context['ticket']['display_recycle'] = $txt['shd_ticket_replies_deleted'];
            $ticketlink = $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'] . (isset($_REQUEST['recycle']) ? '' : ';recycle');
            $context['ticket']['display_recycle'] .= ' ' . sprintf(isset($_REQUEST['recycle']) ? $txt['shd_ticket_replies_deleted_view'] : $txt['shd_ticket_replies_deleted_link'], $ticketlink);
            $context['ticket']['display_recycle_replies'] = isset($_REQUEST['recycle']);
        } else {
            $context['ticket']['display_recycle_replies'] = false;
        }
    } else {
        $context['ticket']['display_recycle'] = false;
        $context['ticket']['display_recycle_replies'] = false;
    }
    // Ticket privacy
    $context['ticket']['privacy']['can_change'] = $context['ticket']['privacy']['can_change'] && (!$context['ticket']['closed'] && !$context['ticket']['deleted']);
    if (empty($modSettings['shd_privacy_display']) || $modSettings['shd_privacy_display'] == 'smart') {
        $context['display_private'] = shd_allowed_to('shd_view_ticket_private_any', $context['ticket']['dept']) || shd_allowed_to(array('shd_alter_privacy_own', 'shd_alter_privacy_any'), $context['ticket']['dept']) || $ticketinfo['private'];
    } else {
        $context['display_private'] = true;
    }
    if ($ticketinfo['modified_time'] > 0) {
        $context['ticket']['modified'] = array('id' => $ticketinfo['modified_id'], 'name' => $ticketinfo['modified_name'], 'link' => shd_profile_link($ticketinfo['modified_name'], $ticketinfo['modified_id']), 'timestamp' => $ticketinfo['modified_time'], 'time' => timeformat($ticketinfo['modified_time']));
    }
    $context['ticket']['urgency'] += shd_can_alter_urgency($ticketinfo['urgency'], $ticketinfo['starter_id'], $ticketinfo['closed'], $ticketinfo['deleted'], $context['ticket']['dept']);
    $context['total_visible_posts'] = empty($context['display_recycle']) ? $context['ticket']['num_replies'] : (int) $context['ticket']['num_replies'] + (int) $context['ticket']['deleted_replies'];
    // OK, before we go crazy, we might need to alter the ticket start. If we're in descending order (non default), we need to reverse it.
    if (!empty($context['shd_preferences']['display_order']) && $context['shd_preferences']['display_order'] == 'desc') {
        if (empty($context['ticket_start_natural'])) {
            $context['ticket_start_from'] = $context['total_visible_posts'] - (empty($context['ticket_start']) ? $context['total_visible_posts'] : $context['ticket_start']);
        } else {
            $context['ticket_start_from'] = $context['ticket_start'];
        }
        $context['ticket_sort'] = 'DESC';
    } else {
        $context['ticket_start_from'] = $context['ticket_start'];
        $context['ticket_sort'] = 'ASC';
    }
    $context['page_index'] = shd_no_expand_pageindex($scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'] . '.%1$d' . (isset($_REQUEST['recycle']) ? ';recycle' : '') . '#replies', $context['ticket_start_from'], $context['total_visible_posts'], $context['messages_per_page'], true);
    $context['get_replies'] = 'shd_prepare_ticket_context';
    $query = shd_db_query('', '
		SELECT id_msg, id_member, modified_member
		FROM {db_prefix}helpdesk_ticket_replies
		WHERE id_ticket = {int:ticket}
			AND id_msg > {int:first_msg}' . (!empty($context['ticket']['display_recycle_replies']) ? '' : '
			AND message_status = {int:msg_status}') . '
		ORDER BY id_msg {raw:sort}' . ($context['messages_per_page'] == -1 ? '' : '
		LIMIT ' . $context['ticket_start_from'] . ', ' . $context['messages_per_page']), array('ticket' => $context['ticket_id'], 'first_msg' => $ticketinfo['id_first_msg'], 'msg_status' => MSG_STATUS_NORMAL, 'sort' => $context['ticket_sort']));
    $context['ticket_messages'] = array();
    $posters = array();
    while ($row = $smcFunc['db_fetch_assoc']($query)) {
        if (!empty($row['id_member'])) {
            $posters[] = $row['id_member'];
        }
        if (!empty($row['modified_member'])) {
            $posters[] = $row['modified_member'];
        }
        $context['ticket_messages'][] = $row['id_msg'];
    }
    $smcFunc['db_free_result']($query);
    // We might want the OP's avatar, add 'em to the list -- just in case.
    $posters[] = $context['ticket']['id_member'];
    $posters = array_unique($posters);
    $context['shd_is_staff'] = array();
    // Get the poster data
    if (!empty($posters)) {
        loadMemberData($posters);
        // Are they current team members?
        $team = array_intersect($posters, shd_members_allowed_to('shd_staff', $context['ticket']['dept']));
        foreach ($team as $member) {
            $context['shd_is_staff'][$member] = true;
        }
    }
    if (!empty($context['ticket_messages'])) {
        $reply_request = shd_db_query('', '
			SELECT
				id_msg, poster_time, poster_ip, id_member, modified_time, modified_name, modified_member, body,
				smileys_enabled, poster_name, poster_email, message_status
			FROM {db_prefix}helpdesk_ticket_replies
			WHERE id_msg IN ({array_int:message_list})' . (!empty($context['ticket']['display_recycle']) ? '' : '
				AND message_status IN ({array_int:msg_normal})') . '
			ORDER BY id_msg {raw:sort}', array('message_list' => $context['ticket_messages'], 'msg_normal' => array(MSG_STATUS_NORMAL), 'sort' => $context['ticket_sort']));
    } else {
        $reply_request = false;
        $context['first_message'] = 0;
        $context['first_new_message'] = false;
    }
    // Load all the custom fields
    // First, get all the values that could apply to the current context. We'll deal with what's active/inactive and where it all goes shortly.
    $query = shd_db_query('', '
		SELECT cfv.id_post, cfv.id_field, cfv.value, cfv.post_type
		FROM {db_prefix}helpdesk_custom_fields_values AS cfv
		WHERE (cfv.id_post = {int:ticket} AND cfv.post_type = 1)' . (!empty($context['ticket_messages']) ? '
			OR (cfv.id_post IN ({array_int:msgs}) AND cfv.post_type = 2)' : ''), array('ticket' => $context['ticket_id'], 'msgs' => $context['ticket_messages']));
    $field_values = array();
    while ($row = $smcFunc['db_fetch_assoc']($query)) {
        $field_values[$row['post_type'] == CFIELD_TICKET ? 'ticket' : $row['id_post']][$row['id_field']] = $row;
    }
    $smcFunc['db_free_result']($query);
    // Set up the storage.
    $context['custom_fields_replies'] = array();
    $context['ticket']['custom_fields'] = array('details' => array(), 'information' => array(), 'prefix' => array(), 'prefixfilter' => array());
    $context['ticket_form']['custom_fields_context'] = 'reply';
    $context['ticket_form']['custom_fields'] = array();
    $query = shd_db_query('', '
		SELECT cf.id_field, cf.active, cf.field_order, cf.field_name, cf.field_desc, cf.field_loc, cf.icon,
			cf.field_type, cf.default_value, cf.bbc, cf.can_see, cf.can_edit, cf.field_length,
			cf.field_options, cf.display_empty, cfd.required, cf.placement
		FROM {db_prefix}helpdesk_custom_fields AS cf
			INNER JOIN {db_prefix}helpdesk_custom_fields_depts AS cfd ON (cf.id_field = cfd.id_field AND cfd.id_dept = {int:dept})
		WHERE cf.active = 1
		ORDER BY cf.field_order', array('dept' => $context['ticket']['dept']));
    // Loop through all fields and figure out where they should be.
    $is_staff = shd_allowed_to('shd_staff', $context['ticket']['dept']);
    $is_admin = shd_allowed_to('admin_helpdesk', $context['ticket']['dept']);
    // this includes forum admins
    $placements = array(CFIELD_PLACE_DETAILS => 'details', CFIELD_PLACE_INFO => 'information', CFIELD_PLACE_PREFIX => 'prefix', CFIELD_PLACE_PREFIXFILTER => 'prefixfilter');
    while ($row = $smcFunc['db_fetch_assoc']($query)) {
        list($user_see, $staff_see) = explode(',', $row['can_see']);
        list($user_edit, $staff_edit) = explode(',', $row['can_edit']);
        if ($is_admin) {
            $editable = true;
        } elseif ($is_staff) {
            if ($staff_see == 0) {
                continue;
            }
            $editable = $staff_edit == 1;
        } elseif ($user_see == 1) {
            $editable = $user_edit == 1;
        } else {
            continue;
        }
        // If this is going to be displayed for the individual ticket, we need to figure out where it should go.
        if ($row['field_loc'] & CFIELD_TICKET) {
            $pos = $placements[$row['placement']];
        }
        $field = array('id' => $row['id_field'], 'name' => $row['field_name'], 'desc' => parse_bbc($row['field_desc'], false), 'icon' => $row['icon'], 'type' => $row['field_type'], 'default_value' => $row['field_type'] == CFIELD_TYPE_LARGETEXT ? explode(',', $row['default_value']) : $row['default_value'], 'options' => !empty($row['field_options']) ? unserialize($row['field_options']) : array(), 'display_empty' => !empty($row['required']) ? true : !empty($row['display_empty']), 'bbc' => !empty($row['bbc']) && ($row['field_type'] == CFIELD_TYPE_TEXT || $row['field_type'] == CFIELD_TYPE_LARGETEXT) && $row['placement'] != CFIELD_PLACE_PREFIX, 'editable' => !empty($editable));
        if (!empty($field['options']) && empty($field['options']['inactive'])) {
            $field['options']['inactive'] = array();
        }
        if (in_array($field['type'], array(CFIELD_TYPE_RADIO, CFIELD_TYPE_SELECT, CFIELD_TYPE_MULTI))) {
            foreach ($field['options'] as $k => $v) {
                if ($k != 'inactive' && strpos($v, '[') !== false) {
                    $field['options'][$k] = parse_bbc($v, false);
                }
            }
        }
        if ($row['field_loc'] & CFIELD_REPLY && $field['editable']) {
            $context['ticket_form']['custom_fields']['reply'][$field['id']] = $field;
        }
        // Add fields to the master list, getting any values as we go.
        if ($row['field_loc'] & CFIELD_TICKET && (!empty($field_values['ticket'][$row['id_field']]['post_type']) && $field_values['ticket'][$row['id_field']]['post_type'] == CFIELD_TICKET || $field['display_empty'])) {
            if (isset($field_values['ticket'][$row['id_field']])) {
                $field['value'] = $field['bbc'] ? shd_format_text($field_values['ticket'][$row['id_field']]['value']) : $field_values['ticket'][$row['id_field']]['value'];
            }
            $context['ticket']['custom_fields'][$pos][$row['id_field']] = $field;
        }
        if ($row['field_loc'] & CFIELD_REPLY) {
            foreach ($field_values as $dest => $field_details) {
                unset($field['value']);
                if ($dest == 'ticket' || !isset($field_details[$row['id_field']]) || $field_details[$row['id_field']]['post_type'] != CFIELD_REPLY) {
                    continue;
                }
                $field['value'] = $field['bbc'] ? shd_format_text($field_details[$row['id_field']]['value']) : $field_details[$row['id_field']]['value'];
                $context['custom_fields_replies'][$dest][$row['id_field']] = $field;
            }
            // We also need to attach the field to replies didn't get the field added, in the event that the field should be displayed by default.
            if ($field['display_empty']) {
                foreach ($context['ticket_messages'] as $msg) {
                    if (!isset($context['custom_fields_replies'][$msg][$row['id_field']])) {
                        $field['value'] = '';
                        $context['custom_fields_replies'][$msg][$row['id_field']] = $field;
                    }
                }
            }
        }
    }
    $smcFunc['db_free_result']($query);
    // Grab the avatar for the poster
    $context['ticket']['poster_avatar'] = empty($context['ticket']['member']['id']) ? array() : (loadMemberContext($context['ticket']['id_member']) ? $memberContext[$context['ticket']['id_member']]['avatar'] : array());
    // Before we grab attachments, also make sure we get any from the first msg (i.e. the ticket)
    $context['ticket_messages'][] = $context['ticket']['first_msg'];
    shd_display_load_attachments();
    // Mark read goes here
    if (!empty($user_info['id'])) {
        $smcFunc['db_insert']('replace', '{db_prefix}helpdesk_log_read', array('id_ticket' => 'int', 'id_member' => 'int', 'id_msg' => 'int'), array($context['ticket_id'], $user_info['id'], $ticketinfo['id_last_msg']), array('id_member', 'id_topic'));
    }
    // Template stuff
    $context['sub_template'] = 'viewticket';
    $ticketname = '';
    if (!empty($context['ticket']['custom_fields']['prefix'])) {
        $ticketname = '[' . $context['ticket']['display_id'] . '] ';
        $fields = '';
        foreach ($context['ticket']['custom_fields']['prefix'] as $field) {
            if (empty($field['value'])) {
                continue;
            }
            if ($field['type'] == CFIELD_TYPE_CHECKBOX) {
                $fields .= !empty($field['value']) ? $txt['yes'] . ' ' : $txt['no'] . ' ';
            } elseif ($field['type'] == CFIELD_TYPE_SELECT || $field['type'] == CFIELD_TYPE_RADIO) {
                $fields .= trim(strip_tags($field['options'][$field['value']])) . ' ';
            } elseif ($field['type'] == CFIELD_TYPE_MULTI) {
                $values = explode(',', $field['value']);
                foreach ($values as $value) {
                    $fields .= trim(strip_tags($field['options'][$value])) . ' ';
                }
            } else {
                $fields .= $field['value'] . ' ';
            }
        }
        $fields = trim($fields);
        $ticketname .= (!empty($fields) ? '[' . trim($fields) . '] ' : '') . $context['ticket']['subject'];
    } else {
        $ticketname = '[' . $context['ticket']['display_id'] . '] ' . $context['ticket']['subject'];
    }
    $context['page_title'] = $txt['shd_helpdesk'] . ' ' . $ticketname;
    // If we're in a department, display that.
    if ($context['shd_multi_dept']) {
        $context['linktree'][] = array('url' => $scripturl . '?' . $context['shd_home'] . $context['shd_dept_link'], 'name' => $context['ticket']['dept_name']);
    }
    // Build the link tree. If the ticket is recycled, display 'Recycle bin'.
    if ($context['ticket']['status']['level'] == TICKET_STATUS_DELETED) {
        $context['linktree'][] = array('url' => $scripturl . '?action=helpdesk;sa=recyclebin' . $context['shd_dept_link'], 'name' => $txt['shd_recycle_bin']);
    } elseif ($context['ticket']['status']['level'] == TICKET_STATUS_CLOSED) {
        $context['linktree'][] = array('url' => $scripturl . '?action=helpdesk;sa=closedtickets' . $context['shd_dept_link'], 'name' => $txt['shd_tickets_closed']);
    }
    // Lastly add the ticket name and link to the linktree.
    $context['linktree'][] = array('url' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'], 'name' => $ticketname);
    // Ticket navigation / permission
    $context['can_move_dept'] = !empty($context['shd_multi_dept']) && (shd_allowed_to('shd_move_dept_any', $context['ticket']['dept']) || $context['ticket']['ticket_opener'] && shd_allowed_to('shd_move_dept_own', $context['ticket']['dept']));
    $context['can_reply'] = !$context['ticket']['closed'] && !$context['ticket']['deleted'] && (shd_allowed_to('shd_reply_ticket_any', $context['ticket']['dept']) || $context['ticket']['ticket_opener'] && shd_allowed_to('shd_reply_ticket_own', $context['ticket']['dept']));
    // needs perms - calc'd here because we use it in display template too
    $context['can_quote'] = $context['can_reply'] && !empty($modSettings['shd_allow_ticket_bbc']);
    $context['can_go_advanced'] = !empty($modSettings['shd_allow_ticket_bbc']) || !empty($modSettings['allow_ticket_smileys']) || shd_allowed_to('shd_post_attachment', $context['ticket']['dept']);
    $context['shd_can_move_to_topic'] = empty($modSettings['shd_disable_tickettotopic']) && shd_allowed_to('shd_ticket_to_topic', $context['ticket']['dept']) && empty($modSettings['shd_helpdesk_only']);
    $context['can_solve'] = !$context['ticket']['closed'] && !$context['ticket']['deleted'] && (shd_allowed_to('shd_resolve_ticket_any', $context['ticket']['dept']) || shd_allowed_to('shd_resolve_ticket_own', $context['ticket']['dept']) && $context['ticket']['ticket_opener']);
    $context['can_unsolve'] = $context['ticket']['closed'] && (shd_allowed_to('shd_unresolve_ticket_any', $context['ticket']['dept']) || shd_allowed_to('shd_unresolve_ticket_own', $context['ticket']['dept']) && $context['ticket']['ticket_opener']);
    $context['can_silent_update'] = $context['can_reply'] && shd_allowed_to('shd_silent_update', $context['ticket']['dept']);
    // And off we go
    $context['ticket_navigation'] = array();
    $context['ticket_navigation'][] = array('url' => $scripturl . '?action=helpdesk;sa=editticket;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'icon' => 'edit', 'alt' => '*', 'display' => !$context['ticket']['closed'] && !$context['ticket']['deleted'] && (shd_allowed_to('shd_edit_ticket_any', $context['ticket']['dept']) || $context['ticket']['ticket_opener'] && shd_allowed_to('shd_edit_ticket_own', $context['ticket']['dept'])), 'text' => 'shd_ticket_edit');
    $context['ticket_navigation'][] = array('url' => $scripturl . '?action=helpdesk;sa=markunread;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'icon' => 'unread', 'alt' => '*', 'display' => !$context['ticket']['closed'] && !$context['ticket']['deleted'], 'text' => 'shd_ticket_markunread');
    $context['ticket_navigation'][] = array('url' => $scripturl . '?action=helpdesk;sa=resolveticket;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'icon' => 'resolved', 'alt' => '*', 'display' => $context['can_solve'], 'text' => 'shd_ticket_resolved');
    $context['ticket_navigation'][] = array('url' => $scripturl . '?action=helpdesk;sa=resolveticket;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'icon' => 'unresolved', 'alt' => '*', 'display' => $context['can_unsolve'], 'text' => 'shd_ticket_unresolved');
    // This is always going to be a pain. But it should be possible to contextualise it nicely.
    // And while this isn't quite as nicely formatted as a single nice array definition,
    // imagine trying to debug the display and text options later if it were done with nested ternaries... *shudder*
    $context['ajax_assign'] = false;
    $assign_nav = array('url' => $scripturl . '?action=helpdesk;sa=assign;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'icon' => 'assign', 'alt' => '*', 'text' => '', 'display' => false);
    if (shd_allowed_to('shd_assign_ticket_any', $context['ticket']['dept'])) {
        $assign_nav['display'] = shd_allowed_to('shd_staff', $context['ticket']['dept']) && !$context['ticket']['closed'] && !$context['ticket']['deleted'];
        $assign_nav['text'] = empty($context['ticket']['id_member_assigned']) ? 'shd_ticket_assign' : 'shd_ticket_reassign';
        $context['ajax_assign'] = $assign_nav['display'];
    } elseif (shd_allowed_to('shd_assign_ticket_own', $context['ticket']['dept'])) {
        $assign_nav['display'] = !$context['ticket']['closed'] && !$context['ticket']['deleted'] && shd_allowed_to('shd_staff', $context['ticket']['dept']) && (empty($context['ticket']['id_member_assigned']) || $context['ticket']['assigned_self']);
        // either not assigned or assigned to self
        $assign_nav['text'] = $context['ticket']['assigned_self'] ? 'shd_ticket_unassign' : 'shd_ticket_assign_self';
    }
    $context['ticket_navigation'][] = $assign_nav;
    $context['ticket_navigation'][] = array('url' => $scripturl . '?action=helpdesk;sa=deleteticket;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'icon' => 'delete', 'alt' => '*', 'display' => !$context['ticket']['closed'] && !$context['ticket']['deleted'] && (shd_allowed_to('shd_delete_ticket_any', $context['ticket']['dept']) || shd_allowed_to('shd_delete_ticket_own', $context['ticket']['dept']) && $context['ticket']['ticket_opener']), 'text' => 'shd_ticket_delete', 'onclick' => 'return confirm(' . JavaScriptEscape($txt['shd_delete_confirm']) . ');');
    $context['ticket_navigation'][] = array('url' => $scripturl . '?action=helpdesk;sa=restoreticket;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'icon' => 'restore', 'alt' => '*', 'display' => $context['ticket']['deleted'] && (shd_allowed_to('shd_restore_ticket_any', $context['ticket']['dept']) || shd_allowed_to('shd_restore_ticket_own', $context['ticket']['dept']) && $context['ticket']['ticket_opener']), 'text' => 'shd_ticket_restore');
    $context['ticket_navigation'][] = array('url' => $scripturl . '?action=helpdesk;sa=permadelete;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'icon' => 'delete', 'alt' => '*', 'display' => $context['ticket']['deleted'] && shd_allowed_to('shd_delete_recycling', $context['ticket']['dept']), 'text' => 'shd_delete_permanently', 'onclick' => 'return confirm(' . JavaScriptEscape($txt['shd_delete_permanently_confirm']) . ');');
    $context['ticket_navigation'][] = array('url' => $scripturl . '?action=helpdesk;sa=movedept;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'icon' => 'movedept', 'alt' => '*', 'display' => $context['can_move_dept'], 'text' => 'shd_move_dept');
    $context['ticket_navigation'][] = array('url' => $scripturl . '?action=helpdesk;sa=tickettotopic;ticket=' . $context['ticket']['id'] . ';' . $context['session_var'] . '=' . $context['session_id'], 'icon' => 'tickettotopic', 'alt' => '*', 'display' => $context['shd_can_move_to_topic'] && !$context['ticket']['closed'] && !$context['ticket']['deleted'] && ($context['ticket']['deleted_replies'] == 0 || shd_allowed_to('shd_access_recyclebin', $context['ticket']['dept'])), 'text' => 'shd_ticket_move_to_topic');
    // While we're at it, set up general navigation for this ticket. We'll sort out access to the action log later.
    $context['navigation']['replies'] = array('text' => 'shd_go_to_replies', 'lang' => true, 'url' => '#replies');
    $context['navigation']['ticketlog'] = array('text' => 'shd_go_to_action_log', 'test' => 'display_ticket_log', 'lang' => true, 'url' => '#ticket_log_header');
    // If we are going SMF style with the navigation, we need to rework the structure a wee bit.
    // No sense making a new array, mind, just fix up the existing one a touch, and don't do this on the master as we don't always need it.
    if (empty($modSettings['shd_ticketnav_style']) || !in_array($modSettings['shd_ticketnav_style'], array('sd', 'sdcompact', 'smf'))) {
        $modSettings['shd_ticketnav_style'] = 'sd';
    }
    if ($modSettings['shd_ticketnav_style'] == 'smf') {
        foreach ($context['ticket_navigation'] as $key => $button) {
            $context['can_' . $button['text']] = $button['display'];
            $context['ticket_navigation'][$key] += array('lang' => true, 'test' => 'can_' . $button['text'], 'image' => 'shd_ticket_' . $button['icon'] . '.png');
        }
    }
    // Quick reply stuffs
    require_once $sourcedir . '/sd_source/SimpleDesk-Post.php';
    require_once $sourcedir . '/Subs-Editor.php';
    loadTemplate('sd_template/SimpleDesk-Post');
    $context['ticket_form']['ticket'] = $context['ticket_id'];
    $context['ticket_form']['num_allowed_attachments'] = empty($modSettings['attachmentNumPerPostLimit']) || $modSettings['shd_attachments_mode'] == 'ticket' ? -1 : $modSettings['attachmentNumPerPostLimit'];
    $context['ticket_form']['do_attach'] = shd_allowed_to('shd_post_attachment', $context['ticket']['dept']);
    $context['ticket_form']['num_replies'] = $context['ticket']['num_replies'];
    $context['ticket_form']['disable_smileys'] = empty($modSettings['shd_allow_ticket_smileys']);
    shd_posting_additional_options();
    if ($context['can_reply']) {
        shd_load_canned_replies();
    }
    $context['can_ping'] = $context['can_reply'] && shd_allowed_to('shd_singleton_email', $context['ticket']['dept']);
    // Set up the fancy editor
    shd_postbox('shd_message', '', array('post_button' => $txt['shd_reply_ticket']));
    // Lastly, our magic AJAX stuff ;D and we know we already made html_headers exist in SimpleDesk.php, score!
    $context['html_headers'] .= '
	<script type="text/javascript"><!-- // --><![CDATA[
	var sSessI = "' . $context['session_id'] . '";
	var sSessV = "' . $context['session_var'] . '";';
    if ($context['ticket']['privacy']['can_change']) {
        $context['html_headers'] .= '
	var shd_ajax_problem = ' . JavaScriptEscape($txt['shd_ajax_problem']) . ';
	var privacyCtl = new shd_privacyControl({
		ticket: ' . $context['ticket_id'] . ',
		sUrl: smf_scripturl + "?action=helpdesk;sa=ajax;op=privacy;ticket=' . $context['ticket_id'] . '",
		sSession: sSessV + "=" + sSessI,
		sSrcA: "privlink",
		sDestSpan: "privacy"
	});';
    }
    if ($context['ticket']['urgency']['increase'] || $context['ticket']['urgency']['decrease']) {
        $context['html_headers'] .= '
	var urgencyCtl = new shd_urgencyControl({
		ticket: ' . $context['ticket_id'] . ',
		sUrl: smf_scripturl + "?action=helpdesk;sa=ajax;op=urgency;ticket=' . $context['ticket_id'] . ';change=",
		sSession: sSessV + "=" + sSessI,
		sDestSpan: "urgency",
		aButtons: ["up", "down"],
		aButtonOps: { up: "increase", down: "decrease" }
	});';
    }
    if (!empty($options['display_quick_reply'])) {
        $context['html_headers'] .= '
	var oQuickReply = new QuickReply({
		bDefaultCollapsed: ' . (!empty($options['display_quick_reply']) && $options['display_quick_reply'] == 2 ? 'false' : 'true') . ',
		iTicketId: ' . $context['ticket_id'] . ',
		iStart: ' . $context['start'] . ',
		sScriptUrl: smf_scripturl,
		sImagesUrl: "' . $settings['images_url'] . '",
		sContainerId: "quickReplyOptions",
		sImageId: "quickReplyExpand",
		sImageCollapsed: "collapse.png",
		sImageExpanded: "expand.png",
		sJumpAnchor: "quickreply",
		sHeaderId: "quickreplyheader",
		sFooterId: "quickreplyfooter"
	});';
    }
    $context['html_headers'] .= '
	var oCustomFields = new CustomFields({
		sImagesUrl: "' . $settings['images_url'] . '",
		sContainerId: "additional_info",
		sImageId: "shd_custom_fields_swap",
		sImageCollapsed: "collapse.png",
		sImageExpanded: "expand.png",
		sHeaderId: "additionalinfoheader",
		sFooterId: "additional_info_footer",
	});';
    if (!empty($options['display_quick_reply']) && $context['can_go_advanced']) {
        $context['html_headers'] .= '
	function goAdvanced()
	{
		document.getElementById("shd_bbcbox").style.display = ' . (!empty($modSettings['shd_allow_ticket_bbc']) ? '""' : '"none"') . ';
		document.getElementById("shd_smileybox").style.display = ' . (!empty($modSettings['shd_allow_ticket_smileys']) ? '""' : '"none"') . ';
		document.getElementById("shd_attach_container").style.display = ' . (!empty($context['ticket_form']['do_attach']) ? '""' : '"none"') . ';
		document.getElementById("shd_goadvancedbutton").style.display = "none";' . (!empty($context['controls']['richedit']['shd_message']['rich_active']) ? '
		oEditorHandle_shd_message.toggleView(true);' : '') . '
	}
	';
    }
    $context['html_headers'] .= '
	// ]' . ']></script>';
    $context['shd_display'] = true;
    $context['controls']['richedit']['shd_message']['rich_active'] = 0;
    // we don't want it by default!
    // Register this form in the session variables.
    checkSubmitOnce('register');
    // Should we load and display this ticket's action log?
    $context['display_ticket_log'] = !empty($modSettings['shd_display_ticket_logs']) && (shd_allowed_to('shd_view_ticket_logs_any', $context['ticket']['dept']) || shd_allowed_to('shd_view_ticket_logs_own', $context['ticket']['dept']) && $context['ticket']['ticket_opener']);
    // If yes, go ahead and load the log entries (Re-using a couple of functions from the ACP)
    if (!empty($context['display_ticket_log'])) {
        require_once $sourcedir . '/sd_source/Subs-SimpleDeskAdmin.php';
        $context['ticket_log'] = shd_load_action_log_entries(-1, 10, '', '', 'la.id_ticket = ' . $context['ticket_id']);
        $context['ticket_log_count'] = shd_count_action_log_entries('la.id_ticket = ' . $context['ticket_id']);
        $context['ticket_full_log'] = allowedTo('admin_forum') || shd_allowed_to('admin_helpdesk', 0);
    }
    // What about related tickets?
    $context['create_relationships'] = shd_allowed_to('shd_create_relationships', $context['ticket']['dept']);
    $context['display_relationships'] = (shd_allowed_to('shd_view_relationships', $context['ticket']['dept']) || $context['create_relationships']) && empty($modSettings['shd_disable_relationships']);
    $context['delete_relationships'] = shd_allowed_to('shd_delete_relationships', $context['ticket']['dept']);
    if (!empty($context['display_relationships'])) {
        shd_load_relationships($context['ticket_id']);
        if ($context['relationships_count'] == 0 && empty($context['create_relationships'])) {
            $context['display_relationships'] = false;
        }
    }
    // And, of course, notifications. If we can see the ticket, we can do something with notifications.
    $context['display_notifications'] = array('show' => false, 'preferences' => array(), 'can_change' => shd_allowed_to(array('shd_view_profile_own', 'shd_view_profile_any'), 0) && shd_allowed_to(array('shd_view_preferences_own', 'shd_view_preferences_any'), 0), 'can_monitor' => shd_allowed_to('shd_monitor_ticket_any', $context['ticket']['dept']) || $context['ticket']['ticket_opener'] && shd_allowed_to('shd_monitor_ticket_own', $context['ticket']['dept']), 'is_monitoring' => false, 'can_ignore' => shd_allowed_to('shd_ignore_ticket_any', $context['ticket']['dept']) || $context['ticket']['ticket_opener'] && shd_allowed_to('shd_ignore_ticket_own', $context['ticket']['dept']), 'is_ignoring' => false);
    $notify_state = NOTIFY_PREFS;
    $query = $smcFunc['db_query']('', '
		SELECT notify_state
		FROM {db_prefix}helpdesk_notify_override
		WHERE id_member = {int:user}
			AND id_ticket = {int:ticket}', array('user' => $context['user']['id'], 'ticket' => $context['ticket_id']));
    if ($smcFunc['db_num_rows']($query) != 0) {
        list($notify_state) = $smcFunc['db_fetch_row']($query);
    }
    $smcFunc['db_free_result']($query);
    $context['display_notifications']['is_monitoring'] = $notify_state == NOTIFY_ALWAYS;
    $context['display_notifications']['is_ignoring'] = $notify_state == NOTIFY_NEVER;
    if ($notify_state != NOTIFY_NEVER) {
        if ($context['ticket']['ticket_opener'] && !empty($context['shd_preferences']['notify_new_reply_own'])) {
            $context['display_notifications']['preferences'][] = 'yourticket';
        }
        if ($context['ticket']['assigned_self'] && !empty($context['shd_preferences']['notify_new_reply_assigned'])) {
            $context['display_notifications']['preferences'][] = 'assignedyou';
        }
        if (!empty($context['shd_preferences']['notify_new_reply_previous'])) {
            // We need to query to see if we've replied here before - but we don't need to check ticket access.
            $query = $smcFunc['db_query']('', '
				SELECT COUNT(hdtr.id_msg)
				FROM {db_prefix}helpdesk_tickets AS hdt
					INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdt.id_ticket = hdtr.id_ticket)
				WHERE hdt.id_ticket = {int:ticket}
					AND hdtr.id_member = {int:user}
					AND hdtr.id_msg != hdt.id_first_msg', array('ticket' => $context['ticket_id'], 'user' => $context['user']['id']));
            list($count) = $smcFunc['db_fetch_row']($query);
            $smcFunc['db_free_result']($query);
            if (!empty($count)) {
                $context['display_notifications']['preferences'][] = 'priorreply';
            }
        }
        if (!empty($context['shd_preferences']['notify_new_reply_any'])) {
            $context['display_notifications']['preferences'][] = 'anyreply';
        }
    }
    if (!empty($context['display_notifications']['preferences']) || $context['display_notifications']['can_monitor'] || $context['display_notifications']['can_ignore'] || $context['display_notifications']['can_change']) {
        $context['display_notifications']['show'] = true;
    }
}