function shd_profile_show_tickets($memID)
{
    global $txt, $user_info, $scripturl, $modSettings, $smcFunc, $board, $user_profile, $context;
    // Navigation
    $context['show_tickets_navigation'] = array('tickets' => array('text' => 'shd_profile_show_tickets', 'lang' => true, 'url' => $scripturl . '?action=profile;u=' . $memID . ';area=hd_showtickets;sa=tickets'), 'replies' => array('text' => 'shd_profile_show_replies', 'lang' => true, 'url' => $scripturl . '?action=profile;u=' . $memID . ';area=hd_showtickets;sa=replies'));
    // We might be adding the monitor/ignore lists, but we're only interested in those if we're actually on our own page.
    if ($memID == $user_info['id']) {
        if (shd_allowed_to('shd_monitor_ticket_any') || shd_allowed_to('shd_monitor_ticket_own')) {
            $context['show_tickets_navigation']['monitor'] = array('text' => 'shd_profile_show_monitor', 'lang' => true, 'url' => $scripturl . '?action=profile;u=' . $memID . ';area=hd_showtickets;sa=monitor');
        }
        if (shd_allowed_to('shd_ignore_ticket_any') || shd_allowed_to('shd_ignore_ticket_own')) {
            $context['show_tickets_navigation']['ignore'] = array('text' => 'shd_profile_show_ignore', 'lang' => true, 'url' => $scripturl . '?action=profile;u=' . $memID . ';area=hd_showtickets;sa=ignore');
        }
        // We have the monitor and ignore lists in this area but this code can't deal with it, so we need to go somewhere else with it.
        if (isset($_GET['sa']) && ($_GET['sa'] == 'monitor' || $_GET['sa'] == 'ignore')) {
            return shd_profile_show_notify_override($memID);
        }
    }
    $context['page_title'] = $txt['shd_profile_show_tickets'] . ' - ' . $user_profile[$memID]['real_name'];
    $context['sub_template'] = 'shd_profile_show_tickets';
    $context['start'] = (int) $_REQUEST['start'];
    // The time has come to choose: Tickets, or just replies?
    $context['can_haz_replies'] = isset($_GET['sa']) && $_GET['sa'] == 'replies' ? true : false;
    // The active button.
    $context['show_tickets_navigation'][$context['can_haz_replies'] ? 'replies' : 'tickets']['active'] = true;
    // "That still only counts as one!"
    if ($context['can_haz_replies']) {
        $request = shd_db_query('', '
			SELECT COUNT(hdtr.id_msg)
			FROM {db_prefix}helpdesk_ticket_replies AS hdtr
				LEFT JOIN {db_prefix}helpdesk_tickets AS hdt ON(hdtr.id_ticket = hdt.id_ticket)
			WHERE hdtr.id_member = {int:user}
				AND {query_see_ticket}', array('user' => $memID));
    } else {
        $request = shd_db_query('', '
			SELECT COUNT(hdt.id_ticket)
			FROM {db_prefix}helpdesk_tickets AS hdt
			WHERE hdt.id_member_started = {int:user}
				AND {query_see_ticket}', array('user' => $memID));
    }
    list($item_count) = $smcFunc['db_fetch_row']($request);
    $smcFunc['db_free_result']($request);
    // Max? Max? Where are you?
    $request = shd_db_query('', '
		SELECT MIN(hdtr.id_msg), MAX(hdtr.id_msg)
		FROM {db_prefix}helpdesk_ticket_replies AS hdtr
			LEFT JOIN {db_prefix}helpdesk_tickets AS hdt ON(hdtr.id_ticket = hdt.id_ticket)
		WHERE hdtr.id_member = {int:user}
			AND {query_see_ticket}', array('user' => $memID));
    list($min_msg_member, $max_msg_member) = $smcFunc['db_fetch_row']($request);
    $smcFunc['db_free_result']($request);
    $reverse = false;
    $max_index = (int) $modSettings['defaultMaxMessages'];
    // A little page index to help us along the way!
    $context['page_index'] = shd_no_expand_pageindex($scripturl . '?action=profile;u=' . $memID . ';area=hd_showtickets' . ($context['can_haz_replies'] ? ';sa=replies' : ''), $context['start'], $item_count, $max_index);
    $context['current_page'] = $context['start'] / $max_index;
    // Reverse the query if we're past 50% of the pages for better performance.
    $start = $context['start'];
    $reverse = $_REQUEST['start'] > $item_count / 2;
    if ($reverse) {
        $max_index = $item_count < $context['start'] + $modSettings['defaultMaxMessages'] + 1 && $item_count > $context['start'] ? $item_count - $context['start'] : (int) $modSettings['defaultMaxMessages'];
        $start = $item_count < $context['start'] + $modSettings['defaultMaxMessages'] + 1 || $item_count < $context['start'] + $modSettings['defaultMaxMessages'] ? 0 : $item_count - $context['start'] - $modSettings['defaultMaxMessages'];
    }
    // Bring 'em to me!
    $looped = false;
    while (true) {
        if ($context['can_haz_replies']) {
            $request = shd_db_query('', '
				SELECT
					hdtr.id_member, hdt.subject, hdt.id_first_msg,
					hdtr.body, hdtr.smileys_enabled, hdtr.poster_time, hdtr.id_ticket, hdtr.id_msg
				FROM {db_prefix}helpdesk_ticket_replies AS hdtr
					INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdt.id_ticket = hdtr.id_ticket)
				WHERE hdtr.id_member = {int:user}
					AND {query_see_ticket}
				ORDER BY hdtr.id_msg ' . ($reverse ? 'ASC' : 'DESC') . '
				LIMIT ' . $start . ', ' . $max_index, array('user' => $memID));
        } else {
            $request = shd_db_query('', '
				SELECT
					hdt.id_member_started, hdt.id_first_msg, hdt.id_last_msg, hdt.subject,
					hdtr.body, hdtr.smileys_enabled, hdtr.poster_time, hdtr.id_ticket, hdtr.id_msg
				FROM {db_prefix}helpdesk_tickets AS hdt
					INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdtr.id_msg = hdt.id_first_msg)
				WHERE hdt.id_member_started = {int:user}
					AND {query_see_ticket}
				ORDER BY hdt.id_first_msg ' . ($reverse ? 'ASC' : 'DESC') . '
				LIMIT ' . $start . ', ' . $max_index, array('user' => $memID));
        }
        // Hold it!
        if ($smcFunc['db_num_rows']($request) === $max_index || $looped) {
            break;
        }
        $looped = true;
    }
    // Start counting at the number of the first message displayed.
    $counter = $reverse ? $context['start'] + $max_index + 1 : $context['start'];
    $context['items'] = array();
    while ($row = $smcFunc['db_fetch_assoc']($request)) {
        // Censor the content
        censorText($row['body']);
        censorText($row['subject']);
        // Do the parsing dance! Eh...
        $row['body'] = shd_format_text($row['body'], $row['smileys_enabled'], 'shd_reply_' . $row['id_msg']);
        // And finally,  store the load of cr--... the results!
        $context['items'][$counter += $reverse ? -1 : 1] = array('body' => $row['body'], 'counter' => $counter, 'alternate' => $counter % 2, 'ticket' => $row['id_ticket'], 'subject' => $row['subject'], 'start' => 'msg' . $row['id_msg'], 'time' => timeformat($row['poster_time']), 'timestamp' => forum_time(true, $row['poster_time']), 'msg' => $row['id_msg'], 'is_ticket' => empty($context['can_haz_replies']) ? true : $row['id_msg'] == $row['id_first_msg']);
    }
    // Freedom.
    $smcFunc['db_free_result']($request);
    // Head's up, feet's down.
    if ($reverse) {
        $context['items'] = array_reverse($context['items'], true);
    }
}
/**
 *	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;
    }
}
Exemple #3
0
/**
 *	Gather the data and prepare to display the ticket blocks.
 *
 *	Actually performs the queries to get data for each block, subject to the parameters specified by the calling functions.
 *
 *	It also sets up per-block pagination links, collects a variety of data (enough to populate all the columns as listed in shd_main_helpdesk,
 *	even if not entirely applicable, and populates it all into $context['ticket_blocks']['tickets'], extending the array that was
 *	already there.
 *
 *	@see shd_main_helpdesk()
 *	@see shd_closed_tickets()
 *	@see shd_recycle_bin()
 *	@since 1.0
*/
function shd_helpdesk_listing()
{
    global $context, $txt, $smcFunc, $user_profile, $scripturl, $settings, $user_info, $modSettings, $language;
    if (!empty($context['shd_permission'])) {
        shd_is_allowed_to($context['shd_permission']);
    }
    $block_list = array_keys($context['ticket_blocks']);
    $primary_url = '?action=helpdesk;sa=' . $_REQUEST['sa'];
    // First figure out the start positions of each item and sanitise them
    foreach ($context['ticket_blocks'] as $block_key => $block) {
        if (empty($block['viewing_as_block'])) {
            $num_per_page = !empty($context['shd_preferences']['blocks_' . $block_key . '_count']) ? $context['shd_preferences']['blocks_' . $block_key . '_count'] : $context['items_per_page'];
            $start = empty($_REQUEST['st_' . $block_key]) ? 0 : (int) $_REQUEST['st_' . $block_key];
            $max_value = $block['count'];
            // easier to read
        } else {
            $num_per_page = $context['items_per_page'];
            $max_value = $context['items_per_page'];
            $start = 0;
        }
        if ($start < 0) {
            $start = 0;
        } elseif ($start >= $max_value) {
            $start = max(0, (int) $max_value - ((int) $max_value % (int) $num_per_page == 0 ? $num_per_page : (int) $max_value % (int) $num_per_page));
        } else {
            $start = max(0, (int) $start - (int) $start % (int) $num_per_page);
        }
        $context['ticket_blocks'][$block_key]['start'] = $start;
        $context['ticket_blocks'][$block_key]['num_per_page'] = $num_per_page;
        if ($start != 0) {
            $_REQUEST['st_' . $block_key] = $start;
        } elseif (isset($_REQUEST['st_' . $block_key])) {
            unset($_REQUEST['st_' . $block_key]);
        }
    }
    // Now ordering the columns, separate loop for breaking the two processes apart
    $sort_methods = array('ticketid' => array('sql' => 'hdt.id_ticket'), 'ticketname' => array('sql' => 'hdt.subject'), 'replies' => array('sql' => 'hdt.num_replies'), 'allreplies' => array('sql' => '(hdt.num_replies + hdt.deleted_replies)'), 'urgency' => array('sql' => 'hdt.urgency'), 'updated' => array('sql' => 'hdt.last_updated'), 'assigned' => array('sql' => 'assigned_name', 'sql_select' => 'IFNULL(mem.real_name, 0) AS assigned_name', 'sql_join' => 'LEFT JOIN {db_prefix}members AS mem ON (hdt.id_member_assigned = mem.id_member)'), 'status' => array('sql' => 'hdt.status'), 'starter' => array('sql' => 'starter_name', 'sql_select' => 'IFNULL(mem.real_name, 0) AS starter_name', 'sql_join' => 'LEFT JOIN {db_prefix}members AS mem ON (hdt.id_member_started = mem.id_member)'), 'lastreply' => array('sql' => 'last_reply', 'sql_select' => 'IFNULL(mem.real_name, 0) AS last_reply', 'sql_join' => 'LEFT JOIN {db_prefix}members AS mem ON (hdtr_last.id_member = mem.id_member)'));
    foreach ($context['ticket_blocks'] as $block_key => $block) {
        $sort = isset($_REQUEST['so_' . $block_key]) ? $_REQUEST['so_' . $block_key] : (!empty($context['shd_preferences']['block_order_' . $block_key . '_block']) ? $context['shd_preferences']['block_order_' . $block_key . '_block'] : '');
        if (strpos($sort, '_') > 0 && substr_count($sort, '_') == 1) {
            list($sort_item, $sort_dir) = explode('_', $sort);
            if (empty($sort_methods[$sort_item])) {
                $sort_item = 'updated';
                $sort = '';
            }
            if (!in_array($sort_dir, array('asc', 'desc'))) {
                $sort = '';
                $sort_dir = 'asc';
            }
        } else {
            $sort = '';
            $sort_item = 'updated';
            $sort_dir = $_REQUEST['sa'] == 'closedtickets' || $_REQUEST['sa'] == 'recyclebin' ? 'desc' : 'asc';
            // default to newest first if on recyclebin or closed tickets, otherwise oldest first
        }
        if ($sort != '') {
            $_REQUEST['so_' . $block_key] = $sort;
        } elseif (isset($_REQUEST['so_' . $block_key])) {
            unset($_REQUEST['so_' . $block_key]);
        }
        $context['ticket_blocks'][$block_key]['sort'] = array('item' => $sort_item, 'direction' => $sort_dir, 'add_link' => $sort != '', 'sql' => array('select' => !empty($sort_methods[$sort_item]['sql_select']) ? $sort_methods[$sort_item]['sql_select'] : '', 'join' => !empty($sort_methods[$sort_item]['sql_join']) ? $sort_methods[$sort_item]['sql_join'] : '', 'sort' => $sort_methods[$sort_item]['sql'] . ' ' . strtoupper($sort_dir)), 'link_bits' => array());
    }
    // Having got all that, step through the blocks again to determine the full URL fragments
    foreach ($context['ticket_blocks'] as $block_key => $block) {
        foreach ($sort_methods as $method => $sort_details) {
            $context['ticket_blocks'][$block_key]['sort']['link_bits'][$method] = ';so_' . $block_key . '=' . $method . '_' . $block['sort']['direction'];
        }
    }
    // Figure out if the user is filtering on anything, and if so, set up containers for the extra joins, selects, pagination link fragments, etc
    $_REQUEST['field'] = isset($_REQUEST['field']) ? (int) $_REQUEST['field'] : 0;
    $_REQUEST['filter'] = isset($_REQUEST['filter']) ? (int) $_REQUEST['filter'] : 0;
    if ($_REQUEST['field'] > 0 && $_REQUEST['filter'] > 0) {
        $context['filter_fragment'] = ';field=' . $_REQUEST['field'] . ';filter=' . $_REQUEST['filter'];
        $context['filter_join'] = '
				INNER JOIN {db_prefix}helpdesk_custom_fields_values AS hdcfv ON (hdcfv.id_post = hdt.id_ticket AND hdcfv.id_field = {int:field} AND hdcfv.post_type = {int:type_ticket})
				INNER JOIN {db_prefix}helpdesk_custom_fields AS hdcf ON (hdcf.id_field = hdcfv.id_field AND hdcf.active = {int:active})';
        $context['filter_where'] = '
				AND hdcfv.value = {string:filter}';
    } else {
        $context['filter_fragment'] = '';
        $context['filter_join'] = '';
        $context['filter_where'] = '';
    }
    // Now go actually do the whole block thang, setting up space for a list of users and tickets as we go along
    $users = array();
    $tickets = array();
    foreach ($context['ticket_blocks'] as $block_key => $block) {
        if (empty($block['display']) || !empty($block['collapsed'])) {
            continue;
        }
        $context['ticket_blocks'][$block_key]['tickets'] = array();
        // If we're filtering, we have to query it first to figure out how many rows there are in this block. It's not pretty.
        if (!empty($context['filter_join'])) {
            $query = shd_db_query('', '
				SELECT COUNT(hdt.id_ticket)
				FROM {db_prefix}helpdesk_tickets AS hdt
					INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr_first ON (hdt.id_first_msg = hdtr_first.id_msg)
					INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr_last ON (hdt.id_last_msg = hdtr_last.id_msg)
					INNER JOIN {db_prefix}helpdesk_depts AS hdd ON (hdt.id_dept = hdd.id_dept)
					' . (!empty($block['sort']['sql']['join']) ? $block['sort']['sql']['join'] : '') . $context['filter_join'] . '
				WHERE {query_see_ticket}' . (!empty($block['where']) ? ' AND ' . $block['where'] : '') . (!empty($context['shd_department']) ? ' AND hdt.id_dept = {int:dept}' : '') . $context['filter_where'], array('dept' => $context['shd_department'], 'user' => $context['user']['id'], 'field' => $_REQUEST['field'], 'filter' => $_REQUEST['filter'], 'type_ticket' => CFIELD_TICKET, 'active' => 1));
            list($context['ticket_blocks'][$block_key]['count']) = $smcFunc['db_fetch_row']($query);
            $block['count'] = $context['ticket_blocks'][$block_key]['count'];
            $smcFunc['db_free_result']($query);
            if ($block['start'] >= $block['count']) {
                $context['ticket_blocks'][$block_key]['start'] = max(0, (int) $block['count'] - ((int) $block['count'] % (int) $block['num_per_page'] == 0 ? $block['num_per_page'] : (int) $block['count'] % (int) $block['num_per_page']));
                $block['start'] = $context['ticket_blocks'][$block_key]['start'];
            }
        }
        $query = shd_db_query('', '
			SELECT hdt.id_ticket, hdt.id_dept, hdd.dept_name, hdt.id_last_msg, hdt.id_member_started, hdt.id_member_updated,
				hdt.id_member_assigned, hdt.subject, hdt.status, hdt.num_replies, hdt.deleted_replies, hdt.private, hdt.urgency,
				hdt.last_updated, hdtr_first.poster_name AS ticket_opener, hdtr_last.poster_name AS respondent, hdtr_last.poster_time,
				IFNULL(hdlr.id_msg, 0) AS log_read' . (!empty($block['sort']['sql']['select']) ? ', ' . $block['sort']['sql']['select'] : '') . '
			FROM {db_prefix}helpdesk_tickets AS hdt
				INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr_first ON (hdt.id_first_msg = hdtr_first.id_msg)
				INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr_last ON (hdt.id_last_msg = hdtr_last.id_msg)
				INNER JOIN {db_prefix}helpdesk_depts AS hdd ON (hdt.id_dept = hdd.id_dept)
				LEFT JOIN {db_prefix}helpdesk_log_read AS hdlr ON (hdt.id_ticket = hdlr.id_ticket AND hdlr.id_member = {int:user})
				' . (!empty($block['sort']['sql']['join']) ? $block['sort']['sql']['join'] : '') . $context['filter_join'] . '
			WHERE {query_see_ticket}' . (!empty($block['where']) ? ' AND ' . $block['where'] : '') . (!empty($context['shd_department']) ? ' AND hdt.id_dept = {int:dept}' : '') . $context['filter_where'] . '
			ORDER BY ' . (!empty($block['sort']['sql']['sort']) ? $block['sort']['sql']['sort'] : 'hdt.id_last_msg ASC') . '
			LIMIT {int:start}, {int:items_per_page}', array('dept' => $context['shd_department'], 'user' => $context['user']['id'], 'start' => $block['start'], 'items_per_page' => $block['num_per_page'], 'field' => $_REQUEST['field'], 'filter' => $_REQUEST['filter'], 'type_ticket' => CFIELD_TICKET, 'active' => 1));
        while ($row = $smcFunc['db_fetch_assoc']($query)) {
            $is_own = $user_info['id'] == $row['id_member_started'];
            censorText($row['subject']);
            $new_block = array('id' => $row['id_ticket'], 'display_id' => str_pad($row['id_ticket'], $modSettings['shd_zerofill'], '0', STR_PAD_LEFT), 'dept_link' => empty($context['shd_department']) && $context['shd_multi_dept'] ? '[<a href="' . $scripturl . '?' . $context['shd_home'] . ';dept=' . $row['id_dept'] . '">' . $row['dept_name'] . '</a>] ' : '', 'link' => '<a href="' . $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $row['id_ticket'] . ($_REQUEST['sa'] == 'recyclebin' ? ';recycle' : '') . '">' . $row['subject'] . '</a>', 'subject' => $row['subject'], 'status' => array('level' => $row['status'], 'label' => $txt['shd_status_' . $row['status']]), 'starter' => array('id' => $row['id_member_started'], 'name' => $row['ticket_opener']), 'last_update' => timeformat($row['last_updated']), 'assigned' => array('id' => $row['id_member_assigned']), 'respondent' => array('id' => $row['id_member_updated'], 'name' => $row['respondent']), 'urgency' => array('level' => $row['urgency'], 'label' => $row['urgency'] > TICKET_URGENCY_HIGH ? '<span class="error">' . $txt['shd_urgency_' . $row['urgency']] . '</span>' : $txt['shd_urgency_' . $row['urgency']]), 'is_unread' => $row['id_last_msg'] > $row['log_read'], 'new_href' => $row['id_last_msg'] <= $row['log_read'] ? '' : $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $row['id_ticket'] . '.new' . ($_REQUEST['sa'] == 'recyclebin' ? ';recycle' : '') . '#new', 'private' => $row['private'], 'actions' => array('movedept' => !empty($context['shd_multi_dept']) && (shd_allowed_to('shd_move_dept_any', $context['shd_department']) || $is_own && shd_allowed_to('shd_move_dept_own', $context['shd_department'])) ? '<a href="' . $scripturl . '?action=helpdesk;sa=movedept;ticket=' . $row['id_ticket'] . ';home;' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_move_dept'] . '"><img src="' . $settings['default_images_url'] . '/simpledesk/movedept.png" alt="' . $txt['shd_move_dept'] . '" /></a>' : ''), 'num_replies' => $row['num_replies'], 'replies_href' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $row['id_ticket'] . '.msg' . $row['id_last_msg'] . '#msg' . $row['id_last_msg'] . ($_REQUEST['sa'] == 'recyclebin' ? ';recycle' : ''), 'all_replies' => (int) $row['num_replies'] + (int) $row['deleted_replies']);
            if ($row['status'] == TICKET_STATUS_CLOSED) {
                $new_block['actions'] += array('resolve' => shd_allowed_to('shd_unresolve_ticket_any', $context['shd_department']) || $is_own && shd_allowed_to('shd_unresolve_ticket_own', $context['shd_department']) ? '<a href="' . $scripturl . '?action=helpdesk;sa=resolveticket;ticket=' . $row['id_ticket'] . ';home;' . $context['shd_dept_link'] . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_ticket_unresolved'] . '"><img src="' . $settings['default_images_url'] . '/simpledesk/unresolved.png" alt="' . $txt['shd_ticket_unresolved'] . '" /></a>' : '');
            } elseif ($row['status'] == TICKET_STATUS_DELETED) {
                $new_block['actions'] += array('restore' => shd_allowed_to('shd_restore_ticket_any', $context['shd_department']) || $is_own && shd_allowed_to('shd_restore_ticket_own', $context['shd_department']) ? '<a href="' . $scripturl . '?action=helpdesk;sa=restoreticket;ticket=' . $row['id_ticket'] . ';home;' . $context['shd_dept_link'] . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_ticket_restore'] . '"><img src="' . $settings['default_images_url'] . '/simpledesk/restore.png" alt="' . $txt['shd_ticket_restore'] . '" /></a>' : '', 'permadelete' => shd_allowed_to('shd_delete_recycling', $context['shd_department']) ? '<a href="' . $scripturl . '?action=helpdesk;sa=permadelete;ticket=' . $row['id_ticket'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_delete_permanently'] . '" onclick="return confirm(' . JavaScriptEscape($txt['shd_delete_permanently_confirm']) . ');"><img src="' . $settings['default_images_url'] . '/simpledesk/delete.png" alt="' . $txt['shd_delete_permanently'] . '" /></a>' : '');
            } else {
                $langstring = '';
                if (shd_allowed_to('shd_assign_ticket_any', $context['shd_department'])) {
                    $langstring = empty($row['id_member_assigned']) ? $txt['shd_ticket_assign'] : $txt['shd_ticket_reassign'];
                } elseif (shd_allowed_to('shd_assign_ticket_own', $context['shd_department']) && (empty($row['id_member_assigned']) || $row['id_member_assigned'] == $context['user']['id'])) {
                    $langstring = $row['id_member_assigned'] == $context['user']['id'] ? $txt['shd_ticket_unassign'] : $txt['shd_ticket_assign_self'];
                }
                if (!empty($langstring)) {
                    $new_block['actions']['assign'] = '<a href="' . $scripturl . '?action=helpdesk;sa=assign;ticket=' . $row['id_ticket'] . ';home;' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $langstring . '"><img src="' . $settings['default_images_url'] . '/simpledesk/assign.png" alt="' . $langstring . '" /></a>';
                }
                $new_block['actions'] += array('resolve' => shd_allowed_to('shd_resolve_ticket_any', $context['shd_department']) || $is_own && shd_allowed_to('shd_resolve_ticket_own', $context['shd_department']) ? '<a href="' . $scripturl . '?action=helpdesk;sa=resolveticket;ticket=' . $row['id_ticket'] . ';home;' . $context['shd_dept_link'] . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_ticket_resolved'] . '"><img src="' . $settings['default_images_url'] . '/simpledesk/resolved.png" alt="' . $txt['shd_ticket_resolved'] . '" /></a>' : '', 'tickettotopic' => empty($modSettings['shd_helpdesk_only']) && shd_allowed_to('shd_ticket_to_topic', $context['shd_department']) && ($row['deleted_replies'] == 0 || shd_allowed_to('shd_access_recyclebin')) ? '<a href="' . $scripturl . '?action=helpdesk;sa=tickettotopic;ticket=' . $row['id_ticket'] . ';' . $context['shd_dept_link'] . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_ticket_move_to_topic'] . '"><img src="' . $settings['default_images_url'] . '/simpledesk/tickettotopic.png" alt="' . $txt['shd_ticket_move_to_topic'] . '" /></a>' : '', 'delete' => shd_allowed_to('shd_delete_ticket_any', $context['shd_department']) || $is_own && shd_allowed_to('shd_delete_ticket_own') ? '<a href="' . $scripturl . '?action=helpdesk;sa=deleteticket;ticket=' . $row['id_ticket'] . ';' . $context['shd_dept_link'] . ';' . $context['session_var'] . '=' . $context['session_id'] . '" title="' . $txt['shd_ticket_delete'] . '" onclick="return confirm(' . JavaScriptEscape($txt['shd_delete_confirm']) . ');"><img src="' . $settings['default_images_url'] . '/simpledesk/delete.png" alt="' . $txt['shd_ticket_delete'] . '" /></a>' : '');
            }
            $context['ticket_blocks'][$block_key]['tickets'][$row['id_ticket']] = $new_block;
            $users[] = $row['id_member_started'];
            $users[] = $row['id_member_updated'];
            $users[] = $row['id_member_assigned'];
            $tickets[$row['id_ticket']] = array();
        }
        $smcFunc['db_free_result']($query);
    }
    $users = array_unique($users);
    if (!empty($users)) {
        loadMemberData($users, false, 'minimal');
    }
    foreach ($context['ticket_blocks'] as $block_id => $block) {
        if (empty($block['tickets'])) {
            continue;
        }
        foreach ($block['tickets'] as $tid => $ticket) {
            // Set up names and profile links for topic starter
            if (!empty($user_profile[$ticket['starter']['id']])) {
                // We found the name, so let's use their current name and profile link
                $context['ticket_blocks'][$block_id]['tickets'][$tid]['starter']['name'] = $user_profile[$ticket['starter']['id']]['real_name'];
                $context['ticket_blocks'][$block_id]['tickets'][$tid]['starter']['link'] = shd_profile_link($user_profile[$ticket['starter']['id']]['real_name'], $ticket['starter']['id']);
            } else {
                // We didn't, so keep using the name we found previously and don't make an actual link
                $context['ticket_blocks'][$block_id]['tickets'][$tid]['starter']['link'] = $context['ticket_blocks'][$block_id]['tickets'][$tid]['starter']['name'];
            }
            // Set up names and profile links for assigned user
            if ($ticket['assigned']['id'] == 0 || empty($user_profile[$ticket['assigned']['id']])) {
                $context['ticket_blocks'][$block_id]['tickets'][$tid]['assigned']['name'] = $txt['shd_unassigned'];
                $context['ticket_blocks'][$block_id]['tickets'][$tid]['assigned']['link'] = '<span class="error">' . $txt['shd_unassigned'] . '</span>';
            } else {
                $context['ticket_blocks'][$block_id]['tickets'][$tid]['assigned']['name'] = $user_profile[$ticket['assigned']['id']]['real_name'];
                $context['ticket_blocks'][$block_id]['tickets'][$tid]['assigned']['link'] = shd_profile_link($user_profile[$ticket['assigned']['id']]['real_name'], $ticket['assigned']['id']);
            }
            // And last respondent
            if ($ticket['respondent']['id'] == 0 || empty($user_profile[$ticket['respondent']['id']])) {
                // Didn't find the name, so reuse what we have
                $context['ticket_blocks'][$block_id]['tickets'][$tid]['respondent']['link'] = $context['ticket_blocks'][$block_id]['tickets'][$tid]['respondent']['name'];
            } else {
                $context['ticket_blocks'][$block_id]['tickets'][$tid]['respondent']['name'] = $user_profile[$ticket['respondent']['id']]['real_name'];
                $context['ticket_blocks'][$block_id]['tickets'][$tid]['respondent']['link'] = shd_profile_link($user_profile[$ticket['respondent']['id']]['real_name'], $ticket['respondent']['id']);
            }
        }
    }
    foreach ($context['ticket_blocks'] as $block_id => $block) {
        if (empty($block['display']) || empty($block['count']) && !$block['required'] && empty($block['collapsed'])) {
            unset($context['ticket_blocks'][$block_id]);
        }
    }
    $base_url = '';
    foreach ($context['ticket_blocks'] as $block_id => $block) {
        if ($block['sort']['add_link']) {
            $base_url .= $block['sort']['link_bits'][$block['sort']['item']];
        }
    }
    if ($_REQUEST['sa'] != 'viewblock') {
        foreach ($context['ticket_blocks'] as $block_id => $block) {
            $num_per_page = !empty($context['shd_preferences']['blocks_' . $block_key . '_count']) ? $context['shd_preferences']['blocks_' . $block_key . '_count'] : $context['items_per_page'];
            $url_fragment = $base_url;
            foreach ($block_list as $block_item) {
                if ($block_item == $block_id) {
                    $url_fragment .= ';st_' . $block_item . '=%1$d';
                } elseif (!empty($context['ticket_blocks'][$block_item]['start'])) {
                    $url_fragment .= ';st_' . $block_item . '=' . $context['ticket_blocks'][$block_item]['start'];
                }
            }
            $context['start'] = $context['ticket_blocks'][$block_id]['start'];
            $context['ticket_blocks'][$block_id]['page_index'] = shd_no_expand_pageindex($scripturl . $primary_url . $url_fragment . $context['shd_dept_link'] . $context['filter_fragment'] . '#shd_block_' . $block_id, $context['start'], $block['count'], $block['num_per_page'], true);
        }
    }
    // Just need to deal with those pesky prefix fields, if there are any.
    if (empty($tickets)) {
        return;
    }
    // We're all done here.
    // 1. Figure out if there are any custom fields that apply to us or not.
    if ($context['shd_multi_dept'] && empty($context['shd_department'])) {
        $dept_list = shd_allowed_to('access_helpdesk', false);
    } else {
        $dept_list = array($context['shd_department']);
    }
    $fields = array();
    $query = $smcFunc['db_query']('', '
		SELECT hdcf.id_field, can_see, field_type, field_options, placement, field_name
		FROM {db_prefix}helpdesk_custom_fields AS hdcf
			INNER JOIN {db_prefix}helpdesk_custom_fields_depts AS hdcfd ON (hdcfd.id_field = hdcf.id_field)
		WHERE placement IN ({array_int:placement_prefix})
			AND field_loc IN ({array_int:locations})
			AND hdcfd.id_dept IN ({array_int:dept_list})
			AND active = {int:active}
		GROUP BY hdcf.id_field
		ORDER BY field_order', array('locations' => array(CFIELD_TICKET, CFIELD_TICKET | CFIELD_REPLY), 'placement_prefix' => array(CFIELD_PLACE_PREFIX, CFIELD_PLACE_PREFIXFILTER), 'active' => 1, 'dept_list' => $dept_list));
    $is_staff = shd_allowed_to('shd_staff', $context['shd_department']);
    $is_admin = $context['user']['is_admin'] || shd_allowed_to('admin_helpdesk', $context['shd_department']);
    $context['shd_filter_fields'] = array();
    while ($row = $smcFunc['db_fetch_assoc']($query)) {
        list($user_see, $staff_see) = explode(',', $row['can_see']);
        if ($is_admin || $is_staff && $staff_see == '1' || !$is_staff && $user_see == '1') {
            if (!empty($row['field_options'])) {
                $row['field_options'] = unserialize($row['field_options']);
                if (isset($row['field_options']['inactive'])) {
                    unset($row['field_options']['inactive']);
                }
                foreach ($row['field_options'] as $k => $v) {
                    if (strpos($v, '[') !== false) {
                        $row['field_options'][$k] = parse_bbc($v);
                    }
                }
            }
            $fields[$row['id_field']] = $row;
            if ($row['placement'] == CFIELD_PLACE_PREFIXFILTER) {
                $context['shd_filter_fields'][$row['id_field']] = array('name' => $row['field_name'], 'options' => $row['field_options'], 'in_use' => array());
            }
        }
    }
    $smcFunc['db_free_result']($query);
    if (empty($fields)) {
        return;
    }
    // No fields to process, time to go.
    // 2. Get the relevant values.
    $query = $smcFunc['db_query']('', '
		SELECT id_post, id_field, value
		FROM {db_prefix}helpdesk_custom_fields_values
		WHERE id_post IN ({array_int:tickets})
			AND id_field IN ({array_int:fields})
			AND post_type = {int:ticket}', array('tickets' => array_keys($tickets), 'fields' => array_keys($fields), 'ticket' => CFIELD_TICKET));
    while ($row = $smcFunc['db_fetch_assoc']($query)) {
        $tickets[$row['id_post']][$row['id_field']] = $row['value'];
    }
    // 3. Apply the values into the tickets.
    if ($_REQUEST['sa'] == 'closedtickets') {
        $context['filterbase'] = $scripturl . '?action=helpdesk;sa=closedtickets';
    } elseif ($_REQUEST['sa'] == 'recyclebin') {
        $context['filterbase'] = $scripturl . '?action=helpdesk;sa=recyclebin';
    } else {
        $context['filterbase'] = $scripturl . '?' . $context['shd_home'];
    }
    foreach ($context['ticket_blocks'] as $block_id => $block) {
        if (empty($block['tickets'])) {
            continue;
        }
        foreach ($block['tickets'] as $ticket_id => $ticket) {
            if (isset($tickets[$ticket_id])) {
                $prefix_filter = '';
                $prefix = '';
                foreach ($fields as $field_id => $field) {
                    if (empty($tickets[$ticket_id][$field_id])) {
                        continue;
                    }
                    if ($field['placement'] == CFIELD_PLACE_PREFIXFILTER) {
                        if (!isset($field['field_options'][$tickets[$ticket_id][$field_id]])) {
                            continue;
                        }
                        $prefix_filter .= '[<a href="' . $context['filterbase'] . $context['shd_dept_link'] . ';field=' . $field_id . ';filter=' . $tickets[$ticket_id][$field_id] . '">' . $field['field_options'][$tickets[$ticket_id][$field_id]] . '</a>] ';
                    } else {
                        if ($field['field_type'] == CFIELD_TYPE_CHECKBOX) {
                            $prefix .= !empty($tickets[$ticket_id][$field_id]) ? $txt['yes'] . ' ' : $txt['no'] . ' ';
                        } elseif ($field['field_type'] == CFIELD_TYPE_SELECT || $field['field_type'] == CFIELD_TYPE_RADIO) {
                            $prefix .= $field['field_options'][$tickets[$ticket_id][$field_id]] . ' ';
                        } elseif ($field['field_type'] == CFIELD_TYPE_MULTI) {
                            $values = explode(',', $tickets[$ticket_id][$field_id]);
                            foreach ($values as $value) {
                                $prefix .= $field['field_options'][$value] . ' ';
                            }
                        } else {
                            $prefix .= $tickets[$ticket_id][$field_id] . ' ';
                        }
                    }
                }
                // First, set aside the subject, and if there is a non category prefix, strip links from it.
                $subject = $ticket['subject'];
                if (!empty($prefix)) {
                    $prefix = '[' . trim(preg_replace('~<a (.*?)</a>~is', '', $prefix)) . '] ';
                }
                // Then, if we have a category prefix, prepend that to any other prefix we have.
                if (!empty($prefix_filter)) {
                    $prefix = $prefix_filter . $prefix;
                }
                // Lastly, if we have some kind of prefix to put in front of this ticket, do so.
                if (!empty($prefix)) {
                    $context['ticket_blocks'][$block_id]['tickets'][$ticket_id]['subject'] = $prefix . $subject;
                    $context['ticket_blocks'][$block_id]['tickets'][$ticket_id]['link'] = $prefix . '<a href="' . $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $ticket_id . ($_REQUEST['sa'] == 'recyclebin' ? ';recycle' : '') . '">' . $subject . '</a>';
                }
            }
        }
    }
    // 4. We've collected the list of prefix-filter fields in use, now establish which values are actually in use.
    if (!empty($context['shd_filter_fields'])) {
        $query = $smcFunc['db_query']('', '
			SELECT id_field, value
			FROM {db_prefix}helpdesk_custom_fields_values
			WHERE id_field IN ({array_int:fields})', array('fields' => array_keys($context['shd_filter_fields'])));
        while ($row = $smcFunc['db_fetch_assoc']($query)) {
            $context['shd_filter_fields'][$row['id_field']]['in_use'][$row['value']] = true;
        }
        $smcFunc['db_free_result']($query);
        foreach ($context['shd_filter_fields'] as $id_field => $field) {
            if (empty($field['in_use'])) {
                unset($context['shd_filter_fields'][$id_field]);
            } else {
                foreach ($field['options'] as $k => $v) {
                    if (!isset($field['in_use'][$k])) {
                        unset($context['shd_filter_fields'][$id_field]['options'][$k]);
                    }
                }
                if (empty($context['shd_filter_fields'][$id_field]['options'])) {
                    unset($context['shd_filter_fields'][$id_field]);
                }
            }
        }
    }
}
/**
 *	Initialises the helpdesk action log.
 *
 *	This function loads the language strings, and hands off to {@link shd_load_action_log_entries()} to perform the actual log
 *	generation.
 *
 *	Before doing so, however, this function will also prepare for deletion of old entries, as well as sorting out the columns and
 *	ordering rules before handing control to the other function.
 *
 *	@since 1.0
*/
function shd_admin_action_log()
{
    global $context, $settings, $scripturl, $txt, $sourcedir, $smcFunc, $sort_types;
    shd_load_language('sd_language/SimpleDeskLogAction');
    $context['can_delete'] = allowedTo('admin_forum');
    $context['displaypage'] = 30;
    $context['hoursdisable'] = 24;
    $context['waittime'] = time() - $context['hoursdisable'] * 3600;
    // Handle deletion...
    if (isset($_REQUEST['removeall']) && $context['can_delete']) {
        shd_db_query('', '
			DELETE FROM {db_prefix}helpdesk_log_action
			WHERE log_time < {int:twenty_four_hours_wait}', array('twenty_four_hours_wait' => $context['waittime']));
    } elseif (!empty($_REQUEST['remove']) && $context['can_delete']) {
        shd_db_query('', '
			DELETE FROM {db_prefix}helpdesk_log_action
			WHERE id_action = {int:gtfo}
			AND log_time < {int:twenty_four_hours_wait}', array('twenty_four_hours_wait' => $context['waittime'], 'gtfo' => (int) $_REQUEST['remove']));
    }
    // Do the column stuff!
    $sort_types = array('action' => 'la.action', 'time' => 'la.log_time', 'member' => 'mem.real_name', 'position' => 'mg.group_name', 'ip' => 'la.ip');
    // Setup the direction stuff...
    $context['sort'] = isset($_REQUEST['sort']) && isset($sort_types[$_REQUEST['sort']]) ? $sort_types[$_REQUEST['sort']] : $sort_types['time'];
    $context['start'] = isset($_REQUEST['start']) ? $_REQUEST['start'] : 0;
    $context['order'] = isset($_REQUEST['asc']) ? 'ASC' : 'DESC';
    $context['url_sort'] = isset($_REQUEST['sort']) ? ';sort=' . $_REQUEST['sort'] : '';
    $context['url_order'] = isset($_REQUEST['asc']) ? ';asc' : '';
    // Get all action log entries
    $context['actions'] = shd_load_action_log_entries($context['start'], $context['displaypage'], $context['sort'], $context['order']);
    $context['page_index'] = shd_no_expand_pageindex($scripturl . '?action=admin;area=helpdesk_info;sa=actionlog' . $context['url_sort'] . $context['url_order'], $context['start'], shd_count_action_log_entries(), $context['displaypage']);
    $context['sub_template'] = 'shd_action_log';
}