/**
 *	Looks to see if an attachment is from the helpdesk, if so we validate it and return a proper request. SMF will handle the rest.
 *
 *	@param string &$attachRequest A resource handle.
 *
 *	@since 2.0
*/
function shd_download_request(&$attachRequest)
{
    global $smcFunc;
    // Is this already a resource?  Then another hook has claimed the attachment as theirs.
    if (is_resource($attachRequest)) {
        return;
    }
    // If we don't have a ticket present, it is not our attachment.
    if (empty($_REQUEST['ticket'])) {
        return;
    }
    $_REQUEST['ticket'] = (int) $_REQUEST['ticket'];
    // First we check that we can see said ticket and figure out what department we're in.
    $request = shd_db_query('', '
		SELECT hdt.id_dept
		FROM {db_prefix}helpdesk_tickets AS hdt
		WHERE id_ticket = {int:ticket}
			AND {query_see_ticket}', array('ticket' => $_REQUEST['ticket']));
    // If there's a row, we need to process it and then issue the follow on query. If not, fall through to the next cut-off point, outside of this edit.
    if ($smcFunc['db_num_rows']($request) != 0) {
        list($dept) = $smcFunc['db_fetch_row']($request);
        $smcFunc['db_free_result']($request);
        // Now check their permission. If they don't have permission to view in this department, bye.
        shd_is_allowed_to('shd_view_attachment', $dept);
        // Make sure the attachment is on this ticket, note right now we're forcing it to be "approved"
        $attachRequest = shd_db_query('', '
			SELECT a.id_folder, a.filename, a.file_hash, a.fileext, a.id_attach, a.attachment_type, a.mime_type, 1 AS approved, hdtr.id_member
			FROM {db_prefix}attachments AS a
				INNER JOIN {db_prefix}helpdesk_attachments AS hda ON (a.id_attach = hda.id_attach)
				INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hda.id_msg = hdtr.id_msg)
				INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdtr.id_ticket = hdt.id_ticket)
			WHERE a.id_attach = {int:attach}
				AND hdt.id_ticket = {int:ticket}
			LIMIT 1', array('attach' => $_REQUEST['attach'], 'ticket' => (int) $_REQUEST['ticket']));
    }
}
/**
 *	Updates a ticket's relationships.
 *
 *	This function is responsible for adding, updating and removing relationships.
 *
 *	Accessed through ?action=helpdesk;sa=relation;ticket=x;linkticket=y;relation=[linked|duplicated|parent|child|delete];sessvar=sessid and will redirect back to the ticket once complete.
 *
 *	@since 2.0
*/
function shd_ticket_relation()
{
    global $context, $smcFunc, $modSettings;
    checkSession('request');
    if (!empty($modSettings['shd_disable_relationships'])) {
        fatal_lang_error('shd_relationships_are_disabled', false);
    }
    $otherticket = isset($_REQUEST['otherticket']) ? (int) $_REQUEST['otherticket'] : 0;
    if (empty($context['ticket_id']) || empty($otherticket)) {
        fatal_lang_error('shd_no_ticket', false);
    }
    if ($context['ticket_id'] == $otherticket) {
        fatal_lang_error('shd_cannot_relate_self', false);
    }
    $actions = array('linked', 'duplicated', 'parent', 'child', 'delete');
    $rel_action = isset($_REQUEST['relation']) && in_array($_REQUEST['relation'], $actions) ? $_REQUEST['relation'] : '';
    if (empty($rel_action)) {
        fatal_lang_error('shd_invalid_relation', false);
    }
    // Quick/consistent way to ensure permissions are adhered to and that the ticket exists. Might as well get the subject while here too.
    $ticketinfo = shd_load_ticket($context['ticket_id']);
    $primary_subject = $ticketinfo['subject'];
    $dept = $ticketinfo['dept'];
    $ticketinfo = shd_load_ticket($otherticket);
    $secondary_subject = $ticketinfo['subject'];
    if ($rel_action == 'delete') {
        shd_is_allowed_to('shd_delete_relationships', $dept);
    } else {
        shd_is_allowed_to('shd_create_relationships', $dept);
    }
    // See if there's an existing relationship with these parameters
    $query = shd_db_query('', '
		SELECT rel_type
		FROM {db_prefix}helpdesk_relationships
		WHERE primary_ticket = {int:ticket}
			AND secondary_ticket = {int:otherticket}', array('ticket' => $context['ticket_id'], 'otherticket' => $otherticket));
    $new_relationship = $smcFunc['db_num_rows']($query) == 0;
    list($existing_relation) = $new_relationship ? array(-1) : $smcFunc['db_fetch_row']($query);
    $smcFunc['db_free_result']($query);
    if ($rel_action == 'delete' && $new_relationship) {
        fatal_lang_error('shd_no_relation_delete', false);
    }
    $log_prefix = 'rel_' . ($new_relationship || $rel_action == 'delete' ? '' : 're_');
    $log_map = array('delete' => 'delete', 'linked' => 'linked', 'duplicated' => 'duplicated', 'parent' => 'child', 'child' => 'parent');
    $log_assoc_ids = array('delete' => -1, 'linked' => RELATIONSHIP_LINKED, 'duplicated' => RELATIONSHIP_DUPLICATED, 'parent' => RELATIONSHIP_ISPARENT, 'child' => RELATIONSHIP_ISCHILD);
    $logaction_ticket = $log_prefix . $rel_action;
    $logaction_otherticket = $log_prefix . $log_map[$rel_action];
    // The "from" ticket is $context['ticket_id'], $otherticket is the ticket whose id the user gives, and $rel_action sets the relationship type.
    call_integration_hook('shd_hook_relations', array(&$rel_action, &$otherticket));
    if ($rel_action == 'delete') {
        // Delete the link
        shd_db_query('', '
			DELETE FROM {db_prefix}helpdesk_relationships
			WHERE (primary_ticket = {int:ticket} AND secondary_ticket = {int:otherticket})
				OR (primary_ticket = {int:otherticket} AND secondary_ticket = {int:ticket})', array('ticket' => $context['ticket_id'], 'otherticket' => $otherticket));
    } else {
        $smcFunc['db_insert']('replace', '{db_prefix}helpdesk_relationships', array('primary_ticket' => 'int', 'secondary_ticket' => 'int', 'rel_type' => 'int'), array(array($context['ticket_id'], $otherticket, $log_assoc_ids[$rel_action]), array($otherticket, $context['ticket_id'], $log_assoc_ids[$log_map[$rel_action]])), array('primary_ticket', 'secondary_ticket'));
    }
    // Now actually log it, main ticket first -- if it's different to the one we had before
    if ($log_assoc_ids[$rel_action] != $existing_relation) {
        $params = array('ticket' => $context['ticket_id'], 'otherticket' => $otherticket, 'subject' => $primary_subject, 'othersubject' => $secondary_subject);
        shd_log_action($logaction_ticket, $params);
        $params = array('ticket' => $otherticket, 'otherticket' => $context['ticket_id'], 'subject' => $secondary_subject, 'othersubject' => $primary_subject);
        shd_log_action($logaction_otherticket, $params);
    }
    // See yah
    redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
}
Example #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]);
                }
            }
        }
    }
}
Example #4
0
function shd_handle_attachments()
{
    global $modSettings, $smcFunc, $context, $user_info, $sourcedir, $txt;
    if (!shd_allowed_to('shd_post_attachment', $context['ticket_form']['dept'])) {
        return;
    }
    $attachIDs = array();
    require_once $sourcedir . '/Subs-Attachments.php';
    // Check if they are trying to delete any current attachments....
    if (isset($_POST['attach_del'])) {
        shd_is_allowed_to('shd_delete_attachment', $context['ticket_form']['dept']);
        $del_temp = array();
        foreach ($_POST['attach_del'] as $i => $dummy) {
            $del_temp[$i] = (int) $dummy;
        }
        // First, get them from the other table
        $query = shd_db_query('', '
			SELECT a.id_attach
			FROM {db_prefix}attachments AS a
				INNER JOIN {db_prefix}helpdesk_attachments AS hda ON (hda.id_attach = a.id_attach)
			WHERE ' . ($modSettings['shd_attachments_mode'] == 'ticket' ? 'hda.id_ticket = {int:ticket}' : 'hda.id_msg = {int:msg}'), array('msg' => $context['ticket_form']['msg'], 'ticket' => $context['ticket_id']));
        $attachments = array();
        while ($row = $smcFunc['db_fetch_assoc']($query)) {
            $attachments[] = $row['id_attach'];
        }
        $smcFunc['db_free_result']($query);
        // OK, so attachments = full list of attachments on this post, del_temp is list of ones to keep, so look for the ones that aren't in both lists
        $del_temp = array_diff($attachments, $del_temp);
        if (!empty($del_temp)) {
            $filenames = array();
            // Before deleting, get the names for the log
            $query = $smcFunc['db_query']('', '
				SELECT filename, attachment_type
				FROM {db_prefix}attachments
				WHERE id_attach IN ({array_int:attach})
				ORDER BY id_attach', array('attach' => $del_temp));
            $removed = array();
            while ($row = $smcFunc['db_fetch_assoc']($query)) {
                $row['filename'] = htmlspecialchars($row['filename']);
                $filenames[] = $row['filename'];
                if ($row['attachment_type'] == 0) {
                    $removed[] = $row['filename'];
                }
            }
            if (!empty($removed)) {
                $context['log_params']['att_removed'] = $removed;
            }
            // Now you can delete
            require_once $sourcedir . '/ManageAttachments.php';
            $attachmentQuery = array('attachment_type' => 0, 'id_msg' => 0, 'id_attach' => $del_temp);
            removeAttachments($attachmentQuery);
        }
    }
    // ...or attach a new file...
    if (!empty($_FILES) || !empty($_SESSION['temp_attachments'])) {
        if (!empty($FILES)) {
            $_FILES = array_reverse($_FILES);
        }
        shd_is_allowed_to('shd_post_attachment');
        // Make sure we're uploading to the right place.
        if (!empty($modSettings['currentAttachmentUploadDir'])) {
            if (!is_array($modSettings['attachmentUploadDir'])) {
                $modSettings['attachmentUploadDir'] = json_decode($modSettings['attachmentUploadDir'], true);
            }
            // The current directory, of course!
            $current_attach_dir = $modSettings['attachmentUploadDir'][$modSettings['currentAttachmentUploadDir']];
        } else {
            $current_attach_dir = $modSettings['attachmentUploadDir'];
        }
        // If this isn't a new post, check the current attachments.
        if (isset($_REQUEST['msg']) || !empty($context['ticket_id'])) {
            $request = shd_db_query('', '
				SELECT COUNT(*), SUM(size)
				FROM {db_prefix}attachments AS a
					INNER JOIN {db_prefix}helpdesk_attachments AS hda ON (a.id_attach = hda.id_attach)
				WHERE ' . ($modSettings['shd_attachments_mode'] == 'ticket' ? 'hda.id_ticket = {int:ticket}' : 'hda.id_msg = {int:msg}') . '
					AND attachment_type = {int:attachment_type}', array('msg' => $context['ticket_form']['msg'], 'ticket' => $context['ticket_id'], 'attachment_type' => 0));
            list($quantity, $total_size) = $smcFunc['db_fetch_row']($request);
            $smcFunc['db_free_result']($request);
        } else {
            $quantity = 0;
            $total_size = 0;
        }
        if (!empty($_SESSION['temp_attachments'])) {
            foreach ($_SESSION['temp_attachments'] as $attachID => $name) {
                if (preg_match('~^post_tmp_' . $user_info['id'] . '_\\d+$~', $attachID) == 0) {
                    continue;
                }
                if (!empty($_POST['attach_del']) && !in_array($attachID, $_POST['attach_del'])) {
                    unset($_SESSION['temp_attachments'][$attachID]);
                    @unlink($current_attach_dir . '/' . $attachID);
                    continue;
                }
                $_FILES['attachment' . $attachID]['tmp_name'] = $attachID;
                $_FILES['attachment' . $attachID]['name'] = $name;
                $_FILES['attachment' . $attachID]['size'] = filesize($current_attach_dir . '/' . $attachID);
                list($_FILES['attachment' . $attachID]['width'], $_FILES['attachment' . $attachID]['height']) = @getimagesize($current_attach_dir . '/' . $attachID);
                unset($_SESSION['temp_attachments'][$attachID]);
            }
        }
        foreach ($_FILES as $uplfile) {
            if ($uplfile['name'] == '') {
                continue;
            }
            // Have we reached the maximum number of files we are allowed?
            $quantity++;
            $file_limit = !empty($modSettings['attachmentNumPerPostLimit']) && $modSettings['shd_attachments_mode'] != 'ticket' ? $modSettings['attachmentNumPerPostLimit'] : $quantity + 1;
            if ($quantity > $file_limit) {
                checkSubmitOnce('free');
                fatal_lang_error('attachments_limit_per_post', false, array($modSettings['attachmentNumPerPostLimit']));
            }
            // Check the total upload size for this post...
            $total_size += $uplfile['size'];
            $size_limit = !empty($modSettings['attachmentPostLimit']) && $modSettings['shd_attachments_mode'] != 'ticket' ? $modSettings['attachmentPostLimit'] * 1024 : $total_size + 1024;
            if ($total_size > $size_limit) {
                checkSubmitOnce('free');
                fatal_lang_error('file_too_big', false, array($modSettings['attachmentPostLimit']));
            }
            $attachmentOptions = array('post' => 0, 'poster' => $user_info['id'], 'name' => $uplfile['name'], 'tmp_name' => $uplfile['tmp_name'], 'size' => $uplfile['size'], 'id_folder' => $modSettings['currentAttachmentUploadDir']);
            if (createAttachment($attachmentOptions)) {
                $attachIDs[] = $attachmentOptions['id'];
                if (!empty($attachmentOptions['thumb'])) {
                    $attachIDs[] = $attachmentOptions['thumb'];
                }
                $context['log_params']['att_added'][] = htmlspecialchars($attachmentOptions['name']);
            } else {
                if (in_array('could_not_upload', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_lang_error('attach_timeout', 'critical');
                }
                if (in_array('too_large', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_lang_error('file_too_big', false, array($modSettings['attachmentSizeLimit']));
                }
                if (in_array('bad_extension', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_error($attachmentOptions['name'] . '.<br />' . $txt['cant_upload_type'] . ' ' . strtr($modSettings['attachmentExtensions'], array(',' => ', ')) . '.', false);
                }
                if (in_array('directory_full', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_lang_error('ran_out_of_space', 'critical');
                }
                if (in_array('bad_filename', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_error(basename($attachmentOptions['name']) . '.<br />' . $txt['restricted_filename'] . '.', 'critical');
                }
                if (in_array('taken_filename', $attachmentOptions['errors'])) {
                    checkSubmitOnce('free');
                    fatal_lang_error('filename_exists');
                }
            }
        }
    }
    return $attachIDs;
}
function shd_search2()
{
    global $context, $smcFunc, $txt, $modSettings, $scripturl, $sourcedir;
    shd_is_allowed_to('shd_search', 0);
    if (!empty($context['load_average']) && !empty($modSettings['loadavg_search']) && $context['load_average'] >= $modSettings['loadavg_search']) {
        fatal_lang_error('loadavg_search_disabled', false);
    }
    // No, no, no... this is a bit hard on the server, so don't you go prefetching it!
    if (isset($_SERVER['HTTP_X_MOZ']) && $_SERVER['HTTP_X_MOZ'] == 'prefetch') {
        ob_end_clean();
        header('HTTP/1.1 403 Forbidden');
        die;
    }
    // We will need this.
    require_once $sourcedir . '/sd_source/Subs-SimpleDeskSearch.php';
    loadTemplate('sd_template/SimpleDesk-Search');
    $context['page_title'] = $txt['shd_search_results'];
    $context['linktree'][] = array('name' => $txt['shd_search_results']);
    $context['search_clauses'] = array('{query_see_ticket}');
    $context['search_params'] = array();
    // Departments first.
    $visible_depts = shd_allowed_to('access_helpdesk', false);
    $using_depts = array();
    if (!empty($_POST['search_dept']) && is_array($_POST['search_dept'])) {
        foreach ($_POST['search_dept'] as $dept) {
            if ((int) $dept > 0) {
                $using_depts[] = (int) $dept;
            }
        }
    }
    if (!empty($using_depts)) {
        $using_depts = array_intersect($using_depts, $visible_depts);
    }
    // No departments? Can't really do a lot, sorry. Bye then.
    if (empty($using_depts)) {
        return $context['sub_template'] = 'search_no_results';
    }
    // Is the selected list the same size as the list we can see? If it is, theory says that means we're picking every department we can see and don't need to exclude it.
    if (count($using_depts) != count($visible_depts)) {
        $context['search_clauses'][] = 'hdt.id_dept IN ({array_int:visible_depts})';
        $context['search_params']['visible_depts'] = $using_depts;
        // Also, we need to get the department list for displaying, only if we can actually see multiple departments at all.
        if ($context['shd_multi_dept']) {
            $query = $smcFunc['db_query']('', '
				SELECT id_dept, dept_name
				FROM {db_prefix}helpdesk_depts
				WHERE id_dept IN ({array_int:dept_list})
				ORDER BY dept_order', array('dept_list' => $using_depts));
            $context['search_dept_list'] = array();
            while ($row = $smcFunc['db_fetch_assoc']($query)) {
                $context['search_dept_list'][$row['id_dept']] = $row['dept_name'];
            }
            $smcFunc['db_free_result']($query);
        }
    }
    // Ticket urgency
    $using_urgency = array();
    if (!empty($_POST['urgency']) && is_array($_POST['urgency'])) {
        foreach ($_POST['urgency'] as $urgency) {
            $urgency = (int) $urgency;
            if ($urgency >= 0 && $urgency <= 5) {
                // All the currently defined urgencies
                $using_urgency[] = $urgency;
            }
        }
    }
    if (empty($using_urgency)) {
        return $context['sub_template'] = 'search_no_results';
    } else {
        $using_urgency = array_unique($using_urgency);
        if (count($using_urgency) < 6) {
            // We have less than 6 selected urgencies, which means we actually need to filter on them, as opposed to if all 6 are selected when we don't.
            $context['search_clauses'][] = 'hdt.urgency IN ({array_int:urgency})';
            $context['search_params']['urgency'] = $using_urgency;
        }
    }
    // Ticket scope
    // All empty? If so, bye.
    if (empty($_POST['scope_open']) && empty($_POST['scope_closed']) && empty($_POST['scope_recycle'])) {
        return $context['sub_template'] = 'search_no_results';
    } elseif (empty($_POST['scope_open']) || empty($_POST['scope_closed']) || empty($_POST['scope_recycle'])) {
        $status = array();
        if (!empty($_POST['scope_open'])) {
            $status = array_merge($status, array(TICKET_STATUS_NEW, TICKET_STATUS_PENDING_STAFF, TICKET_STATUS_PENDING_USER, TICKET_STATUS_WITH_SUPERVISOR, TICKET_STATUS_ESCALATED));
        }
        if (!empty($_POST['scope_closed'])) {
            $status = array_merge($status, array(TICKET_STATUS_CLOSED));
        }
        if (!empty($_POST['scope_recycle'])) {
            $status = array_merge($status, array(TICKET_STATUS_DELETED));
        }
        $context['search_clauses'][] = 'hdt.status IN ({array_int:status})';
        $context['search_params']['status'] = $status;
        // That's ticket level status taken care of. We'll pick up recycled items in non recycled tickets separately since it's only relevant if you're actually searching text.
    }
    // Ticket starter
    $starters = shd_get_named_people('starter');
    if (!empty($starters)) {
        $context['search_clauses'][] = 'hdt.id_member_started IN ({array_int:member_started})';
        $context['search_params']['member_started'] = $starters;
    }
    // Ticket assigned to
    $assignees = shd_get_named_people('assignee');
    if (!empty($assignees)) {
        $context['search_clauses'][] = 'hdt.id_member_assigned IN ({array_int:member_assigned})';
        $context['search_params']['member_assigned'] = $assignees;
    }
    // Lastly, page number. We're doing something different to SMF's normal style here. Long and complicated, but there you go.
    if (isset($_POST['page'])) {
        $context['pagenum'] = (int) $_POST['page'];
    }
    if (empty($context['pagenum']) || $context['pagenum'] < 1) {
        $context['pagenum'] = 1;
    }
    $number_per_page = 20;
    // OK, so are there any words? If not, execute this sucker the quick way and get out to the template quick.
    $context['search_terms'] = !empty($_POST['search']) ? trim($_POST['search']) : '';
    // Also, did we select some text but fail to select what it was searching in? If so, kick it out.
    if (!empty($context['search_terms']) && empty($_POST['search_subjects']) && empty($_POST['search_tickets']) && empty($_POST['search_replies'])) {
        return $context['sub_template'] = 'search_no_results';
    } elseif (!empty($context['search_terms'])) {
        // We're using search terms, and we need to store the areas we're covering. Only makes sense if we're using terms though.
        $context['search_params']['areas'] = array();
        foreach (array('subjects', 'tickets', 'replies') as $area) {
            if (!empty($_POST['search_' . $area])) {
                $context['search_params']['areas'][$area] = true;
            }
        }
        // While we're at it, see if we actually have any words to search for.
        $tokens = shd_tokeniser($context['search_terms']);
        $count_tokens = count($tokens);
        // No actual words?
        if ($count_tokens == 0) {
            $context['search_terms'] = '';
            unset($context['search_params']['areas']);
        }
    }
    // Spam me not!
    if (empty($_SESSION['lastsearch'])) {
        spamProtection('search');
    } else {
        list($temp_clauses, $temp_params, $temp_terms) = unserialize($_SESSION['lastsearch']);
        if ($temp_clauses != $context['search_clauses'] || $temp_params != $context['search_params'] || $temp_terms != $context['search_terms']) {
            spamProtection('search');
        }
    }
    $_SESSION['lastsearch'] = serialize(array($context['search_clauses'], $context['search_params'], $context['search_terms']));
    $context['search_params']['start'] = ($context['pagenum'] - 1) * $number_per_page;
    $context['search_params']['limit'] = $number_per_page;
    if (empty($context['search_terms'])) {
        // This is where it starts to get expensive, *sob*. We first have to query to get the number of applicable rows.
        $query = shd_db_query('', '
			SELECT COUNT(id_ticket)
			FROM {db_prefix}helpdesk_tickets AS hdt
			WHERE ' . implode(' AND ', $context['search_clauses']) . ' LIMIT 1000', $context['search_params']);
        list($count) = $smcFunc['db_fetch_row']($query);
        if ($count == 0) {
            $smcFunc['db_free_result']($query);
            return $context['sub_template'] = 'search_no_results';
        }
        // OK, at least one result, awesome. Are we off the end of the list?
        if ($context['search_params']['start'] > $count) {
            $context['search_params']['start'] = $count - $count % $number_per_page;
            $context['pagenum'] = $context['search_params']['start'] / $number_per_page + 1;
            $context['num_results'] = $count;
        }
        $query = shd_db_query('', '
			SELECT hdt.id_ticket, hdt.id_dept, hdd.dept_name, hdt.subject, hdt.urgency, hdt.private, hdt.last_updated, hdtr.body,
				hdtr.smileys_enabled, hdtr.id_member AS id_member, IFNULL(mem.real_name, hdtr.poster_name) AS poster_name, hdtr.poster_time
			FROM {db_prefix}helpdesk_tickets AS hdt
				INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdt.id_first_msg = hdtr.id_msg)
				INNER JOIN {db_prefix}helpdesk_depts AS hdd ON (hdt.id_dept = hdd.id_dept)
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = hdtr.id_member)
			WHERE ' . implode(' AND ', $context['search_clauses']) . '
			ORDER BY hdt.last_updated DESC
			LIMIT {int:start}, {int:limit}', $context['search_params']);
        $context['search_results'] = array();
        $page_pos = $context['search_params']['start'];
        // e.g. 0 on page 1, 10 for page 2, the first item will be page_pos + 1, so ++ it before using it.
        while ($row = $smcFunc['db_fetch_assoc']($query)) {
            $row['result'] = ++$page_pos;
            // Increment first, then use.
            $row['display_id'] = str_pad($row['id_ticket'], $modSettings['shd_zerofill'], '0', STR_PAD_LEFT);
            $row['is_ticket'] = true;
            // If we're here, we're only handling tickets anyway. If we're searching text we will need to know if it was a ticket or reply though.
            $row['dept_link'] = !$context['shd_multi_dept'] ? '' : '[<a href="' . $scripturl . '?action=helpdesk;sa=main;dept=' . $row['id_dept'] . '">' . $row['dept_name'] . '</a>] ';
            $context['search_results'][] = $row;
        }
        return $context['sub_template'] = 'search_results';
    } else {
        $context['match_all'] = empty($_POST['searchtype']) || $_POST['searchtype'] == 'all';
        // Then figure out what terms are being matched.
        $matches = array('subjects' => array(), 'messages' => array(), 'id_msg' => array());
        // Doing subjects. Fetch all the instances that match and begin filtering as we go.
        if (!empty($context['search_params']['areas']['subjects'])) {
            $query = shd_db_query('', '
				SELECT hdssw.id_word, hdt.id_first_msg
				FROM {db_prefix}helpdesk_search_subject_words AS hdssw
					INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdssw.id_ticket = hdt.id_ticket)
				WHERE {query_see_ticket}
					AND id_word IN ({array_string:tokens})', array('tokens' => $tokens));
            while ($row = $smcFunc['db_fetch_assoc']($query)) {
                $matches['subjects'][$row['id_first_msg']][$row['id_word']] = true;
            }
            $smcFunc['db_free_result']($query);
            // Now go through and figure out which tickets we're interested in keeping.
            if ($context['match_all']) {
                foreach ($matches['subjects'] as $msg => $ticket_words) {
                    if (count($ticket_words) != $count_tokens) {
                        // How many words did we match in this subject? If it isn't the number we're expecting, ditch it.
                        unset($matches['subjects'][$msg]);
                    }
                }
            }
            // Now, we just have a list of tickets to play with. Let's put that together in a master list.
            foreach ($matches['subjects'] as $msg => $ticket_words) {
                $matches['id_msg'][$msg] = true;
            }
            unset($matches['subjects']);
        }
        // Now we get the list of words that apply to tickets and replies. The process is different if we do one or both. Both, first.
        if (!empty($context['search_params']['areas']['tickets']) && !empty($context['search_params']['areas']['replies'])) {
            // If we're doing both replies and tickets themselves, we don't have to care too much about the message itself, except for being deleted.
            $query = shd_db_query('', '
				SELECT hdssw.id_word, hdt.id_first_msg
				FROM {db_prefix}helpdesk_search_subject_words AS hdssw
					INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdssw.id_ticket = hdt.id_ticket)
				WHERE {query_see_ticket}
					AND id_word IN ({array_string:tokens})' . (empty($_POST['scope_recycle']) || !shd_allowed_to('shd_access_recyclebin', 0) ? '
					AND hdtr.message_status = {int:not_deleted}' : ''), array('tokens' => $tokens, 'not_deleted' => MSG_STATUS_NORMAL));
            while ($row = $smcFunc['db_fetch_assoc']($query)) {
                $matches['messages'][$row['id_first_msg']][$row['id_word']] = true;
            }
            $smcFunc['db_free_result']($query);
            if ($context['match_all']) {
                foreach ($matches['messages'] as $msg => $ticket_words) {
                    if (count($ticket_words) != $count_tokens) {
                        // How many words did we match in this subject? If it isn't the number we're expecting, ditch it.
                        unset($matches['messages'][$msg]);
                    }
                }
            }
            // Now, we just have a list of tickets to play with. Let's put that together in a master list.
            foreach ($matches['messages'] as $msg => $ticket_words) {
                $matches['id_msg'][$msg] = true;
            }
            unset($matches['messages']);
        } elseif (!empty($context['search_params']['areas']['tickets']) || !empty($context['search_params']['areas']['replies'])) {
            $query = $smcFunc['db_query']('', '
				SELECT hdstw.id_word, hdstw.id_msg
				FROM {db_prefix}helpdesk_search_ticket_words AS hdstw
					INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdstw.id_msg = hdtr.id_msg)
					INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hdtr.id_ticket = hdt.id_ticket)
				WHERE id_word IN ({array_string:tokens})
					AND hdstw.id_msg {raw:operator} hdt.id_first_msg' . (empty($_POST['scope_recycle']) || !shd_allowed_to('shd_access_recyclebin', 0) ? '
					AND hdtr.message_status = {int:not_deleted}' : ''), array('tokens' => $tokens, 'not_deleted' => MSG_STATUS_NORMAL, 'operator' => !empty($context['search_params']['areas']['tickets']) ? '=' : '!='));
            while ($row = $smcFunc['db_fetch_assoc']($query)) {
                $matches['messages'][$row['id_msg']][$row['id_word']] = true;
            }
            $smcFunc['db_free_result']($query);
            if ($context['match_all']) {
                foreach ($matches['messages'] as $ticket => $ticket_words) {
                    if (count($ticket_words) != $count_tokens) {
                        // How many words did we match in this subject? If it isn't the number we're expecting, ditch it.
                        unset($matches['messages'][$ticket]);
                    }
                }
            }
            // Now, we just have a list of tickets to play with. Let's put that together in a master list.
            foreach ($matches['messages'] as $msg => $ticket_words) {
                $matches['id_msg'][$ticket] = true;
            }
            unset($matches['messages']);
        }
        // Aw, no matches?
        if (empty($matches['id_msg'])) {
            return $context['sub_template'] = 'search_no_results';
        }
        $context['search_clauses'][] = 'hdtr.id_msg IN ({array_int:msg})';
        $context['search_params']['msg'] = array_keys($matches['id_msg']);
        // How many results are there in total?
        $query = shd_db_query('', '
			SELECT COUNT(*)
			FROM {db_prefix}helpdesk_tickets AS hdt
				INNER JOIN {db_prefix}helpdesk_ticket_replies AS hdtr ON (hdtr.id_ticket = hdt.id_ticket)
			WHERE ' . implode(' AND ', $context['search_clauses']) . ' LIMIT 1000', $context['search_params']);
        list($count) = $smcFunc['db_fetch_row']($query);
        if ($count == 0) {
            $smcFunc['db_free_result']($query);
            return $context['sub_template'] = 'search_no_results';
        }
        // OK, at least one result, awesome. Are we off the end of the list?
        if ($context['search_params']['start'] > $count) {
            $context['search_params']['start'] = $count - $count % $number_per_page;
            $context['pagenum'] = $context['search_params']['start'] / $number_per_page + 1;
            $context['num_results'] = $count;
        }
        // Get the results for displaying.
        $query = shd_db_query('', '
			SELECT hdt.id_ticket, hdt.id_dept, hdd.dept_name, hdt.subject, hdt.urgency, hdt.private, hdt.last_updated, hdtr.body,
				hdtr.smileys_enabled, hdtr.id_member AS id_member, IFNULL(mem.real_name, hdtr.poster_name) AS poster_name, hdtr.poster_time,
				hdt.id_first_msg, 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)
				INNER JOIN {db_prefix}helpdesk_depts AS hdd ON (hdt.id_dept = hdd.id_dept)
				LEFT JOIN {db_prefix}members AS mem ON (mem.id_member = hdtr.id_member)
			WHERE ' . implode(' AND ', $context['search_clauses']) . '
			ORDER BY hdt.last_updated DESC, hdtr.id_msg DESC
			LIMIT {int:start}, {int:limit}', $context['search_params']);
        $context['search_results'] = array();
        $page_pos = $context['search_params']['start'];
        // e.g. 0 on page 1, 10 for page 2, the first item will be page_pos + 1, so ++ it before using it.
        while ($row = $smcFunc['db_fetch_assoc']($query)) {
            $row['result'] = ++$page_pos;
            // Increment first, then use.
            $row['display_id'] = str_pad($row['id_ticket'], $modSettings['shd_zerofill'], '0', STR_PAD_LEFT);
            $row['is_ticket'] = $row['id_msg'] == $row['id_first_msg'];
            // If the message we grabbed is the first message, this is actually a ticket, not a reply to one.
            $row['dept_link'] = !$context['shd_multi_dept'] ? '' : '[<a href="' . $scripturl . '?action=helpdesk;sa=main;dept=' . $row['id_dept'] . '">' . $row['dept_name'] . '</a>] ';
            $context['search_results'][] = $row;
        }
        return $context['sub_template'] = 'search_results';
    }
}
Example #6
0
function shd_attach_delete()
{
    global $smcFunc, $user_info, $context, $sourcedir;
    if (empty($context['ticket_id']) || empty($_GET['attach']) || (int) $_GET['attach'] == 0) {
        fatal_lang_error('no_access', false);
    }
    $_GET['attach'] = (int) $_GET['attach'];
    // Well, we have a ticket id. Let's figure out what department we're in so we can check permissions.
    $query = shd_db_query('', '
		SELECT hdt.id_dept, a.filename, hda.id_msg, hdt.subject
		FROM {db_prefix}attachments AS a
			INNER JOIN {db_prefix}helpdesk_attachments AS hda ON (hda.id_attach = a.id_attach)
			INNER JOIN {db_prefix}helpdesk_tickets AS hdt ON (hda.id_ticket = hdt.id_ticket)
		WHERE {query_see_ticket}
			AND hda.id_ticket = {int:ticket}
			AND hda.id_attach = {int:attach}
			AND a.attachment_type = 0', array('attach' => $_GET['attach'], 'ticket' => $context['ticket_id']));
    if ($smcFunc['db_num_rows']($query) == 0) {
        $smcFunc['db_free_result']($query);
        fatal_lang_error('no_access');
    }
    list($dept, $filename, $id_msg, $subject) = $smcFunc['db_fetch_row']($query);
    $smcFunc['db_free_result']($query);
    shd_is_allowed_to('shd_delete_attachment', $dept);
    // So, we can delete the attachment. We already know it exists, we know we have permission.
    $log_params = array('subject' => $subject, 'ticket' => $context['ticket_id'], 'msg' => $id_msg, 'att_removed' => array(htmlspecialchars($filename)));
    shd_log_action('editticket', $log_params);
    // Now you can delete
    require_once $sourcedir . '/ManageAttachments.php';
    $attachmentQuery = array('attachment_type' => 0, 'id_msg' => 0, 'id_attach' => array($_GET['attach']));
    removeAttachments($attachmentQuery);
    redirectexit('action=helpdesk;sa=ticket;ticket=' . $context['ticket_id']);
}