/**
 *	Marks a ticket resolved or unresolved.
 *
 *	This function identifies whether a given ticket is resolved or not, if not resolved, mark it resolved. If it was resolved
 *	reopen the ticket back to an appropriate status based on last respondent. In both cases, the action is logged in the action log.
 *	It is also unassigned from having a user on either closure or reopen.
 *
 *	Accessed through ?action=helpdesk;sa=resolve;ticket=x;sessvar=sessid before redirecting back to the ticket, or add ;home to the
 *	URL to have it redirect back to the home page.
 *
 *	@since 1.0
*/
function shd_ticket_resolve()
{
    global $smcFunc, $user_info, $context, $sourcedir;
    checkSession('get');
    // First, figure out the state of the ticket - is it resolved or not? Can we even see it?
    if (empty($context['ticket_id'])) {
        fatal_lang_error('shd_no_ticket', false);
    }
    $context['shd_return_to'] = isset($_REQUEST['home']) ? 'home' : 'ticket';
    $query = shd_db_query('', '
		SELECT id_member_started, id_member_updated, status, num_replies, subject, id_dept
		FROM {db_prefix}helpdesk_tickets AS hdt
		WHERE {query_see_ticket}
			AND id_ticket = {int:current_ticket}', array('current_ticket' => $context['ticket_id']));
    if ($row = $smcFunc['db_fetch_assoc']($query)) {
        $smcFunc['db_free_result']($query);
        $action = $row['status'] != TICKET_STATUS_CLOSED ? 'resolve' : 'unresolve';
        call_integration_hook('shd_hook_mark' . $action);
        if (!shd_allowed_to('shd_' . $action . '_ticket_any', $row['id_dept']) && (!shd_allowed_to('shd_' . $action . '_ticket_own', $row['id_dept']) || $row['id_member_started'] != $user_info['id'])) {
            fatal_lang_error('shd_cannot_' . $action, false);
        }
        // OK, so what about any children related tickets that are still open? Eeek, could be awkward.
        if (empty($modSettings['shd_disable_relationships'])) {
            $query = shd_db_query('', '
				SELECT COUNT(hdt.id_ticket)
				FROM {db_prefix}helpdesk_relationships AS rel
					INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (rel.secondary_ticket = hdt.id_ticket)
				WHERE rel.primary_ticket = {int:ticket}
					AND rel.rel_type = {int:parent}
					AND hdt.status NOT IN ({array_int:closed_status})', array('ticket' => $context['ticket_id'], 'parent' => RELATIONSHIP_ISPARENT, 'closed_status' => array(TICKET_STATUS_CLOSED, TICKET_STATUS_DELETED)));
            list($count_children) = $smcFunc['db_fetch_row']($query);
            $smcFunc['db_free_result']($query);
            if (!empty($count_children)) {
                fatal_lang_error('error_shd_cannot_resolve_children', false);
            }
        }
        $new = shd_determine_status($action, $row['id_member_started'], $row['id_member_updated'], $row['num_replies'], $row['id_dept']);
        shd_db_query('', '
			UPDATE {db_prefix}helpdesk_tickets
			SET status = {int:status}
			WHERE id_ticket = {int:ticket}', array('status' => $new, 'ticket' => $context['ticket_id']));
        shd_log_action($action, array('ticket' => $context['ticket_id'], 'subject' => $row['subject']));
        shd_clear_active_tickets($row['id_dept']);
        if ($context['shd_return_to'] == 'home') {
            redirectexit($context['shd_home'] . $context['shd_dept_link']);
        } else {
            redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
        }
    } else {
        fatal_lang_error('shd_no_ticket', false);
    }
}
function shd_scheduled_close_tickets()
{
    global $modSettings, $smcFunc, $txt;
    @set_time_limit(600);
    // Ten minutes. Is a big job, possibly.
    // 1. Get the list of tickets.
    $query = $smcFunc['db_query']('', '
		SELECT hdt.id_ticket, hdt.subject, hdt.id_member_started, hdt.id_member_updated, hdt.id_dept
		FROM {db_prefix}helpdesk_depts AS hdd
			INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdd.id_dept = hdt.id_dept)
		WHERE hdd.autoclose_days > 0
			AND hdt.last_updated <= {int:time} - (86400 * hdd.autoclose_days)
			AND hdt.status IN ({array_int:open})', array('open' => array(0, 1, 2), 'time' => time()));
    $tickets = array();
    $subjects = array();
    $members = array();
    $depts = array();
    while ($row = $smcFunc['db_fetch_assoc']($query)) {
        $tickets[$row['id_ticket']] = $row['id_ticket'];
        $subjects[$row['id_ticket']] = $row['subject'];
        $members[] = $row['id_member_started'];
        $members[] = $row['id_member_updated'];
        $depts[$row['id_dept']] = $row['id_dept'];
    }
    $smcFunc['db_free_result']($query);
    // Any to do?
    if (!empty($tickets)) {
        $time = time();
        // 2. Update the tickets.
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}helpdesk_tickets
			SET status = {int:closed},
				last_updated = {int:time}
			WHERE id_ticket IN ({array_int:tickets})', array('closed' => 3, 'tickets' => $tickets, 'time' => $time));
        // 3. Update the log if appropriate. Normally we would call shd_log_action(), however here... we might be doing a lot, so instead, we do it manually ourselves.
        if (empty($modSettings['shd_disable_action_log']) && !empty($modSettings['shd_logopt_autoclose'])) {
            $rows = array();
            foreach ($tickets as $ticket) {
                $rows[] = array($time, 0, '', 'autoclose', $ticket, 0, serialize(array('subject' => $subjects[$ticket], 'auto' => true)));
            }
            $smcFunc['db_insert']('', '{db_prefix}helpdesk_log_action', array('log_time' => 'int', 'id_member' => 'int', 'ip' => 'string-16', 'action' => 'string', 'id_ticket' => 'int', 'id_msg' => 'int', 'extra' => 'string-65534'), $rows, array('id_action'));
        }
        // 4. If caching is enabled, make sure to purge the cache for members so their number of tickets will be recalculated.
        // No need to dump all SD cache items though, though we have to get all those whose tickets were affected, plus all staff.
        $depts = array_flip($depts);
        foreach ($depts as $dept) {
            shd_clear_active_tickets($dept);
        }
    }
}
/**
 *	Updates a ticket/reply item in the database.
 *
 *	This function allows modification of all post/ticket details - and can be used independently; it is possible (and even done
 *	in SimpleDesk) to update just a ticket or just a post from this function. All three parameters are by <b>reference</b>
 *	meaning they WILL be updated if things change. Note that this function is not validating that they are sensible values; it is
 *	up to the calling function to ascertain that.
 *
 *	@param array &$msgOptions - a hash array by reference, stating zero or more details to change on a message (if the change is strictly ticket-only, the entire $msgOptions array can be an empty array):
 *	<ul>
 *	<li>id: Required if changing a message; the principle numeric id of the message to modify</li>
 *	<li>body: Optional, the principle body content of the message to change; if omitted, no change will occur. If set, assumed to have been cleaned already (with $smcFunc['htmlspecialchars'] and preparsecode)</li>
 *	<li>modified: Optional, hash array containing items relating to modification (if 'modified' key exists, all of these should be set)
 *		<ul>
 *			<li>time: Unsigned int timestamp of the change</li>
 *			<li>name: String; user name of the user making the change</li>
 *			<li>id: Unsigned int user id of the user making the change</li>
 *		</ul>
 *	</li>
 *	<li>smileys_enabled: Optional, boolean denoting whether smileys should be active on this post; if omitted no change will occur</li>
 *	<li>attachments: Optional, array of attachment ids that need attaching to this message; if omitted no changes will occur</li>
 *	</ul>
 *
 *	@param array &$ticketOptions - a hash array by reference, stating one or more details necessary to change on a ticket:
 *	<ul>
 *	<li>id: Required in all cases, numeric ticket id that the edit relates to</li>
 *	<li>subject: Optional string with the new subject in; if omitted no change will occur. If set, assumed to have been cleaned already (with $smcFunc['htmlspecialchars'] and strtr)</li>
 *	<li>urgency: Optional integer with the new urgency in; if omitted no change will occur</li>
 *	<li>status: Optional integer with the new status in; if omitted no change will occur</li>
 *	<li>ssigned: Optional integer with the user id of assigned user; if omitted no change will occur (note, you would declare this as 0 to unassign a ticket - set to 0 is not omitted)</li>
 *	<li>private: Optional boolean as to privacy of ticket: true = private, false = not private (note, you still declare this to change it)</li>
 *	</ul>
 *
 *	@param array &$posterOptions - a hash array by reference of details to change on the poster of a message:
 *	<ul>
 *	<li>name: Optional string, name of credited poster (in absence of id, this will be used); if omitted no change will occur</li>
 *	<li>email: Optional string, email address of poster; if omitted no change will occur</li>
 *	</ul>
 *
 *	@return bool True on success, false on failure.
 *	@since 1.0
*/
function shd_modify_ticket_post(&$msgOptions, &$ticketOptions, &$posterOptions)
{
    global $user_info, $txt, $modSettings, $smcFunc, $context;
    $messages_columns = array();
    $ticket_columns = array();
    $ticketOptions['id'] = !empty($ticketOptions['id']) ? (int) $ticketOptions['id'] : 0;
    if ($ticketOptions['id'] == 0) {
        return false;
    }
    if (isset($posterOptions['name'])) {
        $messages_columns['poster_name'] = $posterOptions['name'];
    }
    if (isset($posterOptions['email'])) {
        $messages_columns['poster_email'] = $posterOptions['email'];
    }
    if (isset($msgOptions['body'])) {
        $messages_columns['body'] = $msgOptions['body'];
    }
    if (!empty($msgOptions['modified'])) {
        $messages_columns['modified_time'] = $msgOptions['modified']['time'];
        $messages_columns['modified_name'] = $msgOptions['modified']['name'];
        $messages_columns['modified_member'] = $msgOptions['modified']['id'];
    }
    if (isset($msgOptions['smileys_enabled'])) {
        $messages_columns['smileys_enabled'] = empty($msgOptions['smileys_enabled']) ? 0 : 1;
    }
    if (isset($ticketOptions['subject'])) {
        $ticket_columns['subject'] = $ticketOptions['subject'];
    }
    if (isset($ticketOptions['status'])) {
        $ticket_columns['status'] = $ticketOptions['status'];
    }
    if (isset($ticketOptions['urgency'])) {
        $ticket_columns['urgency'] = $ticketOptions['urgency'];
    }
    if (isset($ticketOptions['assigned'])) {
        $ticket_columns['id_member_assigned'] = $ticketOptions['assigned'];
    }
    if (isset($ticketOptions['private'])) {
        $ticket_columns['private'] = empty($ticketOptions['private']) ? 0 : 1;
    }
    // Fix the attachments.
    if (!empty($msgOptions['attachments'])) {
        $array = array();
        foreach ($msgOptions['attachments'] as $attach) {
            $array[] = array($attach, $msgOptions['id'], $ticketOptions['id']);
        }
        $smcFunc['db_insert']('replace', '{db_prefix}helpdesk_attachments', array('id_attach' => 'int', 'id_msg' => 'int', 'id_ticket' => 'int'), $array, array('id_attach'));
    }
    if (empty($messages_columns) && empty($ticket_columns) && empty($ticketOptions['custom_fields']) && empty($msgOptions['custom_fields'])) {
        return true;
    }
    // It's do or die time: forget any user aborts!
    $previous_ignore_user_abort = ignore_user_abort(true);
    // OK, let's get all this set up ready for SQL magic
    // Which columns need to be ints?
    $messageInts = array('modified_time', 'modified_member', 'smileys_enabled');
    $msg_update_parameters = array('id_msg' => empty($msgOptions['id']) ? 0 : (int) $msgOptions['id']);
    foreach ($messages_columns as $var => $val) {
        $messages_columns[$var] = $var . ' = {' . (in_array($var, $messageInts) ? 'int' : 'string') . ':var_' . $var . '}';
        $msg_update_parameters['var_' . $var] = $val;
    }
    $ticketInts = array('status', 'urgency');
    $ticket_update_parameters = array('id_ticket' => $ticketOptions['id']);
    foreach ($ticket_columns as $var => $val) {
        $ticket_columns[$var] = $var . ' = {' . (in_array($var, $ticketInts) ? 'int' : 'string') . ':var_' . $var . '}';
        $ticket_update_parameters['var_' . $var] = $val;
    }
    // GO GO GO! (message first)
    if (!empty($messages_columns)) {
        shd_db_query('', '
			UPDATE {db_prefix}helpdesk_ticket_replies
			SET ' . implode(', ', $messages_columns) . '
			WHERE id_msg = {int:id_msg}', $msg_update_parameters);
    }
    if (!empty($ticket_columns)) {
        shd_db_query('', '
			UPDATE {db_prefix}helpdesk_tickets
			SET ' . implode(', ', $ticket_columns) . '
			WHERE id_ticket = {int:id_ticket}', $ticket_update_parameters);
    }
    // And fix unread list
    if (!empty($msgOptions['modified'])) {
        shd_db_query('', '
			UPDATE {db_prefix}helpdesk_log_read
			SET id_msg = {int:last_msg}
			WHERE id_member != {int:modified_member}
				AND id_ticket = {int:ticket}
				AND id_msg >= {int:edited_msg}', array('last_msg' => $msg_update_parameters['id_msg'] - 1, 'modified_member' => $msgOptions['modified']['id'], 'ticket' => $ticketOptions['id'], 'edited_msg' => $msg_update_parameters['id_msg']));
    }
    // Are we updating custom fields?
    $rows = array();
    $context['custom_fields_updated'] = array();
    if (!empty($ticketOptions['custom_fields'])) {
        // Some may be pre-existing, some may need purging.
        foreach ($ticketOptions['custom_fields'] as $field_id => $field) {
            // No new value, or new value is the same as the old one.
            if (!isset($field['new_value']) || isset($field['value']) && $field['value'] == $field['new_value']) {
                continue;
            }
            // So we have a new value, or a changed value. If it's changed, but changed to default, mark it as default
            $rows[] = array('id_post' => $ticketOptions['id'], 'id_field' => $field_id, 'value' => $field['new_value'], 'post_type' => CFIELD_TICKET);
            if ($field['type'] == CFIELD_TYPE_MULTI) {
                $values = array();
                if (!empty($field['value'])) {
                    foreach ($field['value'] as $value) {
                        if (!empty($value)) {
                            $values[] = $field['options'][$value];
                        }
                    }
                }
                $oldvalue = implode(', ', $values);
                $values = array();
                $newvalues = explode(',', $field['new_value']);
                foreach ($newvalues as $value) {
                    if (!empty($value)) {
                        $values[] = $field['options'][$value];
                    }
                }
                $newvalue = implode(', ', $values);
            } else {
                $oldvalue = !empty($field['value']) && ($field['type'] == CFIELD_TYPE_RADIO || $field['type'] == CFIELD_TYPE_SELECT) ? $field['options'][$field['value']] : (empty($field['value']) ? '' : $field['value']);
                $newvalue = !empty($field['new_value']) && ($field['type'] == CFIELD_TYPE_RADIO || $field['type'] == CFIELD_TYPE_SELECT) ? $field['options'][$field['new_value']] : $field['new_value'];
            }
            $context['custom_fields_updated'][] = array('ticket' => $ticketOptions['id'], 'fieldname' => $field['name'], 'oldvalue' => $oldvalue, 'newvalue' => $newvalue, 'scope' => CFIELD_TICKET, 'visible' => $field['visible'], 'fieldtype' => $field['type']);
            if ($field['new_value'] == $field['default_value']) {
                $context['custom_fields_updated'][count($context['custom_fields_updated']) - 1]['default'] = true;
            }
        }
    }
    // Same deal, this time for message options. See above for comments.
    if (!empty($msgOptions['custom_fields'])) {
        foreach ($msgOptions['custom_fields'] as $field_id => $field) {
            if (!isset($field['new_value']) || isset($field['value']) && $field['value'] == $field['new_value']) {
                continue;
            }
            $rows[] = array('id_post' => $msgOptions['id'], 'id_field' => $field_id, 'value' => $field['new_value'], 'post_type' => CFIELD_REPLY);
            if ($field['type'] == CFIELD_TYPE_MULTI) {
                $values = array();
                if (!empty($field['value'])) {
                    foreach ($field['value'] as $value) {
                        if (!empty($value)) {
                            $values[] = $field['options'][$value];
                        }
                    }
                }
                $oldvalue = implode(', ', $values);
                $values = array();
                $newvalues = explode(',', $field['new_value']);
                foreach ($newvalues as $value) {
                    if (!empty($value)) {
                        $values[] = $field['options'][$value];
                    }
                }
                $newvalue = implode(', ', $values);
            } else {
                $oldvalue = !empty($field['value']) && ($field['type'] == CFIELD_TYPE_RADIO || $field['type'] == CFIELD_TYPE_SELECT) ? $field['options'][$field['value']] : (empty($field['value']) ? $field['type'] != CFIELD_TYPE_LARGETEXT ? $field['default_value'] : '' : $field['value']);
                $newvalue = !empty($field['new_value']) && ($field['type'] == CFIELD_TYPE_RADIO || $field['type'] == CFIELD_TYPE_SELECT) ? $field['options'][$field['new_value']] : $field['new_value'];
            }
            if ($oldvalue != $newvalue) {
                $context['custom_fields_updated'][] = array('ticket' => $ticketOptions['id'], 'msg' => $msgOptions['id'], 'fieldname' => $field['name'], 'oldvalue' => $oldvalue, 'newvalue' => $newvalue, 'scope' => CFIELD_REPLY, 'visible' => $field['visible'], 'fieldtype' => $field['type']);
                if ($field['new_value'] == $field['default_value']) {
                    $context['custom_fields_updated'][count($context['custom_fields_updated']) - 1]['default'] = true;
                }
            }
        }
    }
    // If there are rows to add or update, commence.
    if (!empty($rows)) {
        $smcFunc['db_insert']('replace', '{db_prefix}helpdesk_custom_fields_values', array('id_post' => 'int', 'id_field' => 'int', 'value' => 'string-65534', 'post_type' => 'int'), $rows, array('id_post', 'id_field'));
    }
    // Int hook
    call_integration_hook('shd_hook_modpost', array(&$msgOptions, &$ticketOptions, &$posterOptions));
    ignore_user_abort($previous_ignore_user_abort);
    if (empty($ticketOptions['dept']) && !empty($ticketOptions['id'])) {
        // So we're making a reply, we need the department id. The ticket will already exist - we just added to it!
        $query = $smcFunc['db_query']('', '
			SELECT id_dept
			FROM {db_prefix}helpdesk_tickets
			WHERE id_ticket = {int:id_ticket}', array('id_ticket' => $ticketOptions['id']));
        list($ticketOptions['dept']) = $smcFunc['db_fetch_row']($query);
        $smcFunc['db_free_result']($query);
    }
    if (!empty($ticketOptions['dept'])) {
        shd_clear_active_tickets($ticketOptions['dept']);
    }
    // Success.
    return true;
}
/**
 *	Receives the form for moving tickets to topics, and actually handles the move.
 *
 *	After checking permissions, and so on, begin to actually move posts.
 *
 *	This is done by invoking SMF's createPost function to make the new thread and repost all the ticket's posts as new thread posts
 *	using defaults for some settings.
 *
 *	Operations:
 *	- check the user can see the board they are linking to
 *	- move the ticket's text as the opening post
 *	- update the ticket row if the post was modified before
 *	- get the rest of the replies
 *	- step through and post, updating for modified details
 *	- send the notification PM if we're doing that
 *	- update the attachments table
 *	- update the action log
 *	- remove the ticket from the DB
 *
 *	@see shd_tickettotopic()
 *	@since 1.0
*/
function shd_tickettotopic2()
{
    global $smcFunc, $context, $txt, $modSettings, $scripturl, $sourcedir;
    checkSession();
    checkSubmitOnce('check');
    if (empty($context['ticket_id'])) {
        fatal_lang_error('shd_no_ticket');
    }
    if (isset($_POST['send_pm']) && (!isset($_POST['pm_content']) || trim($_POST['pm_content']) == '')) {
        checkSubmitOnce('free');
        fatal_lang_error('shd_move_no_pm', false);
    }
    // Just in case, are they cancelling?
    if (isset($_REQUEST['cancel'])) {
        redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
    }
    require_once $sourcedir . '/Subs-Post.php';
    // The destination board must be numeric.
    $_POST['toboard'] = (int) $_POST['toboard'];
    $msg_assoc = array();
    // This is complex, very complex. Hopefully 5 minutes will be enough...
    @set_time_limit(300);
    // Make sure they can see the board they are trying to move to (and get whether posts count in the target board).
    $request = shd_db_query('', '
		SELECT b.count_posts, b.name, hdt.subject, hdt.id_member_started, hdtr.body, hdt.id_first_msg, hdtr.smileys_enabled,
		hdtr.modified_time, hdtr.modified_name, hdtr.poster_time, hdtr.id_msg, hdt.deleted_replies, hdt.id_dept
		FROM {db_prefix}boards AS b
			INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdt.id_ticket = {int:ticket})
			INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdtr.id_msg = hdt.id_first_msg)
		WHERE {query_see_board}
			AND {query_see_ticket}
			AND b.id_board = {int:to_board}
			AND b.redirect = {string:blank_redirect}
		LIMIT 1', array('ticket' => $context['ticket_id'], 'to_board' => $_POST['toboard'], 'blank_redirect' => ''));
    if ($smcFunc['db_num_rows']($request) == 0) {
        fatal_lang_error('no_board');
    } else {
        list($pcounter, $board_name, $subject, $owner, $body, $firstmsg, $smileys_enabled, $modified_time, $modified_name, $time, $shd_id_msg, $deleted_replies, $dept) = $smcFunc['db_fetch_row']($request);
    }
    $smcFunc['db_free_result']($request);
    if (!shd_allowed_to('shd_ticket_to_topic', $dept) || !empty($modSettings['shd_helpdesk_only']) || !empty($modSettings['shd_disable_tickettotopic'])) {
        fatal_lang_error('shd_cannot_move_ticket', false);
    }
    // Are we changing the subject?
    $old_subject = $subject;
    $subject = !empty($_POST['change_subject']) && !empty($_POST['subject']) ? $_POST['subject'] : $subject;
    $context['deleted_prompt'] = false;
    // Hang on... are there any deleted replies?
    if ($deleted_replies > 0) {
        if (shd_allowed_to('shd_access_recyclebin')) {
            $dr_opts = array('abort', 'delete', 'undelete');
            $context['deleted_prompt'] = isset($_REQUEST['deleted_replies']) && in_array($_REQUEST['deleted_replies'], $dr_opts) ? $_REQUEST['deleted_replies'] : 'abort';
        } else {
            fatal_lang_error('shd_cannot_move_ticket_with_deleted');
        }
    }
    if (!empty($context['deleted_prompt']) && $context['deleted_prompt'] == 'abort') {
        redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id'] . ';recycle');
    }
    // Now the madness that is custom fields. First, load the custom fields we might/will be using.
    $query = $smcFunc['db_query']('', '
		SELECT hdcf.id_field, hdcf.field_name, hdcf.field_order, hdcf.field_type, hdcf.can_see, hdcf.field_options, hdcf.bbc, hdcf.placement
		FROM {db_prefix}helpdesk_custom_fields_depts AS hdd
			INNER JOIN {db_prefix}helpdesk_custom_fields AS hdcf ON (hdd.id_field = hdcf.id_field)
		WHERE hdd.id_dept = {int:dept}
			AND hdcf.active = {int:active}
		ORDER BY hdcf.field_order', array('dept' => $dept, 'active' => 1));
    $context['custom_fields'] = array();
    $is_staff = shd_allowed_to('shd_staff', $dept);
    $is_admin = shd_allowed_to('admin_helpdesk', $dept);
    while ($row = $smcFunc['db_fetch_assoc']($query)) {
        list($user_see, $staff_see) = explode(',', $row['can_see']);
        $context['custom_fields'][$row['id_field']] = array('id_field' => $row['id_field'], 'name' => $row['field_name'], 'type' => $row['field_type'], 'bbc' => !empty($row['bbc']), 'options' => !empty($row['field_options']) ? unserialize($row['field_options']) : array(), 'placement' => $row['placement'], 'visible' => array('user' => $user_see, 'staff' => $staff_see, 'admin' => true), 'values' => array());
    }
    $smcFunc['db_free_result']($query);
    // Having got all the possible fields for this ticket, let's fetch the values for it. That way if we don't have any values for a field, we don't have to care about showing the user.
    // But first, we need all the message ids.
    $context['ticket_messages'] = array();
    $query = $smcFunc['db_query']('', '
		SELECT id_msg
		FROM {db_prefix}helpdesk_ticket_replies AS hdtr
		WHERE id_ticket = {int:ticket}', array('ticket' => $context['ticket_id']));
    while ($row = $smcFunc['db_fetch_row']($query)) {
        $context['ticket_messages'][] = $row[0];
    }
    $smcFunc['db_free_result']($query);
    // Now get a reference for the field values.
    $query = $smcFunc['db_query']('', '
		SELECT cfv.id_post, cfv.id_field, cfv.post_type, cfv.value
		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']));
    while ($row = $smcFunc['db_fetch_assoc']($query)) {
        if (isset($context['custom_fields'][$row['id_field']])) {
            $context['custom_fields'][$row['id_field']]['values'][$row['post_type']][$row['id_post']] = $row['value'];
        }
    }
    $smcFunc['db_free_result']($query);
    // Having now established what fields we do actually have values for, let's proceed to deal with them.
    foreach ($context['custom_fields'] as $field_id => $field) {
        // Didn't we have any values? If not, prune it, not interested.
        if (empty($field['values'])) {
            unset($context['custom_fields'][$field_id]);
        }
        // If the user is an administrator, they can always see the fields.
        if ($is_admin) {
            // But users might not be able to, in which case we need to validate that actually, they did need to authorise this one.
            if (!$field['visible']['user'] || !$field['visible']['staff']) {
                $context['custom_fields_warning'] = true;
            }
        } elseif ($is_staff) {
            // So they're staff. But the field might not be visible to them; they can't deal with it whatever.
            if (!$field['visible']['staff']) {
                fatal_lang_error('cannot_shd_move_ticket_topic_hidden_cfs', false);
            } elseif (!$field['visible']['user']) {
                $context['custom_fields_warning'] = true;
            }
        } else {
            // Non staff aren't special. They should not be able to make this decision. If someone can't see it, they don't get to make the choice.
            if (!$field['visible']['user'] || !$field['visible']['staff']) {
                fatal_lang_error('cannot_shd_move_ticket_topic_hidden_cfs', false);
            }
        }
        // Are we ignoring this field? If so, we can now safely get rid of it at this very point.
        if (isset($_POST['field' . $field_id]) && $_POST['field' . $field_id] == 'lose') {
            unset($context['custom_fields'][$field_id]);
        }
    }
    // Were there any special fields? We need to check - and check that they ticked the right box!
    if (!empty($context['custom_fields_warning']) && empty($_POST['accept_move'])) {
        checkSubmitOnce('free');
        fatal_lang_error('shd_ticket_move_reqd_nonselected', false);
    }
    // Just before we do this, make sure we call any hooks. $context has lots of interesting things, as does $_POST.
    call_integration_hook('shd_hook_tickettotopic');
    // OK, so we have some fields, and we're doing something with them. First we need to attach the fields from the ticket to the opening post.
    shd_append_custom_fields($body, $context['ticket_id'], CFIELD_TICKET);
    // All okay, it seems. Let's go create the topic.
    $msgOptions = array('subject' => $subject, 'body' => $body, 'icon' => 'xx', 'smileys_enabled' => !empty($smileys_enabled) ? 1 : 0);
    $topicOptions = array('board' => $_POST['toboard'], 'lock_mode' => 0, 'mark_as_read' => false);
    $posterOptions = array('id' => $owner, 'update_post_count' => empty($pcounter));
    createPost($msgOptions, $topicOptions, $posterOptions);
    // Keep track of SHD msg id to SMF msg id
    $msg_assoc[$shd_id_msg] = $msgOptions['id'];
    // createPost() doesn't handle modified time and name, so we'll fix that here, along with the poster time.
    if (!empty($modified_time)) {
        shd_db_query('', '
			UPDATE {db_prefix}messages
			SET
				modified_time = {int:modified_time},
				modified_name = {string:modified_name},
				poster_time = {int:poster_time}
			WHERE id_msg = {int:id_msg}', array('id_msg' => $firstmsg, 'modified_time' => $modified_time, 'modified_name' => $modified_name, 'poster_time' => $time));
    }
    // Topic created, let's dig out the replies and post them in the topic, if there are any.
    if (isset($topicOptions['id'])) {
        $request = shd_db_query('', '
			SELECT body, id_member, poster_time, poster_name, poster_email, poster_ip, smileys_enabled,
				modified_time, modified_member, modified_name, poster_time, id_msg, message_status
			FROM {db_prefix}helpdesk_ticket_replies AS hdtr
			WHERE id_ticket = {int:ticket}
				AND id_msg > {int:ticket_msg}
			ORDER BY id_msg', array('ticket' => $context['ticket_id'], 'ticket_msg' => $firstmsg));
        // The ID of the topic we created
        $topic = $topicOptions['id'];
        if ($smcFunc['db_num_rows']($request) != 0) {
            // Now loop through each reply and post it.  Hopefully there aren't too many. *looks at clock*
            while ($row = $smcFunc['db_fetch_assoc']($request)) {
                if ($row['message_status'] == MSG_STATUS_DELETED && !empty($context['deleted_prompt']) && $context['deleted_prompt'] == 'delete') {
                    // we don't want these replies!
                    continue;
                }
                shd_append_custom_fields($row['body'], $row['id_msg'], CFIELD_REPLY);
                $msgOptions = array('subject' => $txt['response_prefix'] . $subject, 'body' => $row['body'], 'icon' => 'xx', 'smileys_enabled' => !empty($row['smileys_enabled']) ? 1 : 0);
                $topicOptions = array('id' => $topic, 'board' => $_POST['toboard'], 'lock_mode' => 0, 'mark_as_read' => false);
                $posterOptions = array('id' => $row['id_member'], 'name' => !empty($row['poster_name']) ? $row['poster_name'] : '', 'email' => !empty($row['poster_email']) ? $row['poster_email'] : '', 'ip' => !empty($row['poster_ip']) ? $row['poster_ip'] : '', 'update_post_count' => empty($pcounter));
                createPost($msgOptions, $topicOptions, $posterOptions);
                // Don't forget to note what id
                $msg_assoc[$row['id_msg']] = $msgOptions['id'];
                // Meh, createPost() doesn't have any hooks for modified time and user. Let's fix that now.
                if (!empty($row['modified_time'])) {
                    shd_db_query('', '
						UPDATE {db_prefix}messages
						SET
							modified_time = {int:modified_time},
							modified_name = {string:modified_name},
							poster_time = {int:poster_time}
						WHERE id_msg = {int:id_msg}', array('id_msg' => $msgOptions['id'], 'modified_time' => $row['modified_time'], 'modified_name' => $row['modified_name'], 'poster_time' => $row['poster_time']));
                }
            }
        }
        // Topic: check; Replies: check; Notfiy the ticket starter, if desired.
        if (isset($_POST['send_pm'])) {
            $request = shd_db_query('pm_find_username', '
				SELECT id_member, real_name
				FROM {db_prefix}members
				WHERE id_member = {int:user}
				LIMIT 1', array('user' => $owner));
            list($userid, $username) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
            // Fix the content
            $replacements = array('{user}' => $username, '{subject}' => $old_subject, '{board}' => '[url=' . $scripturl . '?board=' . $_POST['toboard'] . '.0]' . $board_name . '[/url]', '{link}' => $scripturl . '?topic=' . $topic . '.0');
            $message = str_replace(array_keys($replacements), array_values($replacements), $_POST['pm_content']);
            $recipients = array('to' => array($owner), 'bcc' => array());
            sendpm($recipients, $txt['shd_ticket_moved_subject'], un_htmlspecialchars($message));
        }
        // Right, time to do all the attachment fussing too
        if (!empty($msg_assoc)) {
            $attachIDs = array();
            $query = shd_db_query('', '
				SELECT id_attach, id_msg
				FROM {db_prefix}helpdesk_attachments
				WHERE id_msg IN ({array_int:msgs})', array('msgs' => array_keys($msg_assoc)));
            while ($row = $smcFunc['db_fetch_assoc']($query)) {
                $attachIDs[] = $row;
            }
            $smcFunc['db_free_result']($query);
            if (!empty($attachIDs)) {
                // 1. Update all the attachments in the master table; this is the bit that hurts since it can't be done without
                // a query per row :(
                foreach ($attachIDs as $attach) {
                    shd_db_query('', '
						UPDATE {db_prefix}attachments
						SET id_msg = {int:new_msg}
						WHERE id_attach = {int:attach}', array('attach' => $attach['id_attach'], 'new_msg' => $msg_assoc[$attach['id_msg']]));
                }
                // 2. Remove the entries from the SD table since we don't need them no more
                shd_db_query('', '
					DELETE FROM {db_prefix}helpdesk_attachments
					WHERE id_msg IN ({array_int:msgs})', array('msgs' => array_keys($msg_assoc)));
            }
        }
        // Well, it's possible there are some phantom attachments on deleted replies that need to disappear
        if (!empty($context['deleted_replies']) && $context['deleted_replies'] == 'delete') {
            $query = shd_db_query('', '
				SELECT id_msg
				FROM {db_prefix}helpdesk_attachments AS hda
					INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hda.id_msg = hdtr.id_msg)
				WHERE id_ticket = {int:ticket}
					AND hdtr.message_status = {int:deleted}', array('ticket' => $context['ticket_id'], 'deleted' => MSG_STATUS_DELETED));
            if ($smcFunc['db_num_rows']($query) > 0) {
                $msgs = array();
                while ($row = $smcFunc['db_fetch_row']($query)) {
                    $msgs[] = $row[0];
                }
                if (!empty($msgs)) {
                    // Get rid of the parents
                    require_once $sourcedir . '/ManageAttachments.php';
                    $attachmentQuery = array('attachment_type' => 0, 'id_msg' => 0, 'id_attach' => $msgs);
                    removeAttachments($attachmentQuery);
                    // Get rid of the remainder in hda table
                    shd_db_query('', '
						DELETE FROM {db_prefix}helpdesk_attachments
						WHERE id_ticket = {int:ticket}', array('ticket' => $context['ticket_id']));
                }
            }
            $smcFunc['db_free_result']($query);
        }
        // Now we'll add this to the log.
        $log_params = array('subject' => $subject, 'board_id' => $_POST['toboard'], 'board_name' => $board_name, 'ticket' => $topic);
        shd_log_action('tickettotopic', $log_params);
        // Lastly, delete the ticket from the database.
        shd_db_query('', '
			DELETE FROM {db_prefix}helpdesk_tickets
			WHERE id_ticket = {int:ticket}
			LIMIT 1', array('ticket' => $context['ticket_id']));
        // And the replies, too.
        shd_db_query('', '
			DELETE FROM {db_prefix}helpdesk_ticket_replies
			WHERE id_ticket = {int:ticket}', array('ticket' => $context['ticket_id']));
        // And custom fields.
        shd_db_query('', '
			DELETE FROM {db_prefix}helpdesk_custom_fields_values
			WHERE (id_post = {int:ticket} AND post_type = 1)' . (!empty($context['ticket_messages']) ? '
				OR (id_post IN ({array_int:msgs}) AND post_type = 2)' : ''), array('ticket' => $context['ticket_id'], 'msgs' => $context['ticket_messages']));
    } else {
        fatal_lang_error('shd_move_topic_not_created', false);
    }
    // Clear our cache
    shd_clear_active_tickets($dept);
    // Send them to the topic.
    redirectexit('topic=' . $topic . '.0');
}
/**
 *	Handles AJAX updates to privacy.
 *
 *	Receives a request via ?action=helpdesk;sa=ajax;op=privacy to flip the privacy setting.
 *
 *	Operations:
 *	- silent session check; if fail, add to $context['ajax_return']['error']
 *	- ticket id check; if fail, add to $context['ajax_return']['error']
 *	- get enough ticket data to do this
 *	- check if the ticket is not able to be updated (either is closed/deleted, or insufficient permissions); if fail add to $context['ajax_return']['error']
 *	- switch privacy, update database
 *	- clear the cache of tickets for the Helpdesk menu item
 *	- return $context['ajax_return']['message'] as the new privacy item
*/
function shd_ajax_privacy()
{
    global $smcFunc, $user_info, $context, $txt, $sourcedir;
    $session_check = checkSession('get', '', false);
    // check the session but don't die fatally.
    if (!empty($session_check)) {
        $context['ajax_return'] = array('error' => $txt[$session_check]);
        return;
    }
    // First, figure out the state of the ticket - is it private or not? Can we even see it?
    if (empty($context['ticket_id'])) {
        $context['ajax_return'] = array('error' => $txt['shd_no_ticket']);
        return;
    }
    $query = shd_db_query('', '
		SELECT id_member_started, subject, private, status, id_dept
		FROM {db_prefix}helpdesk_tickets AS hdt
		WHERE {query_see_ticket}
			AND id_ticket = {int:current_ticket}', array('current_ticket' => $context['ticket_id']));
    if ($row = $smcFunc['db_fetch_assoc']($query)) {
        if (in_array($row['status'], array(TICKET_STATUS_CLOSED, TICKET_STATUS_DELETED)) || !shd_allowed_to('shd_alter_privacy_any', $row['id_dept']) && (!shd_allowed_to('shd_alter_privacy_own', $row['id_dept']) || $row['id_member_started'] != $user_info['id'])) {
            $context['ajax_return'] = array('error' => $txt['shd_cannot_change_privacy']);
            return;
        }
        $smcFunc['db_free_result']($query);
        $new = empty($row['private']) ? 1 : 0;
        $action = empty($row['private']) ? 'markprivate' : 'marknotprivate';
        require_once $sourcedir . '/sd_source/Subs-SimpleDeskPost.php';
        $msgOptions = array();
        $posterOptions = array();
        $ticketOptions = array('id' => $context['ticket_id'], 'private' => empty($row['private']));
        shd_modify_ticket_post($msgOptions, $ticketOptions, $posterOptions);
        shd_log_action($action, array('ticket' => $context['ticket_id'], 'subject' => $row['subject']));
        // Make sure we recalculate the number of tickets on next page load
        shd_clear_active_tickets($row['id_dept']);
        $context['ajax_return'] = array('message' => $new ? $txt['shd_ticket_private'] : $txt['shd_ticket_notprivate']);
    } else {
        $context['ajax_return'] = array('error' => $txt['shd_no_ticket']);
        return;
    }
}
/**
 *	Handles the actual assignment form, validates it and carries it out.
 *
 *	Primarily this is just about receiving the form, making the same checks that {@link shd_movedept()} does and then
 *	logging the action before updating the database.
 *
 *	@see shd_movedept()
 *	@since 2.0
*/
function shd_movedept2()
{
    global $context, $smcFunc, $user_info, $sourcedir, $txt, $scripturl;
    checkSession();
    checkSubmitOnce('check');
    if (empty($context['ticket_id'])) {
        fatal_lang_error('shd_no_ticket', false);
    }
    if (isset($_POST['send_pm']) && (!isset($_POST['pm_content']) || trim($_POST['pm_content']) == '') && (empty($modSettings['shd_helpdesk_only']) || empty($modSettings['shd_disable_pm']))) {
        fatal_lang_error('shd_move_no_pm', false);
    }
    // Just in case, are they cancelling?
    if (isset($_REQUEST['cancel'])) {
        redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
    }
    if (empty($context['shd_multi_dept'])) {
        fatal_lang_error('shd_cannot_move_dept', false);
    }
    $dest = isset($_REQUEST['to_dept']) ? (int) $_REQUEST['to_dept'] : 0;
    if (empty($dest) || !shd_allowed_to('access_helpdesk', $dest)) {
        fatal_lang_error('shd_cannot_move_dept', false);
    }
    $context['shd_return_to'] = isset($_REQUEST['home']) ? 'home' : 'ticket';
    // Get ticket details - and kick it out if they shouldn't be able to see it.
    $query = shd_db_query('', '
		SELECT id_member_started, subject, hdt.id_dept, dept_name
		FROM {db_prefix}helpdesk_tickets AS hdt
			INNER JOIN {db_prefix}helpdesk_depts AS hdd ON (hdt.id_dept = hdd.id_dept)
		WHERE {query_see_ticket} AND id_ticket = {int:ticket}', array('ticket' => $context['ticket_id']));
    $log_params = array();
    if ($row = $smcFunc['db_fetch_row']($query)) {
        list($ticket_starter, $subject, $context['current_dept'], $context['current_dept_name']) = $row;
    } else {
        $smcFunc['db_free_result']($query);
        fatal_lang_error('shd_no_ticket');
    }
    $smcFunc['db_free_result']($query);
    if ($context['current_dept'] == $dest) {
        fatal_lang_error('shd_cannot_move_dept', false);
    }
    if (shd_allowed_to('shd_move_dept_any', $context['current_dept']) || shd_allowed_to('shd_move_dept_own', $context['current_dept']) && $ticket_starter == $user_info['id']) {
        // Find the new department. We've already established the user can see it, but we need its name.
        $query = $smcFunc['db_query']('', '
			SELECT id_dept, dept_name
			FROM {db_prefix}helpdesk_depts
			WHERE id_dept IN ({int:dest})', array('dest' => $dest));
        list($new_dept, $dept_name) = $smcFunc['db_fetch_row']($query);
        $smcFunc['db_free_result']($query);
        // Just before we move, call any interesting hooks. We do normally have a lot of fun staff in $context and $_POST, but the department ID and name aren't in either.
        call_integration_hook('shd_hook_movedept', array(&$new_dept, &$dept_name));
        $log_params = array('subject' => $subject, 'ticket' => $context['ticket_id'], 'old_dept_id' => $context['current_dept'], 'old_dept_name' => $context['current_dept_name'], 'new_dept_id' => $new_dept, 'new_dept_name' => $dept_name);
        shd_log_action('move_dept', $log_params);
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}helpdesk_tickets
			SET id_dept = {int:new_dept}
			WHERE id_ticket = {int:ticket}', array('new_dept' => $new_dept, 'ticket' => $context['ticket_id']));
        // Now, notify the ticket starter if that's what we wanted to do.
        if (isset($_POST['send_pm'])) {
            require_once $sourcedir . '/Subs-Post.php';
            $request = shd_db_query('pm_find_username', '
				SELECT id_member, real_name
				FROM {db_prefix}members
				WHERE id_member = {int:user}
				LIMIT 1', array('user' => $ticket_starter));
            list($userid, $username) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
            // Fix the content
            $replacements = array('{user}' => $username, '{subject}' => $subject, '{current_dept}' => $context['current_dept_name'], '{new_dept}' => $dept_name, '{link}' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
            $message = str_replace(array_keys($replacements), array_values($replacements), $_POST['pm_content']);
            $recipients = array('to' => array($ticket_starter), 'bcc' => array());
            sendpm($recipients, $txt['shd_ticket_moved_subject'], un_htmlspecialchars($message));
        }
        shd_clear_active_tickets($context['current_dept']);
        shd_clear_active_tickets($new_dept);
        if (!empty($context['shd_return_to']) && $context['shd_return_to'] == 'home') {
            redirectexit($context['shd_home'] . ';dept=' . $new_dept);
        } else {
            redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
        }
    } else {
        fatal_lang_error('shd_no_perm_move_dept', false);
    }
}
Example #7
0
function shd_reply_restore()
{
    global $smcFunc, $user_info, $context, $sourcedir;
    checkSession('get');
    $_REQUEST['reply'] = empty($_REQUEST['reply']) ? 0 : (int) $_REQUEST['reply'];
    if (empty($_REQUEST['reply'])) {
        fatal_lang_error('shd_no_ticket', false);
    }
    // Check we can actually see the ticket we're restoring from, and that we can restore this reply
    $query_ticket = shd_db_query('', '
		SELECT hdt.id_ticket, hdt.id_dept, hdtr.id_member, hdt.id_member_started, hdt.id_member_updated, hdt.num_replies, hdt.subject, hdt.status, hdtr.message_status
		FROM {db_prefix}helpdesk_tickets AS hdt
			INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdt.id_ticket = hdtr.id_ticket)
		WHERE {query_see_ticket}
			AND hdtr.id_msg = {int:reply}
			AND hdt.id_first_msg != {int:reply2}', array('reply' => $_REQUEST['reply'], 'reply2' => $_REQUEST['reply']));
    if ($row = $smcFunc['db_fetch_assoc']($query_ticket)) {
        $smcFunc['db_free_result']($query_ticket);
        if ($row['status'] == TICKET_STATUS_DELETED || $row['status'] == TICKET_STATUS_CLOSED || $row['message_status'] != MSG_STATUS_DELETED || !shd_allowed_to('shd_restore_reply_any', $row['id_dept']) && (!shd_allowed_to('shd_restore_reply_own', $row['id_dept']) || $user_info['id'] != $row['id_member'])) {
            fatal_lang_error('shd_cannot_restore_reply', false);
        }
        $context['ticket_id'] = (int) $row['id_ticket'];
        $subject = $row['subject'];
        $starter = $row['id_member_started'];
        $replier = $row['id_member_updated'];
        $num_replies = $row['num_replies'];
    } else {
        $smcFunc['db_free_result']($query_ticket);
        fatal_lang_error('shd_no_ticket', false);
    }
    // The ticket's id is in $context['ticket_id'], the reply id in $_REQUEST['reply'].
    call_integration_hook('shd_hook_restorereply');
    // OK, let's clear this one, hasta la vista... ticket.
    shd_db_query('', '
		UPDATE {db_prefix}helpdesk_ticket_replies
		SET message_status = {int:msg_status_deleted}
		WHERE id_msg = {int:reply}', array('msg_status_deleted' => MSG_STATUS_NORMAL, 'reply' => $_REQUEST['reply']));
    // Captain's Log, stardate 18.3.10.1010
    shd_log_action('restore_reply', array('ticket' => $context['ticket_id'], 'subject' => $subject, 'msg' => $_REQUEST['reply']));
    // Fix the topic data
    list($starter, $replier, $num_replies) = shd_recalc_ids($context['ticket_id']);
    $query_reply = shd_db_query('', '
		UPDATE {db_prefix}helpdesk_tickets
		SET status = {int:status}
		WHERE id_ticket = {int:ticket}', array('ticket' => $context['ticket_id'], 'status' => shd_determine_status('restorereply', $starter, $replier, $num_replies, $row['id_dept'])));
    // Expire the cache of count(active tickets)
    shd_clear_active_tickets($row['id_dept']);
    redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
}
function shd_admin_maint_massdeptmove()
{
    global $context, $txt, $smcFunc, $sourcedir;
    checkSession('request');
    $context['page_title'] = $txt['shd_admin_maint_massdeptmove'];
    $depts = shd_allowed_to('access_helpdesk', false);
    $_POST['id_dept_from'] = isset($_POST['id_dept_from']) ? (int) $_POST['id_dept_from'] : 0;
    $_POST['id_dept_to'] = isset($_POST['id_dept_to']) ? (int) $_POST['id_dept_to'] : 0;
    if ($_POST['id_dept_from'] == 0 || $_POST['id_dept_to'] == 0 || !in_array($_POST['id_dept_from'], $depts) || !in_array($_POST['id_dept_to'], $depts)) {
        fatal_lang_error('shd_unknown_dept', false);
    } elseif ($_POST['id_dept_from'] == $_POST['id_dept_to']) {
        fatal_lang_error('shd_admin_maint_massdeptmove_samedept', false);
    }
    $clauses = array();
    if (empty($_POST['moveopen'])) {
        $clauses[] = 'AND status NOT IN (' . implode(',', array(TICKET_STATUS_NEW, TICKET_STATUS_PENDING_USER, TICKET_STATUS_PENDING_STAFF, TICKET_STATUS_WITH_SUPERVISOR, TICKET_STATUS_ESCALATED)) . ')';
    }
    if (empty($_POST['moveclosed'])) {
        $clauses[] = 'AND status != ' . TICKET_STATUS_CLOSED;
    }
    if (empty($_POST['movedeleted'])) {
        $clauses[] = 'AND status != ' . TICKET_STATUS_DELETED;
    }
    $_POST['movelast_less_days'] = isset($_POST['movelast_less_days']) && !empty($_POST['movelast_less']) ? (int) $_POST['movelast_less_days'] : 0;
    if ($_POST['movelast_less_days'] > 0) {
        $clauses[] = 'AND last_updated >= ' . (time() - $_POST['movelast_less_days'] * 86400);
    }
    $_POST['movelast_more_days'] = isset($_POST['movelast_more_days']) && !empty($_POST['movelast_more']) ? (int) $_POST['movelast_more_days'] : 0;
    if ($_POST['movelast_more_days'] > 0) {
        $clauses[] = 'AND last_updated < ' . (time() - $_POST['movelast_more_days'] * 86400);
    }
    // OK, let's start. How many tickets are there to move?
    if (empty($_POST['massdeptmove'])) {
        $query = $smcFunc['db_query']('', '
			SELECT COUNT(*)
			FROM {db_prefix}helpdesk_tickets
			WHERE id_dept = {int:dept_from} ' . implode(' ', $clauses), array('dept_from' => $_POST['id_dept_from']));
        list($count) = $smcFunc['db_fetch_row']($query);
        $smcFunc['db_free_result']($query);
        if (!empty($count)) {
            $_POST['massdeptmove'] = $count;
        } else {
            $_GET['done'] = true;
        }
    }
    // OK, so we know we're going to be doing some tickets, or do we?
    $_POST['tickets_done'] = isset($_POST['tickets_done']) ? (int) $_POST['tickets_done'] : 0;
    if (isset($_GET['done']) || $_POST['tickets_done'] >= $_POST['massdeptmove']) {
        $context['sub_template'] = 'shd_admin_maint_massdeptmovedone';
        return;
    }
    // So, do this batch.
    $step_count = 10;
    $tickets = array();
    // We don't need to get particularly clever; whatever tickets we did in any previous batch, well, they will be gone by now.
    $query = $smcFunc['db_query']('', '
		SELECT id_ticket, subject
		FROM {db_prefix}helpdesk_tickets
		WHERE id_dept = {int:dept_from} ' . implode(' ', $clauses) . '
		ORDER BY id_ticket
		LIMIT {int:step}', array('dept_from' => $_POST['id_dept_from'], 'step' => $step_count));
    while ($row = $smcFunc['db_fetch_assoc']($query)) {
        $tickets[$row['id_ticket']] = $row['subject'];
    }
    $smcFunc['db_free_result']($query);
    if (!empty($tickets)) {
        // Get department ids.
        $query = $smcFunc['db_query']('', '
			SELECT id_dept, dept_name
			FROM {db_prefix}helpdesk_depts
			WHERE id_dept IN ({array_int:depts})', array('depts' => array($_POST['id_dept_from'], $_POST['id_dept_to'])));
        $depts = array();
        while ($row = $smcFunc['db_fetch_assoc']($query)) {
            $depts[$row['id_dept']] = $row['dept_name'];
        }
        $smcFunc['db_free_result']($query);
        // OK, we have the ticket ids. Now we'll move the set and log each one moved.
        $smcFunc['db_query']('', '
			UPDATE {db_prefix}helpdesk_tickets
			SET id_dept = {int:dept_to}
			WHERE id_ticket IN ({array_int:ids})', array('dept_to' => $_POST['id_dept_to'], 'ids' => array_keys($tickets)));
        // This is the same every time.
        $log_params = array('old_dept_id' => $_POST['id_dept_from'], 'old_dept_name' => $depts[$_POST['id_dept_from']], 'new_dept_id' => $_POST['id_dept_to'], 'new_dept_name' => $depts[$_POST['id_dept_to']]);
        foreach ($tickets as $id => $subject) {
            $log_params['subject'] = $subject;
            $log_params['ticket'] = $id;
            shd_log_action('move_dept', $log_params);
        }
        shd_clear_active_tickets($_POST['id_dept_from']);
        shd_clear_active_tickets($_POST['id_dept_to']);
        $_POST['tickets_done'] += $step_count;
    }
    // Prepare to shove everything we need into the form so we can go again.
    $context['continue_countdown'] = 3;
    $context['continue_get_data'] = '?action=admin;area=helpdesk_maint;sa=massdeptmove;' . $context['session_var'] . '=' . $context['session_id'];
    $context['continue_post_data'] = '
		<input type="hidden" name="id_dept_from" value="' . $_POST['id_dept_from'] . '" />
		<input type="hidden" name="id_dept_to" value="' . $_POST['id_dept_to'] . '" />
		<input type="hidden" name="tickets_done" value="' . $_POST['tickets_done'] . '" />
		<input type="hidden" name="massdeptmove" value="' . $_POST['massdeptmove'] . '" />';
    if (!empty($_POST['moveopen'])) {
        $context['continue_post_data'] .= '
		<input type="hidden" name="moveopen" value="' . $_POST['moveopen'] . '" />';
    }
    if (!empty($_POST['moveclosed'])) {
        $context['continue_post_data'] .= '
		<input type="hidden" name="moveclosed" value="' . $_POST['moveclosed'] . '" />';
    }
    if (!empty($_POST['movedeleted'])) {
        $context['continue_post_data'] .= '
		<input type="hidden" name="movedeleted" value="' . $_POST['movedeleted'] . '" />';
    }
    if ($_POST['movelast_less_days'] > 0) {
        $context['continue_post_data'] .= '
		<input type="hidden" name="movelast_less" value="1" />
		<input type="hidden" name="movelast_less_days" value="' . $_POST['movelast_less_days'] . '" />';
    }
    if ($_POST['movelast_more_days'] > 0) {
        $context['continue_post_data'] .= '
		<input type="hidden" name="movelast_more" value="1" />
		<input type="hidden" name="movelast_more_days" value="' . $_POST['movelast_more_days'] . '" />';
    }
    $context['sub_template'] = 'not_done';
    $context['continue_percent'] = $_POST['tickets_done'] > $_POST['massdeptmove'] ? 100 : floor($_POST['tickets_done'] / $_POST['massdeptmove'] * 100);
}