function cannedResp($id, $format = '') { global $thisstaff, $_GET; include_once INCLUDE_DIR . 'class.canned.php'; if (!$id || !($canned = Canned::lookup($id)) || !$canned->isEnabled()) { Http::response(404, 'No such premade reply'); } //Load ticket. if ($_GET['tid']) { include_once INCLUDE_DIR . 'class.ticket.php'; $ticket = Ticket::lookup($_GET['tid']); } switch ($format) { case 'json': $resp['id'] = $canned->getId(); $resp['ticket'] = $canned->getTitle(); $resp['response'] = $ticket ? $ticket->replaceVars($canned->getResponse()) : $canned->getResponse(); $resp['files'] = $canned->getAttachments(); $response = $this->json_encode($resp); break; case 'txt': default: $response = $ticket ? $ticket->replaceVars($canned->getResponse()) : $canned->getResponse(); } return $response; }
function cannedResp($id, $format = 'text') { global $thisstaff, $cfg; include_once INCLUDE_DIR . 'class.canned.php'; if (!$id || !($canned = Canned::lookup($id)) || !$canned->isEnabled()) { Http::response(404, 'No such premade reply'); } if (!$cfg->isHtmlThreadEnabled()) { $format .= '.plain'; } return $canned->getFormattedResponse($format); }
case 'disable': $sql = 'UPDATE ' . CANNED_TABLE . ' SET isenabled=0 ' . ' WHERE canned_id IN (' . implode(',', db_input($_POST['ids'])) . ')'; if (db_query($sql) && ($num = db_affected_rows())) { if ($num == $count) { $msg = 'Selected canned responses disabled'; } else { $warn = "{$num} of {$count} selected canned responses disabled"; } } else { $errors['err'] = 'Unable to disable selected canned responses'; } break; case 'delete': $i = 0; foreach ($_POST['ids'] as $k => $v) { if (($c = Canned::lookup($v)) && $c->delete()) { $i++; } } if ($i == $count) { $msg = 'Selected canned responses deleted successfully'; } elseif ($i > 0) { $warn = "{$i} of {$count} selected canned responses deleted"; } elseif (!$errors['err']) { $errors['err'] = 'Unable to delete selected canned responses'; } break; default: $errors['err'] = 'Unknown command'; } }
function cannedResponse($tid, $cid, $format = 'text') { global $thisstaff, $cfg; if (!($ticket = Ticket::lookup($tid)) || !$ticket->checkStaffAccess($thisstaff)) { Http::response(404, 'Unknown ticket ID'); } if ($cid && !is_numeric($cid)) { if (!($response = $ticket->getThread()->getVar($cid))) { Http::response(422, 'Unknown ticket variable'); } // Ticket thread variables are assumed to be quotes $response = "<br/><blockquote>{$response}</blockquote><br/>"; // Return text if html thread is not enabled if (!$cfg->isHtmlThreadEnabled()) { $response = Format::html2text($response, 90); } // XXX: assuming json format for now. return Format::json_encode(array('response' => $response)); } if (!$cfg->isHtmlThreadEnabled()) { $format .= '.plain'; } $varReplacer = function (&$var) use($ticket) { return $ticket->replaceVars($var); }; include_once INCLUDE_DIR . 'class.canned.php'; if (!$cid || !($canned = Canned::lookup($cid)) || !$canned->isEnabled()) { Http::response(404, 'No such premade reply'); } return $canned->getFormattedResponse($format, $varReplacer); }
/** * Loads data from the I18N_DIR for the target language into the * database. This is intended to be done at the time of installation; * however, care should be taken in this process to ensure that the * process could be repeated if an administrator wanted to change the * system language and reload the data. */ function loadDefaultData() { # notrans -- do not translate the contents of this array $models = array('department.yaml' => 'Dept::create', 'sla.yaml' => 'SLA::create', 'form.yaml' => 'DynamicForm::create', 'list.yaml' => 'DynamicList::create', 'help_topic.yaml' => 'Topic::create', 'filter.yaml' => 'Filter::create', 'team.yaml' => 'Team::create', 'organization.yaml' => 'Organization::__create', 'ticket_status.yaml' => 'TicketStatus::__create', 'group.yaml' => 'Group::create', 'file.yaml' => 'AttachmentFile::create', 'sequence.yaml' => 'Sequence::__create'); $errors = array(); foreach ($models as $yaml => $m) { if ($objects = $this->getTemplate($yaml)->getData()) { foreach ($objects as $o) { if ($m && is_callable($m)) { @call_user_func_array($m, array($o, &$errors)); } // TODO: Add a warning to the success page for errors // found here $errors = array(); } } } // Priorities $priorities = $this->getTemplate('priority.yaml')->getData(); foreach ($priorities as $name => $info) { $sql = 'INSERT INTO ' . PRIORITY_TABLE . ' SET priority=' . db_input($name) . ', priority_id=' . db_input($info['priority_id']) . ', priority_desc=' . db_input($info['priority_desc']) . ', priority_color=' . db_input($info['priority_color']) . ', priority_urgency=' . db_input($info['priority_urgency']); db_query($sql); } // Configuration require_once INCLUDE_DIR . 'class.config.php'; if (($tpl = $this->getTemplate('config.yaml')) && ($data = $tpl->getData())) { foreach ($data as $section => $items) { $_config = new Config($section); foreach ($items as $key => $value) { $_config->set($key, $value); } } } // Load core config $_config = new OsticketConfig(); // Determine reasonable default max_file_size $max_size = Format::filesize2bytes(strtoupper(ini_get('upload_max_filesize'))); $val = (int) $max_size / 2; $po2 = 1; while ($po2 < $val) { $po2 <<= 1; } $_config->set('max_file_size', $po2); // Pages and content foreach (array('landing', 'thank-you', 'offline', 'registration-staff', 'pwreset-staff', 'banner-staff', 'registration-client', 'pwreset-client', 'banner-client', 'registration-confirm', 'registration-thanks', 'access-link') as $type) { $tpl = $this->getTemplate("templates/page/{$type}.yaml"); if (!($page = $tpl->getData())) { continue; } $sql = 'INSERT INTO ' . PAGE_TABLE . ' SET type=' . db_input($type) . ', name=' . db_input($page['name']) . ', body=' . db_input($page['body']) . ', lang=' . db_input($tpl->getLang()) . ', notes=' . db_input($page['notes']) . ', created=NOW(), updated=NOW(), isactive=1'; if (db_query($sql) && ($id = db_insert_id()) && in_array($type, array('landing', 'thank-you', 'offline'))) { $_config->set("{$type}_page_id", $id); } } // Default Language $_config->set('system_language', $this->langs[0]); // content_id defaults to the `id` field value db_query('UPDATE ' . PAGE_TABLE . ' SET content_id=id'); // Canned response examples if (($tpl = $this->getTemplate('templates/premade.yaml')) && ($canned = $tpl->getData())) { foreach ($canned as $c) { if (($id = Canned::create($c, $errors)) && isset($c['attachments'])) { $premade = Canned::lookup($id); foreach ($c['attachments'] as $a) { $premade->attachments->save($a, false); } } } } // Email templates // TODO: Lookup tpl_id if ($objects = $this->getTemplate('email_template_group.yaml')->getData()) { foreach ($objects as $o) { $o['lang_id'] = $this->langs[0]; $tpl = EmailTemplateGroup::create($o, $errors); } } // This shouldn't be necessary $tpl = EmailTemplateGroup::lookup(1); foreach ($tpl::$all_names as $name => $info) { if (($tp = $this->getTemplate("templates/email/{$name}.yaml")) && ($t = $tp->getData())) { $t['tpl_id'] = $tpl->getId(); $t['code_name'] = $name; $id = EmailTemplate::create($t, $errors); if ($id && ($template = EmailTemplate::lookup($id)) && ($ids = Draft::getAttachmentIds($t['body']))) { $template->attachments->upload($ids, true); } } } }
function postCannedReply($canned, $msgId, $alert = true) { global $ost, $cfg; if (!is_object($canned) && !($canned = Canned::lookup($canned)) || !$canned->isEnabled()) { return false; } $files = array(); foreach ($canned->attachments->getAll() as $file) { $files[] = $file['id']; } if ($cfg->isHtmlThreadEnabled()) { $response = new HtmlThreadBody($this->replaceVars($canned->getHtml())); } else { $response = new TextThreadBody($this->replaceVars($canned->getPlainText())); } $info = array('msgId' => $msgId, 'poster' => __('SYSTEM (Canned Reply)'), 'response' => $response, 'cannedattachments' => $files); $errors = array(); if (!($response = $this->postReply($info, $errors, false, false))) { return null; } $this->markUnAnswered(); if (!$alert) { return $response; } $dept = $this->getDept(); if (($email = $dept->getEmail()) && ($tpl = $dept->getTemplate()) && ($msg = $tpl->getAutoReplyMsgTemplate())) { if ($dept && $dept->isPublic()) { $signature = $dept->getSignature(); } else { $signature = ''; } $msg = $this->replaceVars($msg->asArray(), array('response' => $response, 'signature' => $signature, 'recipient' => $this->getOwner())); $attachments = $cfg->emailAttachments() && $files ? $response->getAttachments() : array(); $options = array('inreplyto' => $response->getEmailMessageId(), 'references' => $response->getEmailReferences(), 'thread' => $response); $email->sendAutoReply($this, $msg['subj'], $msg['body'], $attachments, $options); } return $response; }
function create($vars, &$errors, $origin, $autorespond = true, $alertstaff = true) { global $ost, $cfg, $thisclient, $_FILES; //Check for 403 if ($vars['email'] && Validator::is_email($vars['email'])) { //Make sure the email address is not banned if (EmailFilter::isBanned($vars['email'])) { $errors['err'] = 'Ticket denied. Error #403'; $ost->logWarning('Ticket denied', 'Banned email - ' . $vars['email']); return 0; } //Make sure the open ticket limit hasn't been reached. (LOOP CONTROL) if ($cfg->getMaxOpenTickets() > 0 && strcasecmp($origin, 'staff') && ($client = Client::lookupByEmail($vars['email'])) && ($openTickets = $client->getNumOpenTickets()) && $openTickets >= $cfg->getMaxOpenTickets()) { $errors['err'] = "You've reached the maximum open tickets allowed."; $ost->logWarning('Ticket denied -' . $vars['email'], sprintf('Max open tickets (%d) reached for %s ', $cfg->getMaxOpenTickets(), $vars['email'])); return 0; } } // Make sure email contents should not be rejected if (($email_filter = new EmailFilter($vars)) && ($filter = $email_filter->shouldReject())) { $errors['err'] = 'Ticket denied. Error #403'; $ost->logWarning('Ticket denied', sprintf('Banned email - %s by filter "%s"', $vars['email'], $filter->getName())); return 0; } $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'); switch (strtolower($origin)) { case 'web': $fields['topicId'] = array('type' => 'int', 'required' => 1, 'error' => 'Select help topic'); break; case 'staff': $fields['deptId'] = array('type' => 'int', 'required' => 1, 'error' => 'Dept. required'); $fields['topicId'] = array('type' => 'int', 'required' => 1, 'error' => 'Topic required'); $fields['duedate'] = array('type' => 'date', 'required' => 0, 'error' => 'Invalid date - must be MM/DD/YY'); case 'api': $fields['source'] = array('type' => 'string', 'required' => 1, 'error' => 'Indicate source'); break; case 'email': $fields['emailId'] = array('type' => 'int', 'required' => 1, 'error' => 'Email unknown'); break; default: # TODO: Return error message $errors['err'] = $errors['origin'] = 'Invalid origin given'; } $fields['priorityId'] = array('type' => 'int', 'required' => 0, 'error' => 'Invalid Priority'); $fields['phone'] = array('type' => 'phone', 'required' => 0, 'error' => 'Valid phone # required'); if (!Validator::process($fields, $vars, $errors) && !$errors['err']) { $errors['err'] = 'Missing or invalid data - check the errors and try again'; } //Make sure phone extension is valid if ($vars['phone_ext']) { if (!is_numeric($vars['phone_ext']) && !$errors['phone']) { $errors['phone'] = 'Invalid phone ext.'; } elseif (!$vars['phone']) { //make sure they just didn't enter ext without phone # XXX: reconsider allowing! $errors['phone'] = 'Phone number required'; } } //Make sure the due date is valid if ($vars['duedate']) { if (!$vars['time'] || strpos($vars['time'], ':') === false) { $errors['time'] = 'Select time'; } elseif (strtotime($vars['duedate'] . ' ' . $vars['time']) === false) { $errors['duedate'] = 'Invalid duedate'; } elseif (strtotime($vars['duedate'] . ' ' . $vars['time']) <= time()) { $errors['duedate'] = 'Due date must be in the future'; } } # Perform email filter actions on the new ticket arguments XXX: Move filter to the top and check for reject... if (!$errors && $email_filter) { $email_filter->apply($vars); } # Some things will need to be unpacked back into the scope of this # function if (isset($vars['autorespond'])) { $autorespond = $vars['autorespond']; } //Any error above is fatal. if ($errors) { return 0; } // OK...just do it. $deptId = $vars['deptId']; //pre-selected Dept if any. $priorityId = $vars['priorityId']; $source = ucfirst($vars['source']); $topic = NULL; // Intenal mapping magic...see if we need to overwrite anything if (isset($vars['topicId']) && ($topic = Topic::lookup($vars['topicId']))) { //Ticket created via web by user/or staff $deptId = $deptId ? $deptId : $topic->getDeptId(); $priorityId = $priorityId ? $priorityId : $topic->getPriorityId(); if ($autorespond) { $autorespond = $topic->autoRespond(); } $source = $vars['source'] ? $vars['source'] : 'Web'; } elseif ($vars['emailId'] && !$vars['deptId'] && ($email = Email::lookup($vars['emailId']))) { //Emailed Tickets $deptId = $email->getDeptId(); $priorityId = $priorityId ? $priorityId : $email->getPriorityId(); if ($autorespond) { $autorespond = $email->autoRespond(); } $email = null; $source = 'Email'; } elseif ($vars['deptId']) { //Opened by staff. $deptId = $vars['deptId']; $source = ucfirst($vars['source']); } //Last minute checks $priorityId = $priorityId ? $priorityId : $cfg->getDefaultPriorityId(); $deptId = $deptId ? $deptId : $cfg->getDefaultDeptId(); $topicId = $vars['topicId'] ? $vars['topicId'] : 0; $ipaddress = $vars['ip'] ? $vars['ip'] : $_SERVER['REMOTE_ADDR']; //We are ready son...hold on to the rails. $extId = Ticket::genExtRandID(); $sql = 'INSERT INTO ' . TICKET_TABLE . ' SET created=NOW() ' . ' ,lastmessage= NOW()' . ' ,ticketID=' . db_input($extId) . ' ,dept_id=' . db_input($deptId) . ' ,topic_id=' . db_input($topicId) . ' ,priority_id=' . db_input($priorityId) . ' ,email=' . db_input($vars['email']) . ' ,name=' . db_input(Format::striptags($vars['name'])) . ' ,subject=' . db_input(Format::striptags($vars['subject'])) . ' ,phone="' . db_input($vars['phone'], false) . '"' . ' ,phone_ext=' . db_input($vars['phone_ext'] ? $vars['phone_ext'] : '') . ' ,ip_address=' . db_input($ipaddress) . ' ,source=' . db_input($source); //Make sure the origin is staff - avoid firebug hack! if ($vars['duedate'] && !strcasecmp($origin, 'staff')) { $sql .= ' ,duedate=' . db_input(date('Y-m-d G:i', Misc::dbtime($vars['duedate'] . ' ' . $vars['time']))); } if (!db_query($sql) || !($id = db_insert_id()) || !($ticket = Ticket::lookup($id))) { return null; } /* -------------------- POST CREATE ------------------------ */ 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 . ' LIMIT 1'); //TODO: RETHING what happens if this fails?? [At the moment on failure random ID is used...making stuff usable] } $dept = $ticket->getDept(); //post the message. $msgid = $ticket->postMessage($vars['message'], $source, $vars['mid'], $vars['header'], true); // Configure service-level-agreement for this ticket $ticket->selectSLAId($vars['slaId']); //Auto assign staff or team - auto assignment based on filter rules. if ($vars['staffId'] && !$vars['assignId']) { $ticket->assignToStaff($vars['staffId'], 'auto-assignment'); } if ($vars['teamId'] && !$vars['assignId']) { $ticket->assignToTeam($vars['teamId'], 'auto-assignment'); } /********** double check auto-response ************/ //Overwrite auto responder if the FROM email is one of the internal emails...loop control. if ($autorespond && Email::getIdByEmail($ticket->getEmail())) { $autorespond = false; } if ($autorespond && $dept && !$dept->autoRespONNewTicket()) { $autorespond = false; } # Messages that are clearly auto-responses from email systems should # not have a return 'ping' message if ($autorespond && $vars['header'] && EmailFilter::isAutoResponse(Mail_Parse::splitHeaders($vars['header']))) { $autorespond = false; } //Don't auto respond to mailer daemons. if ($autorespond && (strpos(strtolower($vars['email']), 'mailer-daemon@') !== false || strpos(strtolower($vars['email']), 'postmaster@') !== false)) { $autorespond = false; } if ($vars['cannedResponseId'] && ($canned = Canned::lookup($vars['cannedResponseId'])) && $canned->isEnabled()) { $files = array(); foreach ($canned->getAttachments() as $file) { $files[] = $file['id']; } $ticket->postReply(array('msgId' => $msgid, 'response' => $ticket->replaceTemplateVars($canned->getResponse()), 'cannedattachments' => $files), $errors, true); // If a canned-response is immediately queued for this ticket, // disable the autoresponse $autorespond = false; } /***** See if we need to send some alerts ****/ $ticket->onNewTicket($vars['message'], $autorespond, $alertstaff); /************ check if the user JUST reached the max. open tickets limit **********/ if ($cfg->getMaxOpenTickets() > 0 && ($client = $ticket->getClient()) && $client->getNumOpenTickets() == $cfg->getMaxOpenTickets()) { $ticket->onOpenLimit($autorespond && strcasecmp($origin, 'staff')); } /* Start tracking ticket lifecycle events */ $ticket->logEvent('created'); /* Phew! ... time for tea (KETEPA) */ return $ticket; }
function postCannedReply($canned, $msgId, $alert = true) { global $ost, $cfg; if (!is_object($canned) && !($canned = Canned::lookup($canned)) || !$canned->isEnabled()) { return false; } $files = array(); foreach ($canned->getAttachments() as $file) { $files[] = $file['id']; } $info = array('msgId' => $msgId, 'poster' => 'SYSTEM (Canned Reply)', 'response' => $this->replaceVars($canned->getResponse()), 'cannedattachments' => $files); $errors = array(); if (!($response = $this->postReply($info, $errors, false))) { return null; } $this->markUnAnswered(); if (!$alert) { return $response; } $dept = $this->getDept(); if (!($tpl = $dept->getTemplate())) { $tpl = $cfg->getDefaultTemplate(); } if (!$dept || !($email = $dept->getEmail())) { $email = $cfg->getDefaultEmail(); } if ($tpl && ($msg = $tpl->getAutoReplyMsgTemplate()) && $email) { if ($dept && $dept->isPublic()) { $signature = $dept->getSignature(); } else { $signature = ''; } $msg = $this->replaceVars($msg->asArray(), array('response' => $response, 'signature' => $signature)); if ($cfg->stripQuotedReply() && ($tag = $cfg->getReplySeparator())) { $msg['body'] = "\n{$tag}\n\n" . $msg['body']; } $attachments = $cfg->emailAttachments() && $files ? $response->getAttachments() : array(); $options = array('inreplyto' => $response->getEmailMessageId(), 'references' => $response->getEmailReferences()); $email->sendAutoReply($this->getEmail(), $msg['subj'], $msg['body'], $attachments, $options); } return $response; }