function shd_save_reply() { global $txt, $modSettings, $sourcedir, $context, $scripturl; global $user_info, $options, $smcFunc, $memberContext; $_REQUEST['msg'] = !empty($_REQUEST['msg']) ? (int) $_REQUEST['msg'] : 0; // We're replying so there must be an existing ticket or sumthin's WRONG. $ticketinfo = shd_load_ticket(); $reply = array(); $new_reply = $_REQUEST['msg'] == 0; // So, at this point, we can see it, but no guarantee we can reply to it. // Can we reply to any? If so, just go right along. If not, we need to do more work. if ($new_reply) { if (!shd_allowed_to('shd_reply_ticket_any', $ticketinfo['dept'])) { if (shd_allowed_to('shd_reply_ticket_own', $ticketinfo['dept'])) { if (!$ticketinfo['is_own']) { fatal_lang_error('shd_cannot_reply_any_but_own', false); } } else { fatal_lang_error('shd_cannot_reply_any', false); } // can't do nuthin' } } else { $query = shd_db_query('', ' SELECT id_msg, id_member, body, modified_time, modified_member, modified_name FROM {db_prefix}helpdesk_ticket_replies WHERE id_msg = {int:msg} AND id_ticket = {int:ticket}', array('msg' => $_REQUEST['msg'], 'ticket' => $context['ticket_id'])); if ($smcFunc['db_num_rows']($query) == 0) { $smcFunc['db_free_result']($query); fatal_lang_error('shd_no_ticket', false); } $reply = $smcFunc['db_fetch_assoc']($query); if (!shd_allowed_to('shd_edit_reply_any', $ticketinfo['dept'])) { if (shd_allowed_to('shd_edit_reply_own', $ticketinfo['dept'])) { if ($reply['id_member'] != $user_info['id']) { fatal_lang_error('shd_cannot_edit_reply_any_but_own', false); } } else { fatal_lang_error('shd_cannot_edit_reply_any', false); } } } $context['ticket_form'] = array('dept' => $ticketinfo['dept'], 'form_title' => $new_reply ? $txt['shd_reply_ticket'] : $txt['shd_ticket_edit_reply'], 'form_action' => $scripturl . '?action=helpdesk;sa=savereply', 'first_msg' => $new_reply ? 0 : $ticketinfo['id_first_msg'], 'message' => shd_format_text($ticketinfo['body'], $ticketinfo['smileys_enabled'], $new_reply ? '' : 'shd_reply_' . $ticketinfo['id_first_msg']), 'subject' => $ticketinfo['subject'], 'ticket' => $context['ticket_id'], 'msg' => $_REQUEST['msg'], 'display_id' => str_pad($context['ticket_id'], $modSettings['shd_zerofill'], '0', STR_PAD_LEFT), 'urgency' => array('setting' => $ticketinfo['urgency']), 'private' => array('setting' => $ticketinfo['private'], 'can_change' => false, 'options' => array(0 => 'shd_ticket_notprivate', 1 => 'shd_ticket_private')), 'status' => $ticketinfo['status'], 'member' => array('name' => $ticketinfo['starter_name'], 'id' => $ticketinfo['starter_id'], 'link' => shd_profile_link($ticketinfo['starter_name'], $ticketinfo['starter_id'])), 'assigned' => array('id' => $ticketinfo['assigned_id'], 'name' => !empty($ticketinfo['assigned_id']) ? $ticketinfo['assigned_name'] : $txt['shd_unassigned'], 'link' => !empty($ticketinfo['assigned_id']) ? shd_profile_link($ticketinfo['assigned_name'], $ticketinfo['assigned_id']) : '<span class="error">' . $txt['shd_unassigned'] . '</span>'), 'num_replies' => $ticketinfo['num_replies'], 'do_attach' => shd_allowed_to('shd_post_attachment', $ticketinfo['dept']), 'reply' => $_POST['shd_message'], 'return_to_ticket' => isset($_REQUEST['goback']), 'disable_smileys' => !empty($_REQUEST['no_smileys'])); $context['can_solve'] = shd_allowed_to('shd_resolve_ticket_any', $ticketinfo['dept']) || shd_allowed_to('shd_resolve_ticket_own', $ticketinfo['dept']) && $ticketinfo['starter_id'] == $user_info['id']; $context['can_silent_update'] = $new_reply && shd_allowed_to('shd_silent_update', $ticketinfo['dept']); $context['log_action'] = $new_reply ? 'newreply' : 'editreply'; $context['log_params']['subject'] = $context['ticket_form']['subject']; shd_posting_additional_options(); shd_load_canned_replies(); $context['can_ping'] = $new_reply && shd_allowed_to('shd_singleton_email', $context['ticket_form']['dept']); // Ticket privacy if (empty($modSettings['shd_privacy_display']) || $modSettings['shd_privacy_display'] == 'smart') { $context['display_private'] = shd_allowed_to('shd_view_ticket_private_any', $ticketinfo['dept']) || shd_allowed_to('shd_alter_privacy_own', $ticketinfo['dept']) || shd_allowed_to('shd_alter_privacy_any', $ticketinfo['dept']) || $context['ticket_form']['private']['setting']; } else { $context['display_private'] = true; } loadMemberData($ticketinfo['starter_id']); if (loadMemberContext($ticketinfo['starter_id'])) { $context['ticket_form']['member']['avatar'] = $memberContext[$ticketinfo['starter_id']]['avatar']; } if (!empty($ticketinfo['modified_time'])) { $context['ticket_form'] += array('modified' => array('name' => $ticketinfo['modified_name'], 'id' => $ticketinfo['modified_id'], 'time' => timeformat($ticketinfo['modified_time']), 'link' => shd_profile_link($ticketinfo['modified_name'], $ticketinfo['modified_id']))); } if (isset($_REQUEST['preview'])) { $context['ticket_form']['preview'] = array('title' => $txt['shd_previewing_reply'] . ': ' . (empty($context['ticket_form']['subject']) ? '<em>' . $txt['no_subject'] . '</em>' : $context['ticket_form']['subject']), 'body' => shd_format_text($_POST['shd_message'])); } shd_load_attachments(); shd_get_urgency_options($ticketinfo['is_own'], $ticketinfo['dept']); $context['ticket_form']['urgency']['can_change'] = false; if (!empty($ticketinfo['num_replies'])) { shd_setup_replies($ticketinfo['id_first_msg']); } // A few basic checks if ($context['ticket_form']['status'] == TICKET_STATUS_CLOSED) { fatal_lang_error('shd_cannot_edit_closed', false); } elseif ($context['ticket_form']['status'] == TICKET_STATUS_DELETED) { fatal_lang_error('shd_cannon_edit_deleted', false); } // Have there been any new replies that we missed? if (empty($options['no_new_reply_warning']) && isset($_REQUEST['num_replies'])) { $_REQUEST['num_replies'] = (int) $_REQUEST['num_replies']; $newReplies = $context['ticket_form']['num_replies'] > $_REQUEST['num_replies'] ? $context['ticket_form']['num_replies'] - $_REQUEST['num_replies'] : 0; if (!empty($newReplies)) { loadLanguage('Post'); if ($newReplies > 1) { $txt['error_new_replies'] = sprintf($txt['error_new_replies'], $newReplies); } $context['shd_errors'][] = $newReplies == 1 ? 'new_reply' : 'new_replies'; } } // OK, does the user want to close this ticket? Are there any problems with that? if (!empty($context['can_solve']) && !empty($_POST['resolve_ticket'])) { $string = shd_check_dependencies(); if (!empty($string)) { $context['shd_errors'][] = $string; } } // Custom fields? shd_load_custom_fields(false, $context['ticket_form']['msg'], $context['ticket_form']['dept']); list($missing_fields, $invalid_fields) = shd_validate_custom_fields($context['ticket_form']['msg'], $context['ticket_form']['dept']); $context['can_override_fields'] = shd_allowed_to('shd_override_cf', $context['ticket_form']['dept']); $context['overriding_fields'] = $context['can_override_fields'] && isset($_POST['override_cf']); // Did any custom fields fail validation? if (!empty($invalid_fields)) { $context['shd_errors'][] = 'invalid_fields'; $txt['error_invalid_fields'] = sprintf($txt['error_invalid_fields'], implode(', ', $invalid_fields)); } // Any flat-out missing? if (!empty($missing_fields) && !$context['overriding_fields']) { $context['shd_errors'][] = 'missing_fields'; $txt['error_missing_fields'] = sprintf($txt['error_missing_fields'], implode(', ', $missing_fields)); } if ($context['can_override_fields'] && !empty($missing_fields)) { $context['ticket_form']['additional_opts']['override_cf'] = array('show' => true, 'checked' => false, 'text' => $txt['shd_override_cf']); } if (!empty($context['shd_errors']) || !empty($context['ticket_form']['preview'])) { checkSubmitOnce('free'); // Anything else for redisplaying the form $context['page_title'] = $txt['shd_helpdesk']; $context['sub_template'] = 'reply_post'; // Oh, yeah, we need to look at the existing notifications pinging stuff, if appropriate, and reset that. if ($context['can_ping'] && !empty($_POST['notify']) && is_array($_POST['notify'])) { $items = array(); foreach ($_POST['notify'] as $item) { if (!empty($item) && is_numeric($item)) { $items[] = $item; } } if (!empty($items)) { $context['notification_ping_list'] = implode(',', $items); } } shd_check_attachments(); // Set up the fancy editor shd_postbox('shd_message', un_preparsecode($_POST['shd_message']), array('post_button' => $new_reply ? $txt['shd_reply_ticket'] : $txt['shd_ticket_edit_reply'])); // Build the link tree and navigation $context['linktree'][] = array('name' => $txt['shd_reply_ticket']); checkSubmitOnce('register'); } else { // It all worked, w00t, so let's get ready to rumble $attachIDs = shd_handle_attachments(); if ($new_reply) { // So... what is the new status? $new_status = shd_determine_status('reply', $ticketinfo['starter_id'], $user_info['id'], -1, $context['ticket_form']['dept']); // We explicitly don't care about how many replies - but it must be non-zero. Default in function spec is -1. // Now to add the ticket details $posterOptions = array('id' => $user_info['id']); $msgOptions = array('body' => $_POST['shd_message'], 'id' => $context['ticket_form']['msg'], 'smileys_enabled' => empty($context['ticket_form']['disable_smileys']), 'attachments' => $attachIDs, 'custom_fields' => !empty($context['ticket_form']['custom_fields'][$context['ticket_form']['msg']]) ? $context['ticket_form']['custom_fields'][$context['ticket_form']['msg']] : array()); $ticketOptions = array('id' => $context['ticket_form']['ticket'], 'mark_as_read' => true, 'status' => $new_status); if (!empty($context['can_solve']) && !empty($_POST['resolve_ticket'])) { $ticketOptions['status'] = TICKET_STATUS_CLOSED; shd_log_action('resolve', array('ticket' => $context['ticket_id'], 'subject' => $ticketinfo['subject'])); } shd_create_ticket_post($msgOptions, $ticketOptions, $posterOptions); // Handle notifications require_once $sourcedir . '/sd_source/SimpleDesk-Notifications.php'; shd_notifications_notify_newreply($msgOptions, $ticketOptions, $posterOptions); } else { // Only add what has actually changed // Now to add the ticket details $posterOptions = array(); $msgOptions = array('id' => $context['ticket_form']['msg'], 'attachments' => $attachIDs, 'custom_fields' => !empty($context['ticket_form']['custom_fields'][$context['ticket_form']['msg']]) ? $context['ticket_form']['custom_fields'][$context['ticket_form']['msg']] : array()); $ticketOptions = array('id' => $context['ticket_form']['ticket']); if ((bool) $ticketinfo['smileys_enabled'] == $context['ticket_form']['disable_smileys']) { // since one is enabled, one is 'now disable'... $msgOptions['smileys_enabled'] = !$context['ticket_form']['disable_smileys']; } if ($reply['body'] != $context['ticket_form']['reply']) { $msgOptions['body'] = $context['ticket_form']['reply']; } if (isset($msgOptions['body'])) { $msgOptions['modified'] = array('id' => $user_info['id'], 'name' => $user_info['name'], 'time' => time()); } if (!empty($context['can_solve']) && !empty($_POST['resolve_ticket'])) { $ticketOptions['status'] = TICKET_STATUS_CLOSED; shd_log_action('resolve', array('ticket' => $context['ticket_id'], 'subject' => $ticketinfo['subject'])); } // DOOOOOOOO EEEEEEEEEEET NAO! shd_modify_ticket_post($msgOptions, $ticketOptions, $posterOptions); // OK, did we get any custom fields back? foreach ($context['custom_fields_updated'] as $field) { $action = 'cf_' . ($field['scope'] == CFIELD_TICKET ? 'tkt' : 'rpl') . (empty($field['default']) ? 'change_' : 'chgdef_') . ($field['visible'][0] ? 'user' : '') . ($field['visible'][1] ? 'staff' : '') . 'admin'; unset($field['default'], $field['scope'], $field['visible']); $field['subject'] = $ticketinfo['subject']; shd_log_action($action, $field); } } $context['ticket_form']['msg'] = $msgOptions['id']; shd_done_posting(); } }
/** * Handles moving a topic into the helpdesk. * * After checking permissions, and so on, begin to actually move posts. * * Broadly this is done using {@link shd_create_ticket_post()}, which has hooks specifically to deal with post modification times (written in specifically to ease this function's workload) * * Operations: * - get the topic information (and checking topic access permission in the process) * - identify the status of the topic (new/with staff/with user) * - create the new ticket from these details * - assuming there are replies, query for them * - step through and repost * - send the notification PM if we're doing that * - update the attachments table * - update the action log * - remove the topic from the forum * * @see shd_topictoticket() * @since 1.0 */ function shd_topictoticket2() { global $smcFunc, $context, $txt, $modSettings, $scripturl, $sourcedir; checkSession(); checkSubmitOnce('check'); $_REQUEST['dept'] = isset($_REQUEST['dept']) ? (int) $_REQUEST['dept'] : 0; if (empty($_REQUEST['dept'])) { $_REQUEST['dept'] = -1; } // which is never a valid department! if (!shd_allowed_to('shd_topic_to_ticket', $_REQUEST['dept']) || !empty($modSettings['shd_helpdesk_only']) || !empty($modSettings['shd_disable_tickettotopic'])) { fatal_lang_error('shd_cannot_move_topic', false); } if (empty($_REQUEST['topic'])) { fatal_lang_error('shd_no_topic'); } $context['topic_id'] = (int) $_REQUEST['topic']; // Just in case, are they cancelling? if (isset($_REQUEST['cancel'])) { redirectexit('topic=' . $context['topic_id']); } if (isset($_POST['send_pm']) && (!isset($_POST['pm_content']) || trim($_POST['pm_content']) == '')) { fatal_lang_error('shd_move_no_pm_topic', false); } require_once $sourcedir . '/sd_source/Subs-SimpleDeskPost.php'; // Fetch the topic information. $request = shd_db_query('', ' SELECT m.subject, t.id_board, t.id_member_started, m.body, t.id_first_msg, m.smileys_enabled, t.id_member_updated, t.num_replies, m.poster_email, m.poster_name, m.poster_ip, m.poster_time, m.modified_time, m.modified_name, m.id_msg FROM {db_prefix}topics AS t INNER JOIN {db_prefix}messages AS m ON (m.id_msg = t.id_first_msg) INNER JOIN {db_prefix}boards AS b ON (t.id_board = b.id_board) WHERE {query_see_board} AND t.id_topic = {int:topic} LIMIT 1', array('topic' => $context['topic_id'])); if ($smcFunc['db_num_rows']($request) == 0) { fatal_lang_error('shd_move_ticket_not_created'); } else { list($subject, $board, $owner, $body, $firstmsg, $smileys_enabled, $memberupdated, $numreplies, $postername, $posteremail, $posterip, $postertime, $modified_time, $modified_name, $smf_id_msg) = $smcFunc['db_fetch_row']($request); } $smcFunc['db_free_result']($request); // Figure out what the status of the ticket should be. $status = shd_determine_status('topictoticket', $owner, $memberupdated, $numreplies, $_REQUEST['dept']); // Are we changing the subject? $old_subject = $subject; $subject = !empty($_POST['change_subject']) && !empty($_POST['subject']) ? $_POST['subject'] : $subject; // Just before we do this, make sure we call any hooks. $context and $_POST have lots of interesting things for us. call_integration_hook('shd_hook_topictoticket'); // All okay, it seems. Let's go create the ticket. $msg_assoc = array(); $msgOptions = array('body' => $body, 'smileys_enabled' => !empty($smileys_enabled) ? 1 : 0, 'modified' => array('time' => $modified_time, 'name' => $modified_name), 'time' => $postertime); $ticketOptions = array('dept' => $_REQUEST['dept'], 'subject' => $subject, 'mark_as_read' => false, 'private' => false, 'status' => $status, 'urgency' => 0, 'assigned' => 0); $posterOptions = array('id' => $owner, 'name' => $postername, 'email' => $posteremail, 'ip' => $posterip); shd_create_ticket_post($msgOptions, $ticketOptions, $posterOptions); $msg_assoc[$smf_id_msg] = $msgOptions['id']; // Ticket created, let's dig out the replies and post them in the ticket, if there are any. if (isset($ticketOptions['id'])) { $request = shd_db_query('', ' SELECT body, id_member, poster_time, poster_name, poster_email, poster_ip, smileys_enabled, id_msg FROM {db_prefix}messages WHERE id_topic = {int:topic} AND id_msg != {int:topic_msg}', array('topic' => $context['topic_id'], 'topic_msg' => $firstmsg)); $num_replies = $smcFunc['db_num_rows']($request) + 1; // Plus one since we want to count the main ticket post as well. // The ID of the ticket we created $ticket = $ticketOptions['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)) { $msgOptions = array('body' => $row['body'], 'smileys_enabled' => !empty($row['smileys_enabled']) ? 1 : 0); $ticketOptions = array('id' => $ticket, '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'] : ''); shd_create_ticket_post($msgOptions, $ticketOptions, $posterOptions); $msg_assoc[$row['id_msg']] = $msgOptions['id']; } } // Ticket: check; Replies: check; Notfiy the topic starter, if desired. 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' => $owner)); list($userid, $username) = $smcFunc['db_fetch_row']($request); $smcFunc['db_free_result']($request); // Fix the content $replacements = array('{user}' => $username, '{subject}' => $old_subject, '{link}' => $scripturl . '?action=helpdesk;sa=ticket;ticket=' . $ticket); $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_topic'], un_htmlspecialchars($message)); } // And now for something completely different: attachments if (!empty($msg_assoc)) { // 1. Get all the attachments for these messages from the attachments table $attachIDs = array(); $query = shd_db_query('', ' SELECT id_attach, id_msg FROM {db_prefix}attachments WHERE id_msg IN ({array_int:smf_msgs})', array('smf_msgs' => array_keys($msg_assoc))); while ($row = $smcFunc['db_fetch_assoc']($query)) { $attachIDs[] = $row; } $smcFunc['db_free_result']($query); if (!empty($attachIDs)) { // 2. Do the switch // 2.1. Add them to SD's tables $array = array(); foreach ($attachIDs as $attach) { $array[] = array($attach['id_attach'], $ticket, $msg_assoc[$attach['id_msg']]); } $smcFunc['db_insert']('replace', '{db_prefix}helpdesk_attachments', array('id_attach' => 'int', 'id_ticket' => 'int', 'id_msg' => 'int'), $array, array('id_attach')); // 2.2. "Remove" them from SMF's table shd_db_query('', ' UPDATE {db_prefix}attachments SET id_msg = 0 WHERE id_msg IN ({array_int:smf_msgs})', array('smf_msgs' => array_keys($msg_assoc))); } } // Now we'll add this to the log. $log_params = array('subject' => $subject, 'ticket' => $ticket); shd_log_action('topictoticket', $log_params); // Update post counts. $request = shd_db_query('', ' SELECT id_member FROM {db_prefix}messages WHERE id_topic = {int:topic}', array('topic' => $context['topic_id'])); $posters = array(); while ($row = $smcFunc['db_fetch_assoc']($request)) { if (!isset($posters[$row['id_member']])) { $posters[$row['id_member']] = 0; } $posters[$row['id_member']]++; } $smcFunc['db_free_result']($request); foreach ($posters as $id_member => $posts) { updateMemberData($id_member, array('posts' => 'posts - ' . $posts)); } // Lastly, delete the topic from the database. shd_db_query('', ' DELETE FROM {db_prefix}topics WHERE id_topic = {int:topic} LIMIT 1', array('topic' => $context['topic_id'])); // And the replies, too. shd_db_query('', ' DELETE FROM {db_prefix}messages WHERE id_topic = {int:topic}', array('topic' => $context['topic_id'])); // Update the stats. require_once $sourcedir . '/Subs-Post.php'; updateStats('message'); updateStats('topic'); updateLastMessages($board); // Update board post counts. shd_db_query('', ' UPDATE {db_prefix}boards SET num_topics = num_topics - 1, num_posts = num_posts - {int:num_posts} WHERE id_board = {int:board}', array('board' => $board, 'num_posts' => $num_replies)); } else { fatal_lang_error('shd_move_ticket_not_created', false); } // Send them to the ticket. redirectexit('action=helpdesk;sa=ticket;ticket=' . $ticket); }