function archiveTicket($id) { $ticket = new Ticket($id); $tid = $ticket->getExtId(); // Delete orphan tickets. $owner = $ticket->getOwner(); if (!$owner) { $ticket->delete(); return; } $o_name = $owner->getName(); $threads = $ticket->getThreadEntries(array('M', 'R', 'N')); $out = ["id" => $tid, "department" => $ticket->getDeptName(), "subject" => $ticket->getSubject(), "opened" => $ticket->getOpenDate(), "closed" => $ticket->getCloseDate(), "owner" => (isset($o_name->name) ? $o_name->name : '') . " <" . $owner->getEmail() . ">", "thread" => []]; $date = date("Y-m-d", strtotime($out["opened"])); $path = TICKET_PATH . "/" . $date . "/"; if (!@file_exists($path)) { @mkdir($path); } // Individual messages. foreach ($threads as $th) { $out["thread"][] = ["id" => $th["id"], "staff_id" => $th["staff_id"], "thread_type" => $th["thread_type"], "poster" => $th["poster"], "title" => $th["title"], "body" => $th["body"], "created" => $th["created"], "updated" => $th["updated"], "attachments" => intval($th["attachments"])]; // Process attachments. if ($th["attachments"] != 0) { $entry = $ticket->getThreadEntry($th['id']); $attachments = $entry->getAttachments(); foreach ($attachments as $a) { $file = Attachment::lookup($a["attach_id"])->getFile(); $ext = $ext = strtolower(substr(strrchr($file->getName(), '.'), 1)); $fname = $tid . "_" . $th["id"] . "." . $ext; @file_put_contents(ATTACHMENT_PATH . "/" . $fname, $file->getData()); } } } // write the ticket to disk file_put_contents($path . $tid, json_encode($out, JSON_PRETTY_PRINT)); // delete the ticket from the db $ticket->delete(); }
} } //See if we can fetch local ticket id associated with the ID given if (!$errors && is_numeric($ticketID) && Validator::is_email($email) && ($tid = Ticket::getIdByExtId($ticketID))) { //At this point we know the ticket is valid. $ticket = new Ticket($tid); //TODO: 1) Check how old the ticket is...3 months max?? 2) Must be the latest 5 tickets?? //Check the email given. if ($ticket->getId() && strcasecmp($ticket->getEMail(), $email) == 0) { //valid match...create session goodies for the client. $user = new ClientSession($email, $ticket->getId()); $_SESSION['_client'] = array(); //clear. $_SESSION['_client']['userID'] = $ticket->getEmail(); //Email $_SESSION['_client']['key'] = $ticket->getExtId(); //Ticket ID --acts as password when used with email. See above. $_SESSION['_client']['token'] = $user->getSessionToken(); $_SESSION['TZ_OFFSET'] = $cfg->getTZoffset(); $_SESSION['daylight'] = $cfg->observeDaylightSaving(); //Log login info... $msg = sprintf("%s/%s logged in [%s]", $ticket->getEmail(), $ticket->getExtId(), $_SERVER['REMOTE_ADDR']); Sys::log(LOG_DEBUG, 'User login', $msg); //Redirect tickets.php session_write_close(); session_regenerate_id(); @header("Location: tickets.php"); require_once 'tickets.php'; //Just incase. of header already sent error. exit; }
} } //See if we can fetch local ticket id associated with the ID given if (!$errors && is_numeric($ticketID) && Validator::is_email($email) && ($tid = Ticket::getIdByExtId($ticketID))) { //At this point we know that a ticket with the given number exists. $ticket = new Ticket($tid); //TODO: 1) Check how old the ticket is...3 months max?? 2) Must be the latest 5 tickets?? //Check the email given. if ($ticket->getId() && strcasecmp($ticket->getEMail(), $email) == 0) { //valid email match...create session goodies for the user. $user = new UserSession($email, $ticket->getId()); $_SESSION['_user'] = array(); //clear. $_SESSION['_user']['userID'] = $ticket->getEmail(); //Email $_SESSION['_user']['key'] = $ticket->getExtId(); //Ticket ID --acts as password when used with email. See above. $_SESSION['_user']['token'] = $user->getSessionToken(); $_SESSION['TZ_OFFSET'] = $cfg->getTZoffset(); $_SESSION['daylight'] = $cfg->observeDaylightSaving(); //Log login info... $msg = sprintf("%s/%s " . _("logged in"), $ticket->getEmail(), $ticket->getExtId()); Sys::log(LOG_DEBUG, 'User login', $msg, $ticket->getEmail()); //Redirect tickets.php session_write_close(); session_regenerate_id(); @header("Location: tickets.php"); require_once 'tickets.php'; //Just incase. of header already sent error. exit; }
$errors['err'] = 'You\'ve reached maximum failed login attempts allowed. Try again after 5 minutes or <a href="new.php">open a new ticket</a>'; } //See if we can fetch local ticket id associated with the ID given if (!$errors && is_numeric($ticketID) && Validator::is_email($email) && ($tid = Ticket::getIdByExtId($ticketID))) { //At this point we know the ticket is valid. $ticket = new Ticket($tid); //TODO: 1) Check how old the ticket is...3 months max?? 2) Must be the latest 5 tickets?? //Check the email given. if ($ticket->getId() && strcasecmp($ticket->getEMail(), $email) == 0) { //valid match...create session goodies for the client. $user = new ClientSession($email, $ticket->getId()); $_SESSION['_client'] = array(); //clear. $_SESSION['_client']['userID'] = $ticket->getEmail(); //Email $_SESSION['_client']['key'] = $ticket->getExtId(); //Ticket ID --acts as password when used with email. See above. $_SESSION['_client']['token'] = $user->getSessionToken(); $_SESSION['TZ_OFFSET'] = $cfg->getTZoffset(); $_SESSION['daylight'] = $cfg->observeDaylightSaving(); //Redirect view.php @header("Location: view.php"); require 'view.php'; //Just incase. of header already sent error. exit; } } //If we get to this point we know the login failed. //TODO: login strikes should be DB based for better security checks ( session can be reset!) $loginmsg = 'Invalid login'; $_SESSION['_client']['strikes'] += 1;
function previewTicket($tid) { global $thisstaff; $ticket = new Ticket($tid); $resp = sprintf('<div style="width:500px;"> <strong>Ticket #%d Preview</strong><br>INFO HERE!!', $ticket->getExtId()); $options[] = array('action' => 'Thread (' . $ticket->getThreadCount() . ')', 'url' => "tickets.php?id={$tid}"); if ($ticket->getNumNotes()) { $options[] = array('action' => 'Notes (' . $ticket->getNumNotes() . ')', 'url' => "tickets.php?id={$tid}#notes"); } if ($ticket->isOpen()) { $options[] = array('action' => 'Reply', 'url' => "tickets.php?id={$tid}#reply"); } if ($thisstaff->canAssignTickets()) { $options[] = array('action' => $ticket->isAssigned() ? 'Reassign' : 'Assign', 'url' => "tickets.php?id={$tid}#assign"); } if ($thisstaff->canTransferTickets()) { $options[] = array('action' => 'Transfer', 'url' => "tickets.php?id={$tid}#transfer"); } $options[] = array('action' => 'Post Note', 'url' => "tickets.php?id={$tid}#note"); if ($options) { $resp .= '<ul class="tip_menu">'; foreach ($options as $option) { $resp .= sprintf('<li><a href="%s">%s</a></li>', $option['url'], $option['action']); } $resp .= '</ul>'; } $resp .= '</div>'; return $resp; }
function create($var, &$errors, $origin, $autorespond = true, $alertstaff = true) { global $cfg, $thisclient, $_FILES; $id = 0; $fields = array(); $fields['name'] = array('type' => 'string', 'required' => 1, 'error' => 'Name required'); $fields['email'] = array('type' => 'email', 'required' => 1, 'error' => 'Valid email required'); $fields['subject'] = array('type' => 'string', 'required' => 1, 'error' => 'Subject required'); $fields['message'] = array('type' => 'text', 'required' => 1, 'error' => 'Message required'); if (strcasecmp($origin, 'web') == 0) { //Help topic only applicable on web tickets. $fields['topicId'] = array('type' => 'int', 'required' => 1, 'error' => 'Select help topic'); } elseif (strcasecmp($origin, 'staff') == 0) { //tickets created by staff...e.g on callins. $fields['deptId'] = array('type' => 'int', 'required' => 1, 'error' => 'Dept. required'); $fields['source'] = array('type' => 'string', 'required' => 1, 'error' => 'Indicate source'); } else { //Incoming emails (PIPE or POP. $fields['emailId'] = array('type' => 'int', 'required' => 1, 'error' => 'Email unknown'); } $fields['pri'] = array('type' => 'int', 'required' => 0, 'error' => 'Invalid Priority'); $fields['phone'] = array('type' => 'phone', 'required' => 0, 'error' => 'Phone # required'); $validate = new Validator($fields); if (!$validate->validate($var)) { $errors = array_merge($errors, $validate->errors()); } //Make sure the email is not banned if (!$errors && BanList::isbanned($var['email'])) { $errors['err'] = 'Ticket denied Error #403'; } if (!$errors && $thisclient && strcasecmp($thisclient->getEmail(), $var['email'])) { $errors['email'] = 'Email mismatch.'; } //check attachment..if any is set ...only set on webbased tickets.. if ($_FILES['attachment']['name'] && $cfg->allowOnlineAttachments()) { if (!$cfg->canUploadFileType($_FILES['attachment']['name'])) { $errors['attachment'] = 'Invalid file type [ ' . $_FILES['attachment']['name'] . ' ]'; } elseif ($_FILES['attachment']['size'] > $cfg->getMaxFileSize()) { $errors['attachment'] = 'File is too big. Max ' . $cfg->getMaxFileSize() . ' bytes allowed'; } } //check ticket limits..if limit set is >0 //TODO: Base ticket limits on SLA... if ($var['email'] && !$errors && $cfg->getMaxOpenTickets() > 0) { $openTickets = Ticket::getOpenTicketsByEmail($var['email']); if ($openTickets >= $cfg->getMaxOpenTickets()) { $errors['err'] = "You've reached the maximum open tickets allowed."; //Send the notice only once (when the limit is reached) incase of autoresponders at client end. if ($cfg->getMaxOpenTickets() == $openTickets && $cfg->sendOverlimitNotice()) { $sql = 'SELECT ticket_overlimit_subj,ticket_overlimit_body FROM ' . EMAIL_TEMPLATE_TABLE . ' WHERE cfg_id=' . db_input($cfg->getId()) . ' AND tpl_id=' . db_input($cfg->getDefaultTemplateId()); $resp = db_query($sql); if (db_num_rows($resp) && (list($subj, $body) = db_fetch_row($resp))) { $body = str_replace("%name", $var['name'], $body); $body = str_replace("%email", $var['email'], $body); $body = str_replace("%url", $cfg->getBaseUrl(), $body); Misc::sendmail($var['email'], $subj, $body, $cfg->getNoReplyEmail()); } } //Alert admin...this might be spammy (no option to disable)...but it is helpful..I think. $msg = 'Support ticket request denied for ' . $var['email'] . "\n" . 'Open ticket:' . $openTickets . "\n" . 'Max Allowed:' . $cfg->getMaxOpenTickets() . "\n"; Misc::alertAdmin('Overlimit Notice', $msg); } } //Any error above is fatal. if ($errors) { return 0; } // OK...just do it. $deptId = $var['deptId']; //pre-selected Dept if any. $priorityId = $var['pri']; $source = ucfirst($var['source']); // Intenal mapping magic...see if we need to overwrite anything if (isset($var['topicId']) && !$var['deptId']) { //Ticket created via web by user if ($var['topicId'] && ($topic = new Topic($var['topicId'])) && $topic->getId()) { $deptId = $topic->getDeptId(); $priorityId = $priorityId ? $priorityId : $topic->getPriorityId(); $autorespond = $topic->autoRespond(); } $topic = null; $source = 'Web'; } elseif ($var['emailId'] && !$var['deptId']) { //Emailed Tickets $email = new Email($var['emailId']); if ($email && $email->getId()) { $deptId = $email->getDeptId(); $autorespond = $email->autoRespond(); $priorityId = $priorityId ? $priorityId : $email->getPriorityId(); } $email = null; $source = 'Email'; } elseif ($var['deptId']) { //Opened by staff. $deptId = $var['deptId']; $source = ucfirst($var['source']); } //Last minute checks $priorityId = $priorityId ? $priorityId : $cfg->getDefaultPriorityId(); $deptId = $deptId ? $deptId : $cfg->getDefaultDeptId(); $ipaddress = $var['ip'] ? $var['ip'] : $_SERVER['REMOTE_ADDR']; //We are ready son...hold on to the rails. $extId = Ticket::genExtRandID(); $sql = 'INSERT INTO ' . TICKET_TABLE . ' SET created=NOW() ' . ',ticketID=' . db_input($extId) . ',dept_id=' . db_input($deptId) . ',priority_id=' . db_input($priorityId) . ',email=' . db_input($var['email']) . ',name=' . db_input(Format::striptags($var['name'])) . ',subject=' . db_input(Format::striptags($var['subject'])) . ',phone=' . db_input($var['phone']) . ',ip_address=' . db_input($ipaddress) . ',source=' . db_input($source); //echo $sql; $ticket = null; //return $ticket; if (db_query($sql) && ($id = db_insert_id())) { if (!$cfg->useRandomIds()) { //Sequential ticketIDs support really..really suck arse. $extId = $id; //To make things really easy we are going to use autoincrement ticket_id. db_query('UPDATE ' . TICKET_TABLE . ' SET ticketID=' . db_input($extId) . ' WHERE ticket_id=' . $id); //TODO: RETHING what happens if this fails?? [At the moment on failure random ID is used...making stuff usable] } //Load newly created ticket. $ticket = new Ticket($id); //post the message. $msgid = $ticket->postMessage($var['message'], $var['header'], $source, true); //TODO: recover from postMessage error?? //Upload attachments...web based. if ($_FILES['attachment']['name'] && $cfg->allowOnlineAttachments() && $msgid) { if (!$cfg->allowAttachmentsOnlogin() || $cfg->allowAttachmentsOnlogin() && ($thisclient && $thisclient->isValid())) { $ticket->uploadAttachment($_FILES['attachment'], $msgid, 'M'); //TODO: recover from upload issues? } } $dept = $ticket->getDept(); //SEND OUT NEW TICKET AUTORESP && ALERTS. //New Ticket AutoResponse.. if ($autorespond && $cfg->autoRespONNewTicket() && $dept->autoRespONNewTicket()) { $sql = 'SELECT ticket_autoresp_subj,ticket_autoresp_body FROM ' . EMAIL_TEMPLATE_TABLE . ' WHERE cfg_id=' . db_input($cfg->getId()) . ' AND tpl_id=' . db_input($cfg->getDefaultTemplateId()); $resp = db_query($sql); if ($resp && (list($subj, $body) = db_fetch_row($resp))) { $subj = str_replace("%ticket", $ticket->getExtId(), $subj); $body = str_replace("%ticket", $ticket->getExtId(), $body); $body = str_replace("%name", $ticket->getName(), $body); $body = str_replace("%email", $ticket->getEmail(), $body); $body = str_replace("%url", $cfg->getBaseUrl(), $body); $body = str_replace("%signature", $dept ? $dept->getSignature() : '', $body); $email = $from = $fromName = null; if (!$dept->noreplyAutoResp() && ($email = $dept->getEmail())) { $from = $email->getEmail(); $fromName = $email->getName(); //Reply separator tag. if ($cfg->stripQuotedReply() && ($tag = $cfg->getReplySeparator())) { $body = "\n{$tag}\n\n" . $body; } } else { $from = $cfg->getNoReplyEmail(); } Misc::sendmail($ticket->getEmail(), $subj, $body, $from, $fromName); } } //If enabled...send alert to staff (New Ticket Alert) if ($alertstaff && $cfg->alertONNewTicket() && is_object($ticket)) { $sql = 'SELECT ticket_alert_subj,ticket_alert_body FROM ' . EMAIL_TEMPLATE_TABLE . ' WHERE cfg_id=' . db_input($cfg->getId()) . ' AND tpl_id=' . db_input($cfg->getDefaultTemplateId()); $resp = db_query($sql); if ($resp && (list($subj, $body) = db_fetch_row($resp))) { $body = str_replace("%ticket", $ticket->getExtId(), $body); $body = str_replace("%name", $ticket->getName(), $body); $body = str_replace("%email", $ticket->getEmail(), $body); $body = str_replace("%subject", $ticket->getSubject(), $body); $body = str_replace("%dept", $dept ? $dept->getName() : '', $body); $body = str_replace("%message", $var['message'], $body); $body = str_replace("%url", $cfg->getBaseUrl(), $body); $sentlist = array(); //Admin Alert. if ($cfg->alertAdminONNewTicket()) { $alert = str_replace("%staff", 'Admin', $body); Misc::sendmail($cfg->getAdminEmail(), $subj, $alert, $cfg->getAlertEmail()); $sentlist[] = $cfg->getAdminEmail(); } //get the list $recipients = array(); //Dept. Manager if ($cfg->alertDeptManagerONNewTicket()) { $recipients[] = $dept->getManager(); } //Staff members if ($cfg->alertDeptMembersONNewTicket()) { $sql = 'SELECT staff_id FROM ' . STAFF_TABLE . ' WHERE onvacation=0 AND dept_id=' . db_input($dept->getId()); if (($users = db_query($sql)) && db_num_rows($users)) { while (list($id) = db_fetch_row($users)) { $recipients[] = new Staff($id); } } } //Ok...we are ready to go...baby! foreach ($recipients as $k => $staff) { if (!$staff || !is_object($staff) || !$staff->isAvailable()) { continue; } if (in_array($staff->getEmail(), $sentlist)) { continue; } //avoid duplicate emails. $alert = str_replace("%staff", $staff->getFirstName(), $body); Misc::sendmail($staff->getEmail(), $subj, $alert, $cfg->getAlertEmail()); $sentlist[] = $staff->getEmail(); } } } } return $ticket; }