public static function _doBulkSetCustomFields($source_extension, $custom_fields, $ids) { $fields = DAO_CustomField::getAll(); if (!empty($custom_fields)) { foreach ($custom_fields as $cf_id => $params) { if (!is_array($params) || !isset($params['value'])) { continue; } $cf_val = $params['value']; // Data massaging switch ($fields[$cf_id]->type) { case Model_CustomField::TYPE_DATE: $cf_val = intval(@strtotime($cf_val)); break; case Model_CustomField::TYPE_CHECKBOX: case Model_CustomField::TYPE_NUMBER: $cf_val = 0 == strlen($cf_val) ? '' : intval($cf_val); break; } // If multi-selection types, handle delta changes if (Model_CustomField::TYPE_MULTI_PICKLIST == $fields[$cf_id]->type || Model_CustomField::TYPE_MULTI_CHECKBOX == $fields[$cf_id]->type) { if (is_array($cf_val)) { foreach ($cf_val as $val) { $op = substr($val, 0, 1); $val = substr($val, 1); if (is_array($ids)) { foreach ($ids as $id) { if ($op == '+') { DAO_CustomFieldValue::setFieldValue($source_extension, $id, $cf_id, $val, true); } elseif ($op == '-') { DAO_CustomFieldValue::unsetFieldValue($source_extension, $id, $cf_id, $val); } } } } } // Otherwise, set/unset as a single field } else { if (is_array($ids)) { foreach ($ids as $id) { if (0 != strlen($cf_val)) { DAO_CustomFieldValue::setFieldValue($source_extension, $id, $cf_id, $cf_val); } else { DAO_CustomFieldValue::unsetFieldValue($source_extension, $id, $cf_id); } } } } } } }
function doTaskBulkUpdateAction() { // Checked rows @($ids_str = DevblocksPlatform::importGPC($_REQUEST['ids'], 'string')); $ids = DevblocksPlatform::parseCsvString($ids_str); // Filter: whole list or check @($filter = DevblocksPlatform::importGPC($_REQUEST['filter'], 'string', '')); // View @($view_id = DevblocksPlatform::importGPC($_REQUEST['view_id'], 'string')); $view = C4_AbstractViewLoader::getView('', $view_id); // Task fields $due = trim(DevblocksPlatform::importGPC($_POST['due'], 'string', '')); $status = trim(DevblocksPlatform::importGPC($_POST['status'], 'string', '')); $worker_id = trim(DevblocksPlatform::importGPC($_POST['worker_id'], 'string', '')); $do = array(); // Do: Due if (0 != strlen($due)) { $do['due'] = $due; } // Do: Status if (0 != strlen($status)) { $do['status'] = $status; } // Do: Worker if (0 != strlen($worker_id)) { $do['worker_id'] = $worker_id; } // Do: Custom fields $do = DAO_CustomFieldValue::handleBulkPost($do); $view->doBulkUpdate($filter, $do, $ids); $view->render(); return; }
function showRecipientPeekAction() { @($id = DevblocksPlatform::importGPC($_REQUEST['id'], 'integer', 0)); @($customer_id = DevblocksPlatform::importGPC($_REQUEST['customer_id'], 'integer', 0)); @($view_id = DevblocksPlatform::importGPC($_REQUEST['view_id'], 'string', '')); $display_view = 0; $tpl = DevblocksPlatform::getTemplateService(); $tpl->assign('path', $this->_TPL_PATH); $tpl->assign('id', $id); $tpl->assign('customer_id', $customer_id); $tpl->assign('view_id', $view_id); $customer_recipient = DAO_CustomerRecipient::get($id); $tpl->assign('customer_recipient', $customer_recipient); // Custom Fields $custom_fields = DAO_CustomField::getBySource(FegCustomFieldSource_CustomerRecipient::ID); $tpl->assign('custom_fields', $custom_fields); $custom_field_values = DAO_CustomFieldValue::getValuesBySourceIds(FegCustomFieldSource_CustomerRecipient::ID, $id); if (isset($custom_field_values[$id])) { $tpl->assign('custom_field_values', $custom_field_values[$id]); } // Below is the Audit log view only avaible is the audit log plugin is enabled. if (class_exists('View_MessageAuditLog', true)) { $display_view = 1; $defaults = new Feg_AbstractViewModel(); $defaults->class_name = 'View_MessageAuditLog'; $defaults->id = '_recipient_audit_log'; $defaults->renderLimit = 10; $defaults->renderSortBy = SearchFields_MessageAuditLog::CHANGE_DATE; $defaults->renderSortAsc = false; $defaults->params = array(); $view = Feg_AbstractViewLoader::getView($defaults->id, $defaults); $view->name = 'Recipient Audit Log'; $view->renderTemplate = 'peek_tab'; $view->params = array(SearchFields_MessageAuditLog::RECIPIENT_ID => new DevblocksSearchCriteria(SearchFields_MessageAuditLog::RECIPIENT_ID, DevblocksSearchCriteria::OPER_EQ, $id)); $view->renderPage = 0; $view->renderLimit = 10; $view->renderSortBy = SearchFields_MessageAuditLog::CHANGE_DATE; $view->renderSortAsc = false; $view->view_columns = array(SearchFields_MessageAuditLog::CHANGE_DATE, SearchFields_MessageAuditLog::MESSAGE_ID, SearchFields_MessageAuditLog::WORKER_ID, SearchFields_MessageAuditLog::CHANGE_FIELD, SearchFields_MessageAuditLog::CHANGE_VALUE); Feg_AbstractViewLoader::setView($view->id, $view); $tpl->assign('view', $view); } $tpl->assign('display_view', $display_view); $tpl->display('file:' . $this->_TPL_PATH . 'customer/tabs/recipient/peek.tpl'); }
function doOrgBulkUpdateAction() { // Checked rows @($org_ids_str = DevblocksPlatform::importGPC($_REQUEST['org_ids'], 'string')); $org_ids = DevblocksPlatform::parseCsvString($org_ids_str); // Filter: whole list or check @($filter = DevblocksPlatform::importGPC($_REQUEST['filter'], 'string', '')); // View @($view_id = DevblocksPlatform::importGPC($_REQUEST['view_id'], 'string')); $view = C4_AbstractViewLoader::getView($view_id); // Org fields @($country = trim(DevblocksPlatform::importGPC($_POST['country'], 'string', ''))); $do = array(); // Do: Country if (0 != strlen($country)) { $do['country'] = $country; } // Do: Custom fields $do = DAO_CustomFieldValue::handleBulkPost($do); $view->doBulkUpdate($filter, $do, $org_ids); $view->render(); return; }
public static function handleFormPost($source_ext_id, $source_id, $field_ids) { $fields = DAO_CustomField::getBySource($source_ext_id); if (is_array($field_ids)) { foreach ($field_ids as $field_id) { if (!isset($fields[$field_id])) { continue; } switch ($fields[$field_id]->type) { case Model_CustomField::TYPE_MULTI_LINE: case Model_CustomField::TYPE_SINGLE_LINE: case Model_CustomField::TYPE_URL: @($field_value = DevblocksPlatform::importGPC($_POST['field_' . $field_id], 'string', '')); if (0 != strlen($field_value)) { DAO_CustomFieldValue::setFieldValue($source_ext_id, $source_id, $field_id, $field_value); } else { DAO_CustomFieldValue::unsetFieldValue($source_ext_id, $source_id, $field_id); } break; case Model_CustomField::TYPE_DROPDOWN: @($field_value = DevblocksPlatform::importGPC($_POST['field_' . $field_id], 'string', '')); if (0 != strlen($field_value)) { DAO_CustomFieldValue::setFieldValue($source_ext_id, $source_id, $field_id, $field_value); } else { DAO_CustomFieldValue::unsetFieldValue($source_ext_id, $source_id, $field_id); } break; case Model_CustomField::TYPE_MULTI_PICKLIST: @($field_value = DevblocksPlatform::importGPC($_POST['field_' . $field_id], 'array', array())); if (!empty($field_value)) { DAO_CustomFieldValue::setFieldValue($source_ext_id, $source_id, $field_id, $field_value); } else { DAO_CustomFieldValue::unsetFieldValue($source_ext_id, $source_id, $field_id); } break; case Model_CustomField::TYPE_CHECKBOX: @($field_value = DevblocksPlatform::importGPC($_POST['field_' . $field_id], 'integer', 0)); $set = !empty($field_value) ? 1 : 0; DAO_CustomFieldValue::setFieldValue($source_ext_id, $source_id, $field_id, $set); break; case Model_CustomField::TYPE_MULTI_CHECKBOX: @($field_value = DevblocksPlatform::importGPC($_POST['field_' . $field_id], 'array', array())); if (!empty($field_value)) { DAO_CustomFieldValue::setFieldValue($source_ext_id, $source_id, $field_id, $field_value); } else { DAO_CustomFieldValue::unsetFieldValue($source_ext_id, $source_id, $field_id); } break; case Model_CustomField::TYPE_DATE: @($field_value = DevblocksPlatform::importGPC($_POST['field_' . $field_id], 'string', '')); @($date = strtotime($field_value)); if (!empty($date)) { DAO_CustomFieldValue::setFieldValue($source_ext_id, $source_id, $field_id, $date); } else { DAO_CustomFieldValue::unsetFieldValue($source_ext_id, $source_id, $field_id); } break; case Model_CustomField::TYPE_NUMBER: case Model_CustomField::TYPE_WORKER: @($field_value = DevblocksPlatform::importGPC($_POST['field_' . $field_id], 'string', '')); if (0 != strlen($field_value)) { DAO_CustomFieldValue::setFieldValue($source_ext_id, $source_id, $field_id, intval($field_value)); } else { DAO_CustomFieldValue::unsetFieldValue($source_ext_id, $source_id, $field_id); } break; } } } return true; }
/** * @return Model_WatcherMailFilter[]|false */ static function getMatches(CerberusTicket $ticket, $event, $only_worker_id = null) { $matches = array(); if (!empty($only_worker_id)) { $filters = DAO_WatcherMailFilter::getWhere(sprintf("%s = %d AND %s = %d", DAO_WatcherMailFilter::WORKER_ID, $only_worker_id, DAO_WatcherMailFilter::IS_DISABLED, 0)); } else { $filters = DAO_WatcherMailFilter::getWhere(sprintf("%s = %d", DAO_WatcherMailFilter::IS_DISABLED, 0)); } // [JAS]: Don't send obvious spam to watchers. if ($ticket->spam_score >= 0.9) { return false; } // Build our objects $ticket_from = DAO_Address::get($ticket->last_wrote_address_id); $ticket_group_id = $ticket->team_id; // [TODO] These expensive checks should only populate when needed $messages = DAO_Ticket::getMessagesByTicket($ticket->id); $message_headers = array(); if (empty($messages)) { return false; } if (null != @($message_last = array_pop($messages))) { /* @var $message_last CerberusMessage */ $message_headers = $message_last->getHeaders(); } // Clear the rest of the message manifests unset($messages); $custom_fields = DAO_CustomField::getAll(); // Lazy load when needed on criteria basis $ticket_field_values = null; $address_field_values = null; $org_field_values = null; // Worker memberships (for checking permissions) $workers = DAO_Worker::getAll(); $group_rosters = DAO_Group::getRosters(); // Check filters if (is_array($filters)) { foreach ($filters as $filter) { /* @var $filter Model_WatcherMailFilter */ $passed = 0; // check the worker's group memberships if (!isset($workers[$filter->worker_id]) || $workers[$filter->worker_id]->is_disabled || !$workers[$filter->worker_id]->is_superuser && !isset($group_rosters[$ticket->team_id][$filter->worker_id])) { // no membership continue; } // check criteria foreach ($filter->criteria as $rule_key => $rule) { @($value = $rule['value']); switch ($rule_key) { case 'dayofweek': $current_day = strftime('%w'); //$current_day = 1; // Forced to English abbrevs as indexes $days = array('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'); // Is the current day enabled? if (isset($rule[$days[$current_day]])) { $passed++; } break; case 'timeofday': $current_hour = strftime('%H'); $current_min = strftime('%M'); //$current_hour = 17; //$current_min = 5; if (null != ($from_time = @$rule['from'])) { list($from_hour, $from_min) = explode(':', $from_time); } if (null != ($to_time = @$rule['to'])) { if (list($to_hour, $to_min) = explode(':', $to_time)) { } } // Do we need to wrap around to the next day's hours? if ($from_hour > $to_hour) { // yes $to_hour += 24; // add 24 hrs to the destination (1am = 25th hour) } // Are we in the right 24 hourly range? if ((int) $current_hour >= $from_hour && (int) $current_hour <= $to_hour) { // If we're in the first hour, are we minutes early? if ($current_hour == $from_hour && (int) $current_min < $from_min) { break; } // If we're in the last hour, are we minutes late? if ($current_hour == $to_hour && (int) $current_min > $to_min) { break; } $passed++; } break; case 'event': if (!empty($event) && is_array($rule) && isset($rule[$event])) { $passed++; } break; case 'groups': if (null !== @($group_buckets = $rule['groups'][$ticket->team_id]) && (empty($group_buckets) || in_array($ticket->category_id, $group_buckets))) { $passed++; } break; case 'next_worker_id': // If it's an assigned event, we only care about the filter's owner if (!empty($event) && 0 == strcasecmp($event, 'ticket_assignment')) { if (intval($value) == intval($filter->worker_id)) { $passed++; break; } } if (intval($value) == intval($ticket->next_worker_id)) { $passed++; } break; case 'mask': $regexp_mask = DevblocksPlatform::strToRegExp($value); if (@preg_match($regexp_mask, $ticket->mask)) { $passed++; } break; case 'from': $regexp_from = DevblocksPlatform::strToRegExp($value); if (@preg_match($regexp_from, $ticket_from->email)) { $passed++; } break; case 'subject': $regexp_subject = DevblocksPlatform::strToRegExp($value); if (@preg_match($regexp_subject, $ticket->subject)) { $passed++; } break; case 'body': if (null == ($message_body = $message_last->getContent())) { break; } // Line-by-line body scanning (sed-like) $lines = preg_split("/[\r\n]/", $message_body); if (is_array($lines)) { foreach ($lines as $line) { if (@preg_match($value, $line)) { $passed++; break; } } } break; case 'header1': case 'header2': case 'header3': case 'header4': case 'header5': @($header = strtolower($rule['header'])); if (empty($header)) { $passed++; break; } if (empty($value)) { // we're checking for null/blanks if (!isset($message_headers[$header]) || empty($message_headers[$header])) { $passed++; } } elseif (isset($message_headers[$header]) && !empty($message_headers[$header])) { $regexp_header = DevblocksPlatform::strToRegExp($value); // Flatten CRLF if (@preg_match($regexp_header, str_replace(array("\r", "\n"), ' ', $message_headers[$header]))) { $passed++; } } break; default: // ignore invalids // Custom Fields if (0 == strcasecmp('cf_', substr($rule_key, 0, 3))) { $field_id = substr($rule_key, 3); // Make sure it exists if (null == @($field = $custom_fields[$field_id])) { continue; } // Lazy values loader $field_values = array(); switch ($field->source_extension) { case ChCustomFieldSource_Address::ID: if (null == $address_field_values) { $address_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Address::ID, $ticket_from->id)); } $field_values =& $address_field_values; break; case ChCustomFieldSource_Org::ID: if (null == $org_field_values) { $org_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Org::ID, $ticket_from->contact_org_id)); } $field_values =& $org_field_values; break; case ChCustomFieldSource_Ticket::ID: if (null == $ticket_field_values) { $ticket_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Ticket::ID, $ticket->id)); } $field_values =& $ticket_field_values; break; } // Type sensitive value comparisons // [TODO] Operators switch ($field->type) { case 'S': // string // string case 'T': // clob // clob case 'U': // URL $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : ''; $oper = isset($rule['oper']) ? $rule['oper'] : "="; if ($oper == "=" && @preg_match(DevblocksPlatform::strToRegExp($value, true), $field_val)) { $passed++; } elseif ($oper == "!=" && @(!preg_match(DevblocksPlatform::strToRegExp($value, true), $field_val))) { $passed++; } break; case 'N': // number $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : 0; $oper = isset($rule['oper']) ? $rule['oper'] : "="; if ($oper == "=" && intval($field_val) == intval($value)) { $passed++; } elseif ($oper == "!=" && intval($field_val) != intval($value)) { $passed++; } elseif ($oper == ">" && intval($field_val) > intval($value)) { $passed++; } elseif ($oper == "<" && intval($field_val) < intval($value)) { $passed++; } break; case 'E': // date $field_val = isset($field_values[$field_id]) ? intval($field_values[$field_id]) : 0; $from = isset($rule['from']) ? $rule['from'] : "0"; $to = isset($rule['to']) ? $rule['to'] : "now"; if (intval(@strtotime($from)) <= $field_val && intval(@strtotime($to)) >= $field_val) { $passed++; } break; case 'C': // checkbox $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : 0; if (intval($value) == intval($field_val)) { $passed++; } break; case 'D': // dropdown // dropdown case 'X': // multi-checkbox // multi-checkbox case 'M': // multi-picklist // multi-picklist case 'W': // worker $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : array(); if (!is_array($value)) { $value = array($value); } if (is_array($field_val)) { // if multiple things set foreach ($field_val as $v) { // loop through possible if (isset($value[$v])) { // is any possible set? $passed++; break; } } } else { // single if (isset($value[$field_val])) { // is our set field in possibles? $passed++; break; } } break; } } break; } } // If our rule matched every criteria, stop and return the filter if ($passed == count($filter->criteria)) { DAO_WatcherMailFilter::increment($filter->id); // ++ the times we've matched $matches[$filter->id] = $filter; } } } if (!empty($matches)) { return $matches; } // No matches return false; }
function doBatchUpdateAction() { @($ticket_id_str = DevblocksPlatform::importGPC($_REQUEST['ticket_ids'], 'string')); @($shortcut_name = DevblocksPlatform::importGPC($_REQUEST['shortcut_name'], 'string', '')); @($filter = DevblocksPlatform::importGPC($_REQUEST['filter'], 'string', '')); @($senders = DevblocksPlatform::importGPC($_REQUEST['senders'], 'string', '')); @($subjects = DevblocksPlatform::importGPC($_REQUEST['subjects'], 'string', '')); @($view_id = DevblocksPlatform::importGPC($_REQUEST['view_id'], 'string')); $view = C4_AbstractViewLoader::getView($view_id); $subjects = DevblocksPlatform::parseCrlfString($subjects); $senders = DevblocksPlatform::parseCrlfString($senders); $do = array(); // [TODO] This logic is repeated in several places -- try to condense (like custom field form handlers) // Move to Group/Bucket @($move_code = DevblocksPlatform::importGPC($_REQUEST['do_move'], 'string', null)); if (0 != strlen($move_code)) { list($g_id, $b_id) = CerberusApplication::translateTeamCategoryCode($move_code); $do['move'] = array('group_id' => intval($g_id), 'bucket_id' => intval($b_id)); } // Assign to worker @($worker_id = DevblocksPlatform::importGPC($_REQUEST['do_assign'], 'string', null)); if (0 != strlen($worker_id)) { $do['assign'] = array('worker_id' => intval($worker_id)); } // Spam training @($is_spam = DevblocksPlatform::importGPC($_REQUEST['do_spam'], 'string', null)); if (0 != strlen($is_spam)) { $do['spam'] = array('is_spam' => !$is_spam ? 0 : 1); } // Set status @($status = DevblocksPlatform::importGPC($_REQUEST['do_status'], 'string', null)); if (0 != strlen($status)) { $do['status'] = array('is_waiting' => 3 == $status ? 1 : 0, 'is_closed' => 0 == $status || 3 == $status ? 0 : 1, 'is_deleted' => 2 == $status ? 1 : 0); } $data = array(); $ticket_ids = array(); if ($filter == 'sender') { $data = $senders; } elseif ($filter == 'subject') { $data = $subjects; } elseif ($filter == 'checks') { $filter = ''; // bulk update just looks for $ticket_ids == !null $ticket_ids = DevblocksPlatform::parseCsvString($ticket_id_str); } // Restrict to current worker groups $active_worker = CerberusApplication::getActiveWorker(); $memberships = $active_worker->getMemberships(); $view->params['tmp'] = new DevblocksSearchCriteria(SearchFields_Ticket::TICKET_TEAM_ID, 'in', array_keys($memberships)); // Do: Custom fields $do = DAO_CustomFieldValue::handleBulkPost($do); $view->doBulkUpdate($filter, '', $data, $do, $ticket_ids); // Clear our temporary group restriction before re-rendering unset($view->params['tmp']); $view->render(); return; }
static function delete($ids) { if (!is_array($ids)) { $ids = array($ids); } $db = DevblocksPlatform::getDatabaseService(); $ids_list = implode(',', $ids); // Opps $db->Execute(sprintf("DELETE QUICK FROM crm_opportunity WHERE id IN (%s)", $ids_list)); // Custom fields DAO_CustomFieldValue::deleteBySourceIds(CrmCustomFieldSource_Opportunity::ID, $ids); // Notes DAO_Note::deleteBySourceIds(CrmNotesSource_Opportunity::ID, $ids); return true; }
function saveTasksPropertiesTabAction() { @($id = DevblocksPlatform::importGPC($_REQUEST['id'], 'integer', '')); @($do_delete = DevblocksPlatform::importGPC($_REQUEST['do_delete'], 'integer', 0)); $active_worker = CerberusApplication::getActiveWorker(); if (!empty($id) && !empty($do_delete)) { // delete $task = DAO_Task::get($id); // Check privs if ($active_worker->hasPriv('core.tasks.actions.create') && $active_worker->id == $task->worker_id || $active_worker->hasPriv('core.tasks.actions.update_nobody') && empty($task->worker_id) || $active_worker->hasPriv('core.tasks.actions.update_all')) { DAO_Task::delete($id); DevblocksPlatform::redirect(new DevblocksHttpResponse(array('activity', 'tasks'))); exit; } } else { // update $fields = array(); // Title @($title = DevblocksPlatform::importGPC($_REQUEST['title'], 'string', '')); $fields[DAO_Task::TITLE] = !empty($title) ? $title : 'New Task'; // Completed @($completed = DevblocksPlatform::importGPC($_REQUEST['completed'], 'integer', 0)); $fields[DAO_Task::IS_COMPLETED] = intval($completed); // [TODO] This shouldn't constantly update the completed date (it should compare) if ($completed) { $fields[DAO_Task::COMPLETED_DATE] = time(); } else { $fields[DAO_Task::COMPLETED_DATE] = 0; } // Updated Date $fields[DAO_Task::UPDATED_DATE] = time(); // Due Date @($due_date = DevblocksPlatform::importGPC($_REQUEST['due_date'], 'string', '')); @($fields[DAO_Task::DUE_DATE] = empty($due_date) ? 0 : intval(strtotime($due_date))); // Worker @($worker_id = DevblocksPlatform::importGPC($_REQUEST['worker_id'], 'integer', 0)); @($fields[DAO_Task::WORKER_ID] = intval($worker_id)); // Save if (!empty($id)) { DAO_Task::update($id, $fields); // Custom field saves @($field_ids = DevblocksPlatform::importGPC($_POST['field_ids'], 'array', array())); DAO_CustomFieldValue::handleFormPost(ChCustomFieldSource_Task::ID, $id, $field_ids); } } DevblocksPlatform::redirect(new DevblocksHttpResponse(array('tasks', 'display', $id, 'properties'))); }
private function _getValueCounts($field_id) { $db = DevblocksPlatform::getDatabaseService(); // Selected custom field if (null == ($field = DAO_CustomField::get($field_id))) { return; } if (null == ($table = DAO_CustomFieldValue::getValueTableName($field_id))) { return; } $sql = sprintf("SELECT field_value, count(field_value) AS hits " . "FROM %s " . "WHERE source_extension = %s " . "AND field_id = %d " . "GROUP BY field_value", $table, $db->qstr($field->source_extension), $field->id); $rs_values = $db->Execute($sql); $value_counts = array(); while (!$rs_values->EOF) { $value = $rs_values->fields['field_value']; $hits = intval($rs_values->fields['hits']); $value_counts[$value] = intval($hits); $rs_values->MoveNext(); } arsort($value_counts); return $value_counts; }
function run($message_id, $template) { if (!empty($message_id)) { $message = DAO_Ticket::getMessage($message_id); $fields = DAO_CustomField::getBySource(ChCustomFieldSource_Ticket::ID); $custom_fields = DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Ticket::ID, $message->ticket_id); foreach ($fields as $field_id => $field) { if ($field->group_id == 0) { $template = str_replace(array('#cf_ticket_' . $field->name . '#'), array($custom_fields[$message->ticket_id][$field->id]), $template); } } } return; }
function getTimeSpentPlus1ReportAction() { $db = DevblocksPlatform::getDatabaseService(); DevblocksPlatform::getExtensions('timetracking.source', true); // Security if (null == ($active_worker = CerberusApplication::getActiveWorker())) { die($translate->_('common.access_denied')); } $tpl = DevblocksPlatform::getTemplateService(); $tpl->cache_lifetime = "0"; $tpl->assign('path', $this->tpl_path); // import dates from form @($start = DevblocksPlatform::importGPC($_REQUEST['start'], 'string', '')); @($end = DevblocksPlatform::importGPC($_REQUEST['end'], 'string', '')); $start_time = 0; $end_time = 0; if (empty($start) && empty($end)) { $start = "-30 days"; $end = "now"; $start_time = strtotime($start); $end_time = strtotime($end); } else { $start_time = strtotime($start); $end_time = strtotime($end); } if ($start_time === false || $end_time === false) { $start = "-30 days"; $end = "now"; $start_time = strtotime($start); $end_time = strtotime($end); $tpl->assign('invalidDate', true); } $groups = DAO_Group::getAll(); $buckets = DAO_Bucket::getAll(); // reload variables in template $tpl->assign('start', $start); $tpl->assign('end', $end); $sources = DAO_TimeTrackingEntry::getSources(); $tpl->assign('sources', $sources); $sql = "SELECT t.mask, t.id, sum(tte.time_actual_mins) mins, a.email, "; $sql .= "t.subject, t.created_date, t.updated_date, t.is_closed, "; $sql .= "t.is_waiting, t.team_id, t.category_id "; $sql .= "FROM timetracking_entry tte "; $sql .= "INNER JOIN ticket t ON tte.source_id = t.id "; $sql .= "INNER JOIN address a ON t.first_wrote_address_id = a.id "; $sql .= sprintf("WHERE log_date > %d AND log_date <= %d ", $start_time, $end_time); $sql .= "GROUP BY t.id "; $sql .= "ORDER BY t.id, tte.log_date "; // echo $sql; $rs = $db->Execute($sql); $time_entries = array(); $filename = "report-plus1-" . $active_worker->id . ".csv"; $full_filename = getcwd() . '/storage/answernet/' . $filename; if (file_exists($full_filename)) { if (!is_writable($full_filename)) { die("The file: {$full_filename} is not writable"); } } elseif (!is_writable(getcwd() . '/storage/answernet/')) { die("you cannot create files in this directory. Check the permissions"); } //open the file for Writing $fh = fopen($full_filename, "w"); //Lock the file for the write operation flock($fh, LOCK_EX); $label = array("Ticket Mask", "Ticket Number", "Client Name", "Asset Name", "Site Name", "Requestor", "Subject", "Created Date", "Last Updated", "Group", "Bucket", "Status", "Total Min"); fputcsv($fh, $label, ",", "\""); if (is_a($rs, 'ADORecordSet')) { while (!$rs->EOF) { $csv = array(); $custom_fields = array(); $mask = $rs->fields['mask']; $id = intval($rs->fields['id']); $email = $rs->fields['email']; $subject = $rs->fields['subject']; $team_id = intval($rs->fields['team_id']); $category_id = intval($rs->fields['category_id']); $created_date = intval($rs->fields['created_date']); $updated_date = intval($rs->fields['updated_date']); $status = "Open"; if (intval($rs->fields['is_waiting'])) { $status = "Waiting for Reply"; } if (intval($rs->fields['is_closed'])) { $status = "Completed"; } $mins = intval($rs->fields['mins']); if (!isset($time_entries[$id])) { $time_entries[$id] = array(); } $csv['mask'] = $mask; $csv['id'] = $id; unset($time_entry); $time_entry['mask'] = $mask; $custom_fields = DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Ticket::ID, $id); if (isset($custom_fields[$id][10])) { $csv['client'] = $custom_fields[$id][10]; $time_entry['client'] = $custom_fields[$id][10]; } else { $csv['client'] = ""; $time_entry['client'] = ""; } if (isset($custom_fields[$id][11])) { $csv['asset'] = $custom_fields[$id][11]; $time_entry['asset'] = $custom_fields[$id][11]; } else { $csv['asset'] = ""; $time_entry['asset'] = ""; } if (isset($custom_fields[$id][1])) { $csv['sitename'] = $custom_fields[$id][1]; $time_entry['sitename'] = $custom_fields[$id][1]; } else { $csv['sitename'] = ""; $time_entry['sitename'] = ""; } $csv['email'] = $email; $time_entry['email'] = $email; $csv['subject'] = $subject; $time_entry['subject'] = $subject; $csv['created_date'] = date("Y-m-d h:i A", $created_date); $time_entry['created_date'] = $created_date; $csv['updated_date'] = date("Y-m-d h:i A", $updated_date); $time_entry['updated_date'] = $updated_date; $csv['group'] = $groups[$team_id]->name; $time_entry['group'] = $groups[$team_id]->name; if ($category_id) { $csv['bucket'] = $buckets[$category_id]->name; $time_entry['bucket'] = $buckets[$category_id]->name; } else { $csv['bucket'] = 'Inbox'; $time_entry['bucket'] = 'Inbox'; } $csv['status'] = $status; $time_entry['status'] = $status; $csv['mins'] = $mins; $time_entry['mins'] = $mins; $time_entries[$id] = $time_entry; fputcsv($fh, $csv, ",", "\""); $rs->MoveNext(); } } fclose($fh); $tpl->assign('time_entries', $time_entries); $tpl->display('file:' . $this->tpl_path . '/report_plus1_time_html.tpl'); }
function getTimeSpentWorkerReportAction() { $db = DevblocksPlatform::getDatabaseService(); $subtotal = array(); $total_cm = array(); DevblocksPlatform::getExtensions('timetracking.source', true); @($sel_worker_id = DevblocksPlatform::importGPC($_REQUEST['worker_id'], 'integer', 0)); @($report_type = DevblocksPlatform::importGPC($_REQUEST['report_type'], 'integer', 0)); // Security if (null == ($active_worker = CerberusApplication::getActiveWorker())) { die($translate->_('common.access_denied')); } $tpl = DevblocksPlatform::getTemplateService(); $tpl->cache_lifetime = "0"; $tpl->assign('path', $this->tpl_path); // import dates from form @($start = DevblocksPlatform::importGPC($_REQUEST['start'], 'string', '')); @($end = DevblocksPlatform::importGPC($_REQUEST['end'], 'string', '')); // use date rang@$sel_worker_id = DevblocksPlatform::importGPC($_REQUEST['worker_id'],'integer',0);e if specified, else use duration prior to now $start_time = 0; $end_time = 0; if (empty($start) && empty($end)) { $start = "Last Monday"; $end = "now"; $start_time = strtotime($start); $end_time = strtotime($end); } else { $start_time = strtotime($start); $end_time = strtotime($end); } if ($start_time === false || $end_time === false) { $start = "Last Monday"; $end = "now"; $start_time = strtotime($start); $end_time = strtotime($end); $tpl->assign('invalidDate', true); } // reload variables in template $tpl->assign('start', $start); $tpl->assign('end', $end); $workers = DAO_Worker::getAll(); $tpl->assign('workers', $workers); $sources = DAO_TimeTrackingEntry::getSources(); $tpl->assign('sources', $sources); $sql = "SELECT tte.log_date, tte.time_actual_mins, tte.worker_id, tte.notes, "; $sql .= "tte.source_extension_id, tte.source_id, "; $sql .= "tta.name activity_name "; $sql .= "FROM timetracking_entry tte "; $sql .= "INNER JOIN timetracking_activity tta ON tte.activity_id = tta.id "; $sql .= "INNER JOIN worker w ON tte.worker_id = w.id "; $sql .= sprintf("WHERE log_date > %d AND log_date <= %d ", $start_time, $end_time); if ($sel_worker_id) { $sql .= sprintf("AND tte.worker_id = %d ", $sel_worker_id); } // Do Not use Group By it breaks things. // $sql .= "GROUP BY activity_name "; $sql .= "ORDER BY w.last_name, w.first_name, activity_name, w.id, tte.log_date "; // echo $sql; $rs = $db->Execute($sql); $time_entries = array(); $filename = "worker-" . $active_worker->id . ".csv"; $full_filename = getcwd() . '/storage/answernet/' . $filename; if (file_exists($full_filename)) { if (!is_writable($full_filename)) { die("The file: {$full_filename} is not writable"); } } elseif (!is_writable(getcwd() . '/storage/answernet/')) { die("you cannot create files in this directory. Check the permissions"); } //open the file for Writing $fh = fopen($full_filename, "w"); //Lock the file for the write operation flock($fh, LOCK_EX); $label = array("Worker Name", "Ticket No", "Client", "Asset", "Site Name", "Billing Group", "Billing Min", "Sub-Total", "Total", "Date Recorded", "Notes"); fputcsv($fh, $label, ",", "\""); if (is_a($rs, 'ADORecordSet')) { while (!$rs->EOF) { $csv = array(); $custom_fields = array(); $mins = intval($rs->fields['time_actual_mins']); $worker_id = intval($rs->fields['worker_id']); $org_id = intval($rs->fields['org_id']); $activity = $rs->fields['activity_name']; $log_date = intval($rs->fields['log_date']); $notes = $rs->fields['notes']; if (!isset($time_entries[$worker_id])) { $time_entries[$worker_id] = array(); $time_entries[$worker_id]['mins'] = array(); } if (!isset($subtotal)) { $subtotal = array(); $subtotal_activity = $activity; $subtotal['name'] = $workers[$worker_id]->getName(false); $subtotal['source_id'] = ""; $subtotal['client'] = ""; $subtotal['asset'] = ""; $subtotal['sitename'] = ""; $subtotal['activity_name'] = $activity; $subtotal['mins'] = ""; } else { if ($subtotal_activity != $activity) { //Dump Sub Total fputcsv($fh, $subtotal, ",", "\""); $subtotal = array(); $subtotal_activity = $activity; $subtotal['name'] = $workers[$worker_id]->getName(false); $subtotal['source_id'] = ""; $subtotal['client'] = ""; $subtotal['asset'] = ""; $subtotal['sitename'] = ""; $subtotal['activity_name'] = $activity; $subtotal['mins'] = ""; } } if (!isset($total_cm)) { $total_cm = array(); $total_worker_id = $worker_id; $total_cm['name'] = $workers[$worker_id]->getName(false); $total_cm['source_id'] = ""; $total_cm['client'] = ""; $total_cm['asset'] = ""; $total_cm['sitename'] = ""; $total_cm['activity_name'] = ""; $total_cm['mins'] = ""; } else { if ($total_worker_id != $worker_id) { //Dump Total fputcsv($fh, $total_cm, ",", "\""); $total_cm = array(); $total_worker_id = $worker_id; $total_cm['name'] = $workers[$worker_id]->getName(false); $total_cm['source_id'] = ""; $total_cm['client'] = ""; $total_cm['asset'] = ""; $total_cm['sitename'] = ""; $total_cm['activity_name'] = ""; $total_cm['mins'] = ""; } } $csv['name'] = $workers[$worker_id]->getName(false); unset($time_entry); $time_entry['activity_name'] = $activity; $time_entry['mins'] = $mins; $time_entry['log_date'] = $log_date; $time_entry['notes'] = $notes; $time_entry['source_extension_id'] = $rs->fields['source_extension_id']; $time_entry['source_id'] = intval($rs->fields['source_id']); $csv['source_id'] = intval($rs->fields['source_id']); $custom_fields = DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Ticket::ID, $csv['source_id']); if (isset($custom_fields[$csv['source_id']][10])) { $csv['client'] = $custom_fields[$csv['source_id']][10]; } else { $csv['client'] = ""; } if (isset($custom_fields[$csv['source_id']][11])) { $csv['asset'] = $custom_fields[$csv['source_id']][11]; } else { $csv['asset'] = ""; } if (isset($custom_fields[$csv['source_id']][1])) { $csv['sitename'] = $custom_fields[$csv['source_id']][1]; } else { $csv['sitename'] = ""; } $csv['activity_name'] = $activity; $csv['mins'] = $mins; $csv['subtotal'] = ""; $csv['total'] = ""; $csv['log_date'] = date("Y-m-d h:i A", $log_date); $csv['notes'] = $notes; $time_entries[$worker_id]['entries'][] = $time_entry; @($time_entries[$worker_id]['mins'][$activity] = intval($time_entries[$worker_id]['mins'][$activity]) + $mins); @($time_entries[$worker_id]['mins']['total'] = intval($time_entries[$worker_id]['mins']['total']) + $mins); $subtotal['subtotal'] = $time_entries[$worker_id]['mins'][$activity]; $total_cm['subtotal'] = ""; $subtotal['total'] = ""; $total_cm['total'] = $time_entries[$worker_id]['mins']['total']; fputcsv($fh, $csv, ",", "\""); $rs->MoveNext(); } } // print_r($time_entries); $tpl->assign('time_entries', $time_entries); $tpl->assign('report_type', $report_type); $tpl->assign('href_filename', $href_filename); fputcsv($fh, $subtotal, ",", "\""); fputcsv($fh, $total_cm, ",", "\""); fclose($fh); $tpl->display('file:' . $this->tpl_path . '/report_worker_time_html.tpl'); }
function doBulkUpdateAction() { // Checked rows @($ids_str = DevblocksPlatform::importGPC($_REQUEST['ids'], 'string')); $ids = DevblocksPlatform::parseCsvString($ids_str); // Filter: whole list or check @($filter = DevblocksPlatform::importGPC($_REQUEST['filter'], 'string', '')); // View @($view_id = DevblocksPlatform::importGPC($_REQUEST['view_id'], 'string')); $view = C4_AbstractViewLoader::getView($view_id); // Feedback fields // @$list_id = trim(DevblocksPlatform::importGPC($_POST['list_id'],'integer',0)); $do = array(); // Do: List // if(0 != strlen($list_id)) // $do['list_id'] = $list_id; // Do: Custom fields $do = DAO_CustomFieldValue::handleBulkPost($do); $view->doBulkUpdate($filter, $do, $ids); $view->render(); return; }
function saveSensorPeekAction() { $translate = DevblocksPlatform::getTranslationService(); $active_worker = PortSensorApplication::getActiveWorker(); // [TODO] ACL // return; @($id = DevblocksPlatform::importGPC($_POST['id'], 'integer')); @($view_id = DevblocksPlatform::importGPC($_POST['view_id'], 'string')); @($name = DevblocksPlatform::importGPC($_POST['name'], 'string')); @($extension_id = DevblocksPlatform::importGPC($_POST['extension_id'], 'string')); @($disabled = DevblocksPlatform::importGPC($_POST['is_disabled'], 'integer', 0)); @($delete = DevblocksPlatform::importGPC($_POST['do_delete'], 'integer', 0)); // [TODO] The superuser set bit here needs to be protected by ACL if (empty($name)) { $name = "New Sensor"; } if (!empty($id) && !empty($delete)) { DAO_Sensor::delete($id); } else { $fields = array(DAO_Sensor::NAME => $name, DAO_Sensor::EXTENSION_ID => $extension_id, DAO_Sensor::IS_DISABLED => $disabled); if (empty($id)) { $id = DAO_Sensor::create($fields); } else { DAO_Sensor::update($id, $fields); } // Save sensor extension config if (!empty($extension_id)) { if (null != ($ext = DevblocksPlatform::getExtension($extension_id, true))) { if (null != ($sensor = DAO_Sensor::get($id)) && $ext instanceof Extension_Sensor) { $ext->saveConfig($sensor); } } } // Custom field saves @($field_ids = DevblocksPlatform::importGPC($_POST['field_ids'], 'array', array())); DAO_CustomFieldValue::handleFormPost(PsCustomFieldSource_Sensor::ID, $id, $field_ids); } if (!empty($view_id)) { $view = Ps_AbstractViewLoader::getView($view_id); $view->render(); } }
function doContactSendAction() { @($sFrom = DevblocksPlatform::importGPC($_POST['from'], 'string', '')); @($sSubject = DevblocksPlatform::importGPC($_POST['subject'], 'string', '')); @($sContent = DevblocksPlatform::importGPC($_POST['content'], 'string', '')); @($sCaptcha = DevblocksPlatform::importGPC($_POST['captcha'], 'string', '')); @($aFieldIds = DevblocksPlatform::importGPC($_POST['field_ids'], 'array', array())); @($aFollowUpQ = DevblocksPlatform::importGPC($_POST['followup_q'], 'array', array())); // Load the answers to any situational questions $aFollowUpA = array(); if (is_array($aFollowUpQ)) { foreach ($aFollowUpQ as $idx => $q) { @($answer = DevblocksPlatform::importGPC($_POST['followup_a_' . $idx], 'string', '')); $aFollowUpA[$idx] = $answer; } } $umsession = $this->getSession(); $fingerprint = parent::getFingerprint(); $settings = CerberusSettings::getInstance(); $default_from = $settings->get(CerberusSettings::DEFAULT_REPLY_FROM); $umsession->setProperty('support.write.last_from', $sFrom); $umsession->setProperty('support.write.last_subject', $sSubject); $umsession->setProperty('support.write.last_content', $sContent); // $umsession->setProperty('support.write.last_followup_q',$aFollowUpQ); $umsession->setProperty('support.write.last_followup_a', $aFollowUpA); $sNature = $umsession->getProperty('support.write.last_nature', ''); $captcha_enabled = DAO_CommunityToolProperty::get($this->getPortal(), UmScApp::PARAM_CAPTCHA_ENABLED, 1); if (empty($sFrom) || $captcha_enabled && 0 != strcasecmp($sCaptcha, @$umsession->getProperty(UmScApp::SESSION_CAPTCHA, '***'))) { if (empty($sFrom)) { $umsession->setProperty('support.write.last_error', 'Invalid e-mail address.'); } else { $umsession->setProperty('support.write.last_error', 'What you typed did not match the image.'); } // [TODO] Need to report the captcha didn't match and redraw the form DevblocksPlatform::setHttpResponse(new DevblocksHttpResponse(array('portal', $this->getPortal(), 'contact', 'step2'))); return; } // Dispatch $to = $default_from; $subject = 'Contact me: Other'; $sDispatch = DAO_CommunityToolProperty::get($this->getPortal(), UmScApp::PARAM_DISPATCH, ''); $dispatch = !empty($sDispatch) ? unserialize($sDispatch) : array(); foreach ($dispatch as $k => $v) { if (md5($k) == $sNature) { $to = $v['to']; $subject = 'Contact me: ' . strip_tags($k); break; } } if (!empty($sSubject)) { $subject = $sSubject; } $fieldContent = ''; if (!empty($aFollowUpQ)) { $fieldContent = "\r\n\r\n"; $fieldContent .= "--------------------------------------------\r\n"; if (!empty($sNature)) { $fieldContent .= $subject . "\r\n"; $fieldContent .= "--------------------------------------------\r\n"; } foreach ($aFollowUpQ as $idx => $q) { $answer = isset($aFollowUpA[$idx]) ? $aFollowUpA[$idx] : ''; $fieldContent .= "Q) " . $q . "\r\n" . "A) " . $answer . "\r\n"; if ($idx + 1 < count($aFollowUpQ)) { $fieldContent .= "\r\n"; } } $fieldContent .= "--------------------------------------------\r\n"; "\r\n"; } $message = new CerberusParserMessage(); $message->headers['date'] = date('r'); $message->headers['to'] = $to; $message->headers['subject'] = $subject; $message->headers['message-id'] = CerberusApplication::generateMessageId(); $message->headers['x-cerberus-portal'] = 1; // Sender $fromList = imap_rfc822_parse_adrlist($sFrom, ''); if (empty($fromList) || !is_array($fromList)) { return; // abort with message } $from = array_shift($fromList); $message->headers['from'] = $from->mailbox . '@' . $from->host; $message->body = 'IP: ' . $fingerprint['ip'] . "\r\n\r\n" . $sContent . $fieldContent; $ticket_id = CerberusParser::parseMessage($message); $ticket = DAO_Ticket::getTicket($ticket_id); // Auto-save any custom fields $fields = DAO_CustomField::getBySource('cerberusweb.fields.source.ticket'); if (!empty($aFieldIds)) { foreach ($aFieldIds as $iIdx => $iFieldId) { if (!empty($iFieldId)) { $field =& $fields[$iFieldId]; /* @var $field Model_CustomField */ $value = ""; switch ($field->type) { case Model_CustomField::TYPE_SINGLE_LINE: case Model_CustomField::TYPE_MULTI_LINE: @($value = trim($aFollowUpA[$iIdx])); break; case Model_CustomField::TYPE_NUMBER: @($value = intval($aFollowUpA[$iIdx])); break; case Model_CustomField::TYPE_DATE: if (false !== ($time = strtotime($aFollowUpA[$iIdx]))) { @($value = intval($time)); } break; case Model_CustomField::TYPE_DROPDOWN: @($value = $aFollowUpA[$iIdx]); break; case Model_CustomField::TYPE_CHECKBOX: @($value = isset($aFollowUpA[$iIdx]) && !empty($aFollowUpA[$iIdx]) ? 1 : 0); break; } if (!empty($value)) { DAO_CustomFieldValue::setFieldValue('cerberusweb.fields.source.ticket', $ticket_id, $iFieldId, $value); } } } } // Clear any errors $umsession->setProperty('support.write.last_nature', null); $umsession->setProperty('support.write.last_nature_string', null); $umsession->setProperty('support.write.last_content', null); $umsession->setProperty('support.write.last_error', null); $umsession->setProperty('support.write.last_opened', $ticket->mask); DevblocksPlatform::setHttpResponse(new DevblocksHttpResponse(array('portal', $this->getPortal(), 'contact', 'confirm'))); }
/** * Enter description here... * * @param array $ids */ static function delete($ids) { if (!is_array($ids)) { $ids = array($ids); } $db = DevblocksPlatform::getDatabaseService(); $ids_list = implode(',', $ids); // Tasks $db->Execute(sprintf("DELETE QUICK FROM task WHERE id IN (%s)", $ids_list)); // Custom fields DAO_CustomFieldValue::deleteBySourceIds(ChCustomFieldSource_Task::ID, $ids); return true; }
function savePropertiesAction() { @($ticket_id = DevblocksPlatform::importGPC($_POST['ticket_id'], 'integer', 0)); @($remove = DevblocksPlatform::importGPC($_POST['remove'], 'array', array())); @($next_worker_id = DevblocksPlatform::importGPC($_POST['next_worker_id'], 'integer', 0)); @($ticket_reopen = DevblocksPlatform::importGPC($_POST['ticket_reopen'], 'string', '')); @($unlock_date = DevblocksPlatform::importGPC($_POST['unlock_date'], 'string', '')); @($subject = DevblocksPlatform::importGPC($_POST['subject'], 'string', '')); @($closed = DevblocksPlatform::importGPC($_POST['closed'], 'closed', 0)); @($ticket = DAO_Ticket::getTicket($ticket_id)); if (empty($ticket_id) || empty($ticket)) { return; } $fields = array(); // Properties if (empty($next_worker_id)) { $unlock_date = ""; } // Status if (isset($closed)) { switch ($closed) { case 0: // open $fields[DAO_Ticket::IS_WAITING] = 0; $fields[DAO_Ticket::IS_CLOSED] = 0; $fields[DAO_Ticket::IS_DELETED] = 0; $fields[DAO_Ticket::DUE_DATE] = 0; break; case 1: // closed $fields[DAO_Ticket::IS_WAITING] = 0; $fields[DAO_Ticket::IS_CLOSED] = 1; $fields[DAO_Ticket::IS_DELETED] = 0; if (isset($ticket_reopen)) { @($time = intval(strtotime($ticket_reopen))); $fields[DAO_Ticket::DUE_DATE] = $time; } break; case 2: // waiting $fields[DAO_Ticket::IS_WAITING] = 1; $fields[DAO_Ticket::IS_CLOSED] = 0; $fields[DAO_Ticket::IS_DELETED] = 0; if (isset($ticket_reopen)) { @($time = intval(strtotime($ticket_reopen))); $fields[DAO_Ticket::DUE_DATE] = $time; } break; case 3: // deleted $fields[DAO_Ticket::IS_WAITING] = 0; $fields[DAO_Ticket::IS_CLOSED] = 1; $fields[DAO_Ticket::IS_DELETED] = 1; $fields[DAO_Ticket::DUE_DATE] = 0; break; } } if (isset($next_worker_id)) { $fields[DAO_Ticket::NEXT_WORKER_ID] = $next_worker_id; } if (isset($unlock_date)) { @($time = intval(strtotime($unlock_date))); $fields[DAO_Ticket::UNLOCK_DATE] = $time; } if (!empty($subject)) { $fields[DAO_Ticket::SUBJECT] = $subject; } if (!empty($fields)) { DAO_Ticket::updateTicket($ticket_id, $fields); } // Custom field saves @($field_ids = DevblocksPlatform::importGPC($_POST['field_ids'], 'array', array())); DAO_CustomFieldValue::handleFormPost(ChCustomFieldSource_Ticket::ID, $ticket_id, $field_ids); // Requesters @($req_list = DevblocksPlatform::importGPC($_POST['add'], 'string', '')); if (!empty($req_list)) { $req_list = DevblocksPlatform::parseCrlfString($req_list); $req_list = array_unique($req_list); // [TODO] This is redundant with the Requester Peek on Reply if (is_array($req_list) && !empty($req_list)) { foreach ($req_list as $req) { if (empty($req)) { continue; } $rfc_addys = imap_rfc822_parse_adrlist($req, 'localhost'); foreach ($rfc_addys as $rfc_addy) { $addy = $rfc_addy->mailbox . '@' . $rfc_addy->host; if (null != ($req_addy = CerberusApplication::hashLookupAddress($addy, true))) { DAO_Ticket::createRequester($req_addy->id, $ticket_id); } } } } } if (!empty($remove) && is_array($remove)) { foreach ($remove as $address_id) { $addy = DAO_Address::get($address_id); DAO_Ticket::deleteRequester($ticket_id, $address_id); // echo "Removed <b>" . $addy->email . "</b> as a recipient.<br>"; } } DevblocksPlatform::redirect(new DevblocksHttpResponse(array('display', $ticket->mask))); }
function AnswernetMetlifeReportDRReport($RunFromCron = 0) { $db = DevblocksPlatform::getDatabaseService(); $translate = DevblocksPlatform::getTranslationService(); if ($RunFromCron) { $logger = DevblocksPlatform::getConsoleLog(); } $radius = 12; $start_time = 0; date_default_timezone_set('Etc/UTC'); // Security if (null == ($active_worker = CerberusApplication::getActiveWorker())) { die($translate->_('common.access_denied')); } // import dates from form @($start = DevblocksPlatform::importGPC($_REQUEST['start'], 'string', '')); if (empty($start)) { $start = "Yesterday"; } $start_time = strtotime($start); if ($start_time === false) { $start = "Yesterday"; $start_time = strtotime($start); } if (empty($start_time) || !is_numeric($start_time)) { return; } $start_ofday = mktime(0, 0, 0, date("m", $start_time), date("d", $start_time), date("Y", $start_time)); $end_ofday = $start_ofday + 86400; if ($RunFromCron) { $logger->info("[Answernet.com] Gerating the Metlife DR Report."); $logger->info("[Answernet.com] " . $translate->_('answernet.er.metlife.report.dr.date.from.text') . date(" Y-m-d H:i:s ", $start_ofday) . $translate->_('answernet.er.metlife.report.dr.date.to.text') . date(" Y-m-d H:i:s T", $end_ofday)); $logger->info("[Answernet.com] " . $translate->_('answernet.er.metlife.generate.report')); } else { print '<br><br><br>'; print $translate->_('answernet.er.metlife.report.dr.date.from.text'); print date(" Y-m-d H:i:s ", $start_ofday); print $translate->_('answernet.er.metlife.report.dr.date.to.text'); print date(" Y-m-d H:i:s T", $end_ofday); print '<br>'; print $translate->_('answernet.er.metlife.generate.report'); } $filename = "report-metlife-dr-" . date("Ymd", $start_time) . ".xls"; $full_filename = getcwd() . '/storage/answernet/' . $filename; if ($RunFromCron) { $logger->info("[Answernet.com] " . $translate->_('answernet.er.metlife.generating')); } else { print '<br>'; print $translate->_('answernet.er.metlife.generating'); } // Create new Excel Spreadsheet. $workbook = new Spreadsheet_Excel_Writer($full_filename); // Create Open Status Tab and set Column Width and Row Hight. $worksheet_transaction =& $workbook->addWorksheet('Transaction Report'); $worksheet_transaction->setColumn(0, 0, $radius * 0.85); $worksheet_transaction->setColumn(1, 1, $radius * 0.85); $worksheet_transaction->setColumn(2, 2, $radius * 0.35); $worksheet_transaction->setColumn(3, 3, $radius * 1.65); $worksheet_transaction->setColumn(4, 4, $radius * 1.85); $worksheet_transaction->setColumn(5, 5, $radius * 1.16); $worksheet_transaction->setColumn(6, 6, $radius * 2.6); $worksheet_transaction->setColumn(7, 8, $radius * 0.87); $worksheet_transaction->setColumn(9, 9, $radius * 3.28); $worksheet_transaction->setColumn(10, 10, $radius * 1.34); // $worksheet_open_status->setRow(0, 28); // $worksheet_open_status->setRow(2, 32); // Create Open Status Tab and set Column Width and Row Hight. $worksheet_open_status =& $workbook->addWorksheet('Open DR Report'); $worksheet_open_status->setColumn(0, 0, $radius * 0.85); $worksheet_open_status->setColumn(1, 1, $radius * 0.35); $worksheet_open_status->setColumn(2, 2, $radius * 0.7); $worksheet_open_status->setColumn(3, 3, $radius * 1.65); $worksheet_open_status->setColumn(4, 4, $radius * 1.85); $worksheet_open_status->setColumn(5, 5, $radius * 1.16); $worksheet_open_status->setColumn(6, 6, $radius * 2.6); $worksheet_open_status->setColumn(7, 8, $radius * 0.87); $worksheet_open_status->setColumn(9, 9, $radius * 3.28); $worksheet_open_status->setColumn(10, 10, $radius * 1.34); // $worksheet_open_status->setRow(0, 28); // $worksheet_open_status->setRow(2, 32); // Create monthly Tab and set Column Width and Row Hight. $worksheet_monthly =& $workbook->addWorksheet('Month DR Summary'); $worksheet_monthly->setColumn(0, 0, $radius * 2.46); $worksheet_monthly->setColumn(1, 1, $radius * 0.47); $worksheet_monthly->setColumn(2, 2, $radius * 0.63); $worksheet_monthly->setColumn(3, 3, $radius * 0.6899999999999999); $worksheet_monthly->setColumn(4, 4, $radius * 0.6899999999999999); $worksheet_monthly->setColumn(5, 5, $radius * 0.6899999999999999); // $worksheet_monthly->setRow(0, 56); // Formats used thoughout the workbook. $format_general =& $workbook->addFormat(); $format_general->setBorder(1); $format_general->setHAlign('left'); $format_general->setTextWrap(); $format_general_title =& $workbook->addFormat(); $format_general_title->setBorder(1); $format_general_title->setColor('black'); $format_general_title->setFgColor('silver'); $format_general_title->setHAlign('left'); $format_general_title->setVAlign('vjustify'); $format_general_title->setVAlign('top'); $format_general_nowrap =& $workbook->addFormat(); $format_general_nowrap->setBorder(1); // Setup templating for the formating of certain cells in the Monthly Group. $format_monthly_title =& $workbook->addFormat(); $format_monthly_title->setColor(8); $format_monthly_title->setFgColor(35); $format_monthly_title->setBorder(1); $format_monthly_title->setBold(); $format_monthly_title->setHAlign('center'); $format_monthly_title->setVAlign('vjustify'); $format_monthly_title->setVAlign('top'); $format_monthly_title->setTextWrap(); $format_monthly_title->setAlign('merge'); // Added headers since they never change in the transaction Group. $worksheet_transaction->write(0, 0, 'Status', $format_general_title); $worksheet_transaction->write(0, 1, 'Due Date', $format_general_title); $worksheet_transaction->write(0, 2, 'SLA', $format_general_title); $worksheet_transaction->write(0, 3, 'Date Received', $format_general_title); $worksheet_transaction->write(0, 4, 'RM Name', $format_general_title); $worksheet_transaction->write(0, 5, 'RM Employee id', $format_general_title); $worksheet_transaction->write(0, 6, 'Request Type', $format_general_title); $worksheet_transaction->write(0, 7, 'MetLife Staff', $format_general_title); $worksheet_transaction->write(0, 8, 'New Hire', $format_general_title); $worksheet_transaction->write(0, 9, 'Nates (email body)', $format_general_title); $worksheet_transaction->write(0, 10, 'Ticket Mask', $format_general_title); // Added headers since they never change in the Open Status Group. $worksheet_open_status->write(0, 0, 'Due Date', $format_general_title); $worksheet_open_status->write(0, 1, 'SLA', $format_general_title); $worksheet_open_status->write(0, 2, 'Overdue', $format_general_title); $worksheet_open_status->write(0, 3, 'Date Received', $format_general_title); $worksheet_open_status->write(0, 4, 'RM Name', $format_general_title); $worksheet_open_status->write(0, 5, 'RM Employee id', $format_general_title); $worksheet_open_status->write(0, 6, 'Request Type', $format_general_title); $worksheet_open_status->write(0, 7, 'MetLife Staff', $format_general_title); $worksheet_open_status->write(0, 8, 'New Hire', $format_general_title); $worksheet_open_status->write(0, 9, 'Nates (email body)', $format_general_title); $worksheet_open_status->write(0, 10, 'Ticket Mask', $format_general_title); // Added headers since they never change in the monthly Group. $month_text = date("F-y", $start_time); $worksheet_monthly->write(0, 0, $month_text, $format_monthly_title); $worksheet_monthly->write(0, 1, '', $format_monthly_title); $worksheet_monthly->write(0, 2, '', $format_monthly_title); $worksheet_monthly->write(0, 3, '', $format_monthly_title); $worksheet_monthly->write(0, 4, '', $format_monthly_title); $worksheet_monthly->write(0, 5, '', $format_monthly_title); $worksheet_monthly->write(1, 0, 'DR Summary', $format_general_title); $worksheet_monthly->write(1, 1, '', $format_general_title); $worksheet_monthly->write(2, 0, 'All DRs incoming*', $format_general); $worksheet_monthly->write(3, 0, 'DRs Sent to MetLife', $format_general); $worksheet_monthly->write(4, 0, 'DRs Completed', $format_general); $worksheet_monthly->write(5, 0, 'Average time to reply (day)', $format_general); $worksheet_monthly->write(6, 0, 'Missed DR SLA', $format_general); $worksheet_monthly->write(7, 0, 'DR escalations', $format_general); $worksheet_monthly->write(8, 0, 'DR Unique Users', $format_general); $worksheet_monthly->write(9, 0, 'New Hires (30 Days)', $format_general); $worksheet_monthly->write(10, 0, '', $format_general); $worksheet_monthly->write(10, 1, '', $format_general); $worksheet_monthly->write(11, 0, 'DR Categories', $format_general_title); $worksheet_monthly->write(11, 1, '#s', $format_general_title); $worksheet_monthly->write(11, 2, 'Avg SLA', $format_general_title); $worksheet_monthly->write(11, 3, 'Linda #s', $format_general_title); $worksheet_monthly->write(11, 4, 'Colin #s', $format_general_title); $worksheet_monthly->write(11, 5, 'Sarfraz #s', $format_general_title); $worksheet_monthly->write(12, 0, 'Import Contacts - New hire', $format_general); $worksheet_monthly->write(13, 0, 'Import Contacts', $format_general); $worksheet_monthly->write(14, 0, 'Create mailing list from exiting date', $format_general); $worksheet_monthly->write(15, 0, 'Update existing contacts - batch', $format_general); $worksheet_monthly->write(16, 0, 'Missing or incorrect customer info', $format_general); $worksheet_monthly->write(17, 0, 'Fix duplicate contacts', $format_general); $worksheet_monthly->write(18, 0, 'Export third party file', $format_general); $worksheet_monthly->write(19, 0, 'Other', $format_general); $worksheet_monthly->write(20, 0, 'Total', $format_general_title); $worksheet_monthly->write(20, 1, '', $format_general_title); $worksheet_monthly->write(20, 2, '', $format_general_title); $worksheet_monthly->write(20, 3, '', $format_general_title); $worksheet_monthly->write(20, 4, '', $format_general_title); $worksheet_monthly->write(20, 5, '', $format_general_title); $worksheet_monthly->write(22, 0, '* Some DRs will be deemed normal care and should use other reporting codes, remove from DR reporting', $format_general_nowrap); $worksheet_monthly->write(23, 0, '** Days should be tracked as business days', $format_general_nowrap); $worksheet_monthly->write(25, 0, 'SLA Goals', $format_general_title); $worksheet_monthly->write(25, 1, '', $format_general_title); $worksheet_monthly->write(26, 0, 'Import Contacts - New hire', $format_general); $worksheet_monthly->write(27, 0, 'Import Contacts', $format_general); $worksheet_monthly->write(28, 0, 'Create mailing list from exiting date', $format_general); $worksheet_monthly->write(29, 0, 'Update existing contacts - batch', $format_general); $worksheet_monthly->write(30, 0, 'Missing or incorrect customer info', $format_general); $worksheet_monthly->write(31, 0, 'Fix duplicate contacts', $format_general); $worksheet_monthly->write(32, 0, 'Export third party file', $format_general); $worksheet_monthly->write(33, 0, 'Other', $format_general); $worksheet_monthly->write(34, 0, 'Avgerage', $format_general_title); $worksheet_monthly->write(34, 1, '', $format_general_title); if ($RunFromCron) { $logger->info("[Answernet.com] " . $translate->_('answernet.er.metlife.metlife.done')); $logger->info("[Answernet.com] " . $translate->_('answernet.er.metlife.generating.dr.daily.report')); } else { print $translate->_('answernet.er.metlife.metlife.done'); print '<br>'; print $translate->_('answernet.er.metlife.generating.dr.daily.report'); } //SELECT t.id, t.mask, t.created_date ticket_created_date, mc.content, t.is_closed //FROM ticket t //INNER JOIN message_content mc on t.first_message_id = mc.message_id //INNER JOIN message m on t.first_message_id = m.id //and team_id = 1721 //ORDER BY t.id $sql = "SELECT t.id, t.mask, "; $sql .= "t.created_date ticket_created_date, mc.content "; $sql .= "FROM ticket t "; $sql .= "INNER JOIN message_content mc on t.first_message_id = mc.message_id "; $sql .= "WHERE t.is_closed = 0 "; $sql .= "and t.team_id = 1721 "; $sql .= "ORDER BY t.id "; $rs = $db->Execute($sql); $row = 1; if (is_a($rs, 'ADORecordSet')) { while (!$rs->EOF) { $worksheet_open_status->setRow($row, 12); $custom_fields = DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Ticket::ID, $rs->fields['id']); //print "<pre>"; //print_r($custom_fields); //print "</pre>"; // Due Date, SLA, Date Recived, RM Name, RM Employee ID, Topic, Staff, New Hire, Notes/Email Body // Due Date Column 0 $due_date_int = intval($custom_fields[$rs->fields['id']][1]); if ($due_date_int) { $ticket_due_date = date("n/j/y", $due_date_int); } else { $ticket_due_date = ""; } $worksheet_open_status->writeString($row, 0, $ticket_due_date, $format_general); // SLA Column 1 $worksheet_open_status->write($row, 1, $custom_fields[$rs->fields['id']][5], $format_general); // Overdue Column 2 if (date("U", $due_date_int) >= time()) { $worksheet_open_status->write($row, 2, "No", $format_general); } else { $worksheet_open_status->write($row, 2, "Yes", $format_general); } // Date Recieved Column 3 $ticket_created_date = date("n/j/y g:i:s A", intval($rs->fields['ticket_created_date'])); $worksheet_open_status->writeString($row, 3, $ticket_created_date, $format_general); // RM Name Column 4 $worksheet_open_status->write($row, 4, $custom_fields[$rs->fields['id']][3], $format_general); // RM Employee ID Column 5 $worksheet_open_status->write($row, 5, $custom_fields[$rs->fields['id']][2], $format_general); // Topic / Request Type Column 6 $worksheet_open_status->write($row, 6, $custom_fields[$rs->fields['id']][4], $format_general); // Staff Column 7 $worksheet_open_status->write($row, 7, $custom_fields[$rs->fields['id']][8], $format_general); // New Hire Column 8 $worksheet_open_status->write($row, 8, $custom_fields[$rs->fields['id']][6], $format_general); // Email Body Column 9 $message_content = $rs->fields['content']; $worksheet_open_status->write($row, 9, trim($message_content), $format_general_nowrap); // Ticket Mask Column 10 $mask = $rs->fields['mask']; $worksheet_open_status->write($row, 10, $mask, $format_general); $row++; $rs->MoveNext(); } } if ($RunFromCron) { $logger->info("[Answernet.com] " . $translate->_('answernet.er.metlife.metlife.done')); $logger->info("[Answernet.com] " . $translate->_('answernet.er.metlife.generating.dr.transaction.report')); } else { print $translate->_('answernet.er.metlife.metlife.done'); print '<br>'; print $translate->_('answernet.er.metlife.generating.dr.transaction.report'); } //SELECT t.mask, t.created_date ticket_created_date, //m.created_date message_created_date, mc.content, //m.is_outgoing //FROM message m //INNER JOIN ticket t ON m.ticket_id = t.id //INNER JOIN address a ON m.address_id = a.id //INNER JOIN message_content mc on m.id = mc.message_id //WHERE t.team_id = 1721 //ORDER BY m.id; $sql = "SELECT t.id, t.mask, t.created_date ticket_created_date, "; $sql .= "m.created_date message_created_date, mc.content, m.is_outgoing "; $sql .= "FROM message m "; $sql .= "INNER JOIN ticket t ON m.ticket_id = t.id "; $sql .= "INNER JOIN address a ON m.address_id = a.id "; $sql .= "INNER JOIN message_content mc on m.id = mc.message_id "; $sql .= sprintf("WHERE m.created_date > %d AND m.created_date <= %d ", $start_ofday, $end_ofday); $sql .= "and t.team_id = 1721 "; $sql .= "ORDER BY t.id "; $rs = $db->Execute($sql); $row = 1; if (is_a($rs, 'ADORecordSet')) { while (!$rs->EOF) { $worksheet_transaction->setRow($row, 12); $custom_fields = DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Ticket::ID, $rs->fields['id']); // Status, Due Date, SLA, SLA Age, Date Recived, RM Name, RM Employee ID, Topic, Staff, New Hire, Notes/Email Body if (intval($rs->fields['is_outgoing'])) { $worksheet_transaction->write($row, 0, "Recieved", $format_general); } else { $worksheet_transaction->write($row, 0, "Sent", $format_general); } // Due Date Column 1 $due_date_int = intval($custom_fields[$rs->fields['id']][1]); if ($due_date_int) { $ticket_due_date = date("n/j/y", $due_date_int); } else { $ticket_due_date = ""; } $worksheet_transaction->writeString($row, 1, $ticket_due_date, $format_general); // SLA Column 2 $worksheet_transaction->write($row, 2, $custom_fields[$rs->fields['id']][5], $format_general); // Date Recieved Column 3 $ticket_created_date = date("n/j/y g:i:s A", intval($rs->fields['ticket_created_date'])); $worksheet_transaction->writeString($row, 3, $ticket_created_date, $format_general); // RM Name Column 4 $worksheet_transaction->write($row, 4, $custom_fields[$rs->fields['id']][3], $format_general); // RM Employee ID Column 5 $worksheet_transaction->write($row, 5, $custom_fields[$rs->fields['id']][2], $format_general); // Topic / Request Type Column 6 $worksheet_transaction->write($row, 6, $custom_fields[$rs->fields['id']][4], $format_general); // Staff Column 7 $worksheet_transaction->write($row, 7, $custom_fields[$rs->fields['id']][8], $format_general); // New Hire Column 8 $worksheet_transaction->write($row, 8, $custom_fields[$rs->fields['id']][6], $format_general); // Email Body Column 9 $message_content = $rs->fields['content']; $worksheet_transaction->write($row, 9, trim($message_content), $format_general_nowrap); // Ticket Mask Column 10 $mask = $rs->fields['mask']; $worksheet_transaction->write($row, 10, $mask, $format_general); $row++; $rs->MoveNext(); } } if ($RunFromCron) { $logger->info("[Answernet.com] " . $translate->_('answernet.er.metlife.metlife.done')); $logger->info("[Answernet.com] " . $translate->_('answernet.er.metlife.generating.dr.monthly.report')); } else { print $translate->_('answernet.er.metlife.metlife.done'); print '<br>'; print $translate->_('answernet.er.metlife.generating.dr.monthly.report'); } /* print $translate->_('answernet.er.metlife.metlife.done'); print '<br>'; print $translate->_('answernet.er.metlife.generating.email.count'); $worksheet_in_count->setRow(1, 24); $row_count = 2; foreach ($in_count_admin as $record) { $worksheet_in_count->write($row_count, 0, $record['email'], $format_general); $worksheet_in_count->write($row_count, 1, $record['count'], $format_general); $row_count++; } $worksheet_in_count->write(1, 0, 'Total Admin Email', $format_in_count_total); $worksheet_in_count->writeFormula(1, 1, "=SUM(B3:B".$row_count.")", $format_general); $row_count = 2; foreach ($in_count_other as $record) { $worksheet_in_count->write($row_count, 2, $record['email'], $format_general); $worksheet_in_count->write($row_count, 3, '', $format_general); $worksheet_in_count->write($row_count, 4, $record['count'], $format_general); $row_count++; } $worksheet_in_count->write(1, 2, 'Total Field Email', $format_in_count_total); $worksheet_in_count->write(1, 3, '', $format_in_count_total); $worksheet_in_count->writeFormula(1, 4, "=SUM(E3:E".$row_count.")", $format_general); // Grand Total $worksheet_in_count->write(1, 5, 'Grand Total Email', $format_general); $worksheet_in_count->writeFormula(1, 6, "=SUM(B2,E2)", $format_general); $worksheet_out_count->setRow(1, 24); $row_count = 2; foreach ($out_count_admin as $record) { $worksheet_out_count->write($row_count, 0, $record['email'], $format_general); $worksheet_out_count->write($row_count, 1, $record['count'], $format_general); $row_count++; } $worksheet_out_count->write(1, 0, 'Total Admin Email', $format_general); $worksheet_out_count->writeFormula(1, 1, "=SUM(B3:B".$row_count.")", $format_general); $row_count = 2; foreach ($out_count_other as $record) { $worksheet_out_count->write($row_count, 2, $record['email'], $format_general); $worksheet_out_count->write($row_count, 3, '', $format_general); $worksheet_out_count->write($row_count, 4, $record['count'], $format_general); $row_count++; } $worksheet_out_count->write(1, 2, 'Total Field Email', $format_general); $worksheet_out_count->write(1, 3, '', $format_out_count_total); $worksheet_out_count->writeFormula(1, 4, "=SUM(E3:E".$row_count.")", $format_general); // Grand Total $worksheet_out_count->write(1, 5, 'Grand Total Email', $format_general); $worksheet_out_count->writeFormula(1, 6, "=SUM(B2,E2)", $format_general); */ $workbook->close(); return $filename; }
/** * Enter description here... * * @param CerberusParserMessage $message * @return integer */ public static function parseMessage(CerberusParserMessage $message, $options = array()) { /* * options: * 'no_autoreply' */ $logger = DevblocksPlatform::getConsoleLog(); $settings = DevblocksPlatform::getPluginSettingsService(); $helpdesk_senders = CerberusApplication::getHelpdeskSenders(); // Pre-parse mail filters $pre_filters = Model_PreParseRule::getMatches($message); if (is_array($pre_filters) && !empty($pre_filters)) { // Load filter action manifests for reuse $ext_action_mfts = DevblocksPlatform::getExtensions('cerberusweb.mail_filter.action', false); // Loop through all matching filters foreach ($pre_filters as $pre_filter) { // Do something with matching filter's actions foreach ($pre_filter->actions as $action_key => $action) { switch ($action_key) { case 'blackhole': return NULL; break; case 'redirect': @($to = $action['to']); CerberusMail::reflect($message, $to); return NULL; break; case 'bounce': @($msg = $action['message']); @($subject = 'Delivery failed: ' . self::fixQuotePrintableString($message->headers['subject'])); // [TODO] Follow the RFC spec on a true bounce if (null != ($fromAddressInst = CerberusParser::getAddressFromHeaders($message->headers))) { CerberusMail::quickSend($fromAddressInst->email, $subject, $msg); } return NULL; break; default: // Plugin pre-parser filter actions if (isset($ext_action_mfts[$action_key])) { if (null != @($ext_action = $ext_action_mfts[$action_key]->createInstance())) { try { /* @var $ext_action Extension_MailFilterAction */ $ext_action->run($pre_filter, $message); } catch (Exception $e) { } } } break; } } } } $headers =& $message->headers; // From if (null == ($fromAddressInst = CerberusParser::getAddressFromHeaders($headers))) { $logger->err("[Parser] 'From' address could not be created."); return NULL; } // To/Cc/Bcc $to = array(); $sTo = @$headers['to']; $bIsNew = true; if (!empty($sTo)) { // [TODO] Do we still need this RFC address parser? $to = CerberusParser::parseRfcAddress($sTo); } // Subject // Fix quote printable subject (quoted blocks can appear anywhere in subject) $sSubject = ""; if (isset($headers['subject']) && !empty($headers['subject'])) { $sSubject = $headers['subject']; if (is_array($sSubject)) { $sSubject = array_shift($sSubject); } } // The subject can still end up empty after QP decode if (empty($sSubject)) { $sSubject = "(no subject)"; } // Date $iDate = @strtotime($headers['date']); // If blank, or in the future, set to the current date if (empty($iDate) || $iDate > time()) { $iDate = time(); } // Is banned? if (1 == $fromAddressInst->is_banned) { $logger->info("[Parser] Ignoring ticket from banned address: " . $fromAddressInst->email); return NULL; } // Overloadable $enumSpamTraining = ''; // Message Id / References / In-Reply-To @($sMessageId = $headers['message-id']); $body_append_text = array(); $body_append_html = array(); // [mdf]Check attached files before creating the ticket because we may need to overwrite the message-id // also store any contents of rfc822 files so we can include them after the body foreach ($message->files as $filename => $file) { /* @var $file ParserFile */ switch ($file->mime_type) { case 'message/rfc822': $full_filename = $file->tmpname; $mail = mailparse_msg_parse_file($full_filename); $struct = mailparse_msg_get_structure($mail); $msginfo = mailparse_msg_get_part_data($mail); $inline_headers = $msginfo['headers']; if (isset($headers['from']) && (strtolower(substr($headers['from'], 0, 11)) == 'postmaster@' || strtolower(substr($headers['from'], 0, 14)) == 'mailer-daemon@')) { $headers['in-reply-to'] = $inline_headers['message-id']; } break; } } // [JAS] [TODO] References header may contain multiple message-ids to find if (null != ($ids = self::findParentMessage($headers))) { $bIsNew = false; $id = $ids['ticket_id']; $msgid = $ids['message_id']; // Is it a worker reply from an external client? If so, proxy if (null != ($worker_address = DAO_AddressToWorker::getByAddress($fromAddressInst->email))) { $logger->info("[Parser] Handling an external worker response from " . $fromAddressInst->email); if (!DAO_Ticket::isTicketRequester($worker_address->address, $id)) { // Watcher Commands [TODO] Document on wiki/etc if (0 != ($matches = preg_match_all("/\\[(.*?)\\]/i", $message->headers['subject'], $commands))) { @($command = strtolower(array_pop($commands[1]))); $logger->info("[Parser] Worker command: " . $command); switch ($command) { case 'close': DAO_Ticket::updateTicket($id, array(DAO_Ticket::IS_CLOSED => CerberusTicketStatus::CLOSED)); break; case 'take': DAO_Ticket::updateTicket($id, array(DAO_Ticket::NEXT_WORKER_ID => $worker_address->worker_id)); break; case 'comment': $comment_id = DAO_TicketComment::create(array(DAO_TicketComment::ADDRESS_ID => $fromAddressInst->id, DAO_TicketComment::CREATED => time(), DAO_TicketComment::TICKET_ID => $id, DAO_TicketComment::COMMENT => $message->body)); return $id; break; default: // Typo? break; } } $attachment_files = array(); $attachment_files['name'] = array(); $attachment_files['type'] = array(); $attachment_files['tmp_name'] = array(); $attachment_files['size'] = array(); $i = 0; foreach ($message->files as $filename => $file) { $attachment_files['name'][$i] = $filename; $attachment_files['type'][$i] = $file->mime_type; $attachment_files['tmp_name'][$i] = $file->tmpname; $attachment_files['size'][$i] = $file->file_size; $i++; } CerberusMail::sendTicketMessage(array('message_id' => $msgid, 'content' => $message->body, 'files' => $attachment_files, 'agent_id' => $worker_address->worker_id)); return $id; } else { // ... worker is a requester, treat as normal $logger->info("[Parser] The external worker was a ticket requester, so we're not treating them as a watcher."); } } else { // Reply: Not sent by a worker /* * [TODO] check that this sender is a requester on the matched ticket * Otherwise blank out the $id */ } } $group_id = 0; if (empty($id)) { // New Ticket $sMask = CerberusApplication::generateTicketMask(); $groups = DAO_Group::getAll(); // Routing new tickets if (null != ($routing_rules = Model_MailToGroupRule::getMatches($fromAddressInst, $message))) { if (is_array($routing_rules)) { foreach ($routing_rules as $rule) { // Only end up with the last 'move' action (ignore the previous) if (isset($rule->actions['move'])) { $group_id = intval($rule->actions['move']['group_id']); // We don't need to move again when running rule actions unset($rule->actions['move']); } } } } // Make sure the group exists if (!isset($groups[$group_id])) { $group_id = null; } // Last ditch effort to check for a default group to deliver to if (empty($group_id)) { if (null != ($default_team = DAO_Group::getDefaultGroup())) { $group_id = $default_team->id; } else { // Bounce return null; } } // [JAS] It's important to not set the group_id on the ticket until the messages exist // or inbox filters will just abort. $fields = array(DAO_Ticket::MASK => $sMask, DAO_Ticket::SUBJECT => $sSubject, DAO_Ticket::IS_CLOSED => 0, DAO_Ticket::FIRST_WROTE_ID => intval($fromAddressInst->id), DAO_Ticket::LAST_WROTE_ID => intval($fromAddressInst->id), DAO_Ticket::CREATED_DATE => $iDate, DAO_Ticket::UPDATED_DATE => $iDate, DAO_Ticket::LAST_ACTION_CODE => CerberusTicketActionCode::TICKET_OPENED); $id = DAO_Ticket::createTicket($fields); // Apply routing actions to our new ticket ID if (isset($routing_rules) && is_array($routing_rules)) { foreach ($routing_rules as $rule) { $rule->run($id); } } } // [JAS]: Add requesters to the ticket if (!empty($fromAddressInst->id) && !empty($id)) { // Don't add a requester if the sender is a helpdesk address if (isset($helpdesk_senders[$fromAddressInst->email])) { $logger->info("[Parser] Not adding ourselves as a requester: " . $fromAddressInst->email); } else { DAO_Ticket::createRequester($fromAddressInst->id, $id); } } // Add the other TO/CC addresses to the ticket // [TODO] This should be cleaned up and optimized if ($settings->get('cerberusweb.core', CerberusSettings::PARSER_AUTO_REQ, 0)) { @($autoreq_exclude_list = $settings->get('cerberusweb.core', CerberusSettings::PARSER_AUTO_REQ_EXCLUDE, '')); $destinations = self::getDestinations($headers); if (is_array($destinations) && !empty($destinations)) { // Filter out any excluded requesters if (!empty($autoreq_exclude_list)) { @($autoreq_exclude = DevblocksPlatform::parseCrlfString($autoreq_exclude_list)); if (is_array($autoreq_exclude) && !empty($autoreq_exclude)) { foreach ($autoreq_exclude as $excl_pattern) { $excl_regexp = DevblocksPlatform::parseStringAsRegExp($excl_pattern); // Check all destinations for this pattern foreach ($destinations as $idx => $dest) { if (@preg_match($excl_regexp, $dest)) { unset($destinations[$idx]); } } } } } foreach ($destinations as $dest) { if (null != ($destInst = CerberusApplication::hashLookupAddress($dest, true))) { // Skip if the destination is one of our senders or the matching TO if (isset($helpdesk_senders[$destInst->email])) { continue; } DAO_Ticket::createRequester($destInst->id, $id); } } } } $attachment_path = APP_STORAGE_PATH . '/attachments/'; // [TODO] This should allow external attachments (S3) $fields = array(DAO_Message::TICKET_ID => $id, DAO_Message::CREATED_DATE => $iDate, DAO_Message::ADDRESS_ID => $fromAddressInst->id); $email_id = DAO_Message::create($fields); // Content DAO_MessageContent::create($email_id, $message->body); // Headers foreach ($headers as $hk => $hv) { DAO_MessageHeader::create($email_id, $hk, $hv); } // [mdf] Loop through files to insert attachment records in the db, and move temporary files if (!empty($email_id)) { foreach ($message->files as $filename => $file) { /* @var $file ParserFile */ //[mdf] skip rfc822 messages since we extracted their content above if ($file->mime_type == 'message/rfc822') { continue; } $fields = array(DAO_Attachment::MESSAGE_ID => $email_id, DAO_Attachment::DISPLAY_NAME => $filename, DAO_Attachment::MIME_TYPE => $file->mime_type, DAO_Attachment::FILE_SIZE => intval($file->file_size)); $file_id = DAO_Attachment::create($fields); if (empty($file_id)) { @unlink($file->tmpname); // remove our temp file continue; } // Make file attachments use buckets so we have a max per directory $attachment_bucket = sprintf("%03d/", mt_rand(1, 100)); $attachment_file = $file_id; if (!file_exists($attachment_path . $attachment_bucket)) { @mkdir($attachment_path . $attachment_bucket, 0770, true); // [TODO] Needs error checking } rename($file->getTempFile(), $attachment_path . $attachment_bucket . $attachment_file); // [TODO] Split off attachments into its own DAO DAO_Attachment::update($file_id, array(DAO_Attachment::FILEPATH => $attachment_bucket . $attachment_file)); } } // Pre-load custom fields if (isset($message->custom_fields) && !empty($message->custom_fields)) { foreach ($message->custom_fields as $cf_id => $cf_val) { if (is_array($cf_val) && !empty($cf_val) || !is_array($cf_val) && 0 != strlen($cf_val)) { DAO_CustomFieldValue::setFieldValue('cerberusweb.fields.source.ticket', $id, $cf_id, $cf_val); } } } // Finalize our new ticket details (post-message creation) if ($bIsNew && !empty($id) && !empty($email_id)) { // First thread (needed for anti-spam) DAO_Ticket::updateTicket($id, array(DAO_Ticket::FIRST_MESSAGE_ID => $email_id)); // Prime the change fields (which a few things like anti-spam might change before we commit) $change_fields = array(DAO_Ticket::TEAM_ID => $group_id); $out = CerberusBayes::calculateTicketSpamProbability($id); if (!empty($group_id)) { @($spam_threshold = DAO_GroupSettings::get($group_id, DAO_GroupSettings::SETTING_SPAM_THRESHOLD, 80)); @($spam_action = DAO_GroupSettings::get($group_id, DAO_GroupSettings::SETTING_SPAM_ACTION, '')); @($spam_action_param = DAO_GroupSettings::get($group_id, DAO_GroupSettings::SETTING_SPAM_ACTION_PARAM, '')); if ($out['probability'] * 100 >= $spam_threshold) { $enumSpamTraining = CerberusTicketSpamTraining::SPAM; switch ($spam_action) { default: case 0: // do nothing break; case 1: // delete $change_fields[DAO_Ticket::IS_CLOSED] = 1; $change_fields[DAO_Ticket::IS_DELETED] = 1; break; case 2: // move $buckets = DAO_Bucket::getAll(); // Verify bucket exists if (!empty($spam_action_param) && isset($buckets[$spam_action_param])) { $change_fields[DAO_Ticket::TEAM_ID] = $group_id; $change_fields[DAO_Ticket::CATEGORY_ID] = $spam_action_param; } break; } } } // end spam training // Save properties if (!empty($change_fields)) { DAO_Ticket::updateTicket($id, $change_fields); } } // Reply notifications (new messages are handled by 'move' listener) if (!$bIsNew) { // Inbound Reply Event $eventMgr = DevblocksPlatform::getEventService(); $eventMgr->trigger(new Model_DevblocksEvent('ticket.reply.inbound', array('ticket_id' => $id))); } // New ticket processing if ($bIsNew) { // Auto reply @($autoreply_enabled = DAO_GroupSettings::get($group_id, DAO_GroupSettings::SETTING_AUTO_REPLY_ENABLED, 0)); @($autoreply = DAO_GroupSettings::get($group_id, DAO_GroupSettings::SETTING_AUTO_REPLY, '')); /* * Send the group's autoreply if one exists, as long as this ticket isn't spam */ if (!isset($options['no_autoreply']) && $autoreply_enabled && !empty($autoreply) && $enumSpamTraining != CerberusTicketSpamTraining::SPAM) { CerberusMail::sendTicketMessage(array('ticket_id' => $id, 'message_id' => $email_id, 'content' => str_replace(array('#ticket_id#', '#mask#', '#subject#', '#timestamp#', '#sender#', '#sender_first#', '#orig_body#'), array($id, $sMask, $sSubject, date('r'), $fromAddressInst->email, $fromAddressInst->first_name, ltrim($message->body)), $autoreply), 'is_autoreply' => true, 'dont_keep_copy' => true)); } } // end bIsNew unset($message); // Re-open and update our date on new replies if (!$bIsNew) { DAO_Ticket::updateTicket($id, array(DAO_Ticket::UPDATED_DATE => time(), DAO_Ticket::IS_WAITING => 0, DAO_Ticket::IS_CLOSED => 0, DAO_Ticket::IS_DELETED => 0, DAO_Ticket::LAST_WROTE_ID => $fromAddressInst->id, DAO_Ticket::LAST_ACTION_CODE => CerberusTicketActionCode::TICKET_CUSTOMER_REPLY)); // [TODO] The TICKET_CUSTOMER_REPLY should be sure of this message address not being a worker } @imap_errors(); // Prevent errors from spilling out into STDOUT return $id; }
static function getMatches(Model_Address $fromAddress, CerberusParserMessage $message) { // print_r($fromAddress); // print_r($message); $matches = array(); $rules = DAO_MailToGroupRule::getWhere(); $message_headers = $message->headers; $custom_fields = DAO_CustomField::getAll(); // Lazy load when needed on criteria basis $address_field_values = null; $org_field_values = null; // Check filters if (is_array($rules)) { foreach ($rules as $rule) { /* @var $rule Model_MailToGroupRule */ $passed = 0; // check criteria foreach ($rule->criteria as $crit_key => $crit) { @($value = $crit['value']); switch ($crit_key) { case 'dayofweek': $current_day = strftime('%w'); // $current_day = 1; // Forced to English abbrevs as indexes $days = array('sun', 'mon', 'tue', 'wed', 'thu', 'fri', 'sat'); // Is the current day enabled? if (isset($crit[$days[$current_day]])) { $passed++; } break; case 'timeofday': $current_hour = strftime('%H'); $current_min = strftime('%M'); // $current_hour = 17; // $current_min = 5; if (null != ($from_time = @$crit['from'])) { list($from_hour, $from_min) = explode(':', $from_time); } if (null != ($to_time = @$crit['to'])) { if (list($to_hour, $to_min) = explode(':', $to_time)) { } } // Do we need to wrap around to the next day's hours? if ($from_hour > $to_hour) { // yes $to_hour += 24; // add 24 hrs to the destination (1am = 25th hour) } // Are we in the right 24 hourly range? if ((int) $current_hour >= $from_hour && (int) $current_hour <= $to_hour) { // If we're in the first hour, are we minutes early? if ($current_hour == $from_hour && (int) $current_min < $from_min) { break; } // If we're in the last hour, are we minutes late? if ($current_hour == $to_hour && (int) $current_min > $to_min) { break; } $passed++; } break; case 'tocc': $tocc = array(); $destinations = DevblocksPlatform::parseCsvString($value); // Build a list of To/Cc addresses on this message @($to_list = imap_rfc822_parse_adrlist($message_headers['to'], 'localhost')); @($cc_list = imap_rfc822_parse_adrlist($message_headers['cc'], 'localhost')); if (is_array($to_list)) { foreach ($to_list as $addy) { $tocc[] = $addy->mailbox . '@' . $addy->host; } } if (is_array($cc_list)) { foreach ($cc_list as $addy) { $tocc[] = $addy->mailbox . '@' . $addy->host; } } $dest_flag = false; // bail out when true if (is_array($destinations) && is_array($tocc)) { foreach ($destinations as $dest) { if ($dest_flag) { break; } $regexp_dest = DevblocksPlatform::strToRegExp($dest); foreach ($tocc as $addy) { if (@preg_match($regexp_dest, $addy)) { $passed++; $dest_flag = false; break; } } } } break; case 'from': $regexp_from = DevblocksPlatform::strToRegExp($value); if (@preg_match($regexp_from, $fromAddress->email)) { $passed++; } break; case 'subject': // [TODO] Decode if necessary @($subject = $message_headers['subject']); $regexp_subject = DevblocksPlatform::strToRegExp($value); if (@preg_match($regexp_subject, $subject)) { $passed++; } break; case 'body': // Line-by-line body scanning (sed-like) $lines = preg_split("/[\r\n]/", $message->body); if (is_array($lines)) { foreach ($lines as $line) { if (@preg_match($value, $line)) { $passed++; break; } } } break; case 'header1': case 'header2': case 'header3': case 'header4': case 'header5': @($header = strtolower($crit['header'])); if (empty($header)) { $passed++; break; } if (empty($value)) { // we're checking for null/blanks if (!isset($message_headers[$header]) || empty($message_headers[$header])) { $passed++; } } elseif (isset($message_headers[$header]) && !empty($message_headers[$header])) { $regexp_header = DevblocksPlatform::strToRegExp($value); // Flatten CRLF if (@preg_match($regexp_header, str_replace(array("\r", "\n"), ' ', $message_headers[$header]))) { $passed++; } } break; default: // ignore invalids // Custom Fields if (0 == strcasecmp('cf_', substr($crit_key, 0, 3))) { $field_id = substr($crit_key, 3); // Make sure it exists if (null == @($field = $custom_fields[$field_id])) { continue; } // Lazy values loader $field_values = array(); switch ($field->source_extension) { case ChCustomFieldSource_Address::ID: if (null == $address_field_values) { $address_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Address::ID, $fromAddress->id)); } $field_values =& $address_field_values; break; case ChCustomFieldSource_Org::ID: if (null == $org_field_values) { $org_field_values = array_shift(DAO_CustomFieldValue::getValuesBySourceIds(ChCustomFieldSource_Org::ID, $fromAddress->contact_org_id)); } $field_values =& $org_field_values; break; } // No values, default. if (!isset($field_values[$field_id])) { continue; } // Type sensitive value comparisons switch ($field->type) { case 'S': // string // string case 'T': // clob // clob case 'U': // URL $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : ''; $oper = isset($crit['oper']) ? $crit['oper'] : "="; if ($oper == "=" && @preg_match(DevblocksPlatform::strToRegExp($value, true), $field_val)) { $passed++; } elseif ($oper == "!=" && @(!preg_match(DevblocksPlatform::strToRegExp($value, true), $field_val))) { $passed++; } break; case 'N': // number if (!isset($field_values[$field_id])) { break; } $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : 0; $oper = isset($crit['oper']) ? $crit['oper'] : "="; if ($oper == "=" && intval($field_val) == intval($value)) { $passed++; } elseif ($oper == "!=" && intval($field_val) != intval($value)) { $passed++; } elseif ($oper == ">" && intval($field_val) > intval($value)) { $passed++; } elseif ($oper == "<" && intval($field_val) < intval($value)) { $passed++; } break; case 'E': // date $field_val = isset($field_values[$field_id]) ? intval($field_values[$field_id]) : 0; $from = isset($crit['from']) ? $crit['from'] : "0"; $to = isset($crit['to']) ? $crit['to'] : "now"; if (intval(@strtotime($from)) <= $field_val && intval(@strtotime($to)) >= $field_val) { $passed++; } break; case 'C': // checkbox $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : 0; if (intval($value) == intval($field_val)) { $passed++; } break; case 'D': // dropdown // dropdown case 'X': // multi-checkbox // multi-checkbox case 'M': // multi-picklist // multi-picklist case 'W': // worker $field_val = isset($field_values[$field_id]) ? $field_values[$field_id] : array(); if (!is_array($value)) { $value = array($value); } if (is_array($field_val)) { // if multiple things set foreach ($field_val as $v) { // loop through possible if (isset($value[$v])) { // is any possible set? $passed++; break; } } } else { // single if (isset($value[$field_val])) { // is our set field in possibles? $passed++; break; } } break; } } break; } } // If our rule matched every criteria, stop and return the filter if ($passed == count($rule->criteria)) { DAO_MailToGroupRule::increment($rule->id); // ++ the times we've matched $matches[$rule->id] = $rule; // Bail out if this rule had a move action if (isset($rule->actions['move'])) { return $matches; } } } } // If we're at the end of rules and didn't bail out yet if (!empty($matches)) { return $matches; } // No matches return NULL; }
function doWorkersBulkUpdateAction() { // Checked rows @($ids_str = DevblocksPlatform::importGPC($_REQUEST['ids'], 'string')); $ids = DevblocksPlatform::parseCsvString($ids_str); // Filter: whole list or check @($filter = DevblocksPlatform::importGPC($_REQUEST['filter'], 'string', '')); // View @($view_id = DevblocksPlatform::importGPC($_REQUEST['view_id'], 'string')); $view = C4_AbstractViewLoader::getView($view_id); // Worker fields @($is_disabled = trim(DevblocksPlatform::importGPC($_POST['is_disabled'], 'string', ''))); $do = array(); // Do: Disabled if (0 != strlen($is_disabled)) { $do['is_disabled'] = $is_disabled; } // Do: Custom fields $do = DAO_CustomFieldValue::handleBulkPost($do); $view->doBulkUpdate($filter, $do, $ids); $view->render(); return; }
public function saveFeedbackAction() { $active_worker = CerberusApplication::getActiveWorker(); // Make sure we're an active worker if (empty($active_worker) || empty($active_worker->id)) { return; } @($id = DevblocksPlatform::importGPC($_REQUEST['id'], 'integer', 0)); @($do_delete = DevblocksPlatform::importGPC($_REQUEST['do_delete'], 'integer', 0)); @($email = DevblocksPlatform::importGPC($_POST['email'], 'string', '')); @($mood = DevblocksPlatform::importGPC($_POST['mood'], 'integer', 0)); @($quote = DevblocksPlatform::importGPC($_POST['quote'], 'string', '')); @($url = DevblocksPlatform::importGPC($_POST['url'], 'string', '')); @($source_extension_id = DevblocksPlatform::importGPC($_POST['source_extension_id'], 'string', '')); @($source_id = DevblocksPlatform::importGPC($_POST['source_id'], 'integer', 0)); // Translate email string into addy id, if exists $address_id = 0; if (!empty($email)) { if (null != ($author_address = DAO_Address::lookupAddress($email, true))) { $address_id = $author_address->id; } } // Delete entries if (!empty($id) && !empty($do_delete)) { if (null != ($entry = DAO_FeedbackEntry::get($id))) { // Only superusers and owners can delete entries if ($active_worker->is_superuser || $active_worker->id == $entry->worker_id) { DAO_FeedbackEntry::delete($id); } } return; } // New or modify $fields = array(DAO_FeedbackEntry::QUOTE_MOOD => intval($mood), DAO_FeedbackEntry::QUOTE_TEXT => $quote, DAO_FeedbackEntry::QUOTE_ADDRESS_ID => intval($address_id), DAO_FeedbackEntry::SOURCE_URL => $url); // Only on new if (empty($id)) { $fields[DAO_FeedbackEntry::LOG_DATE] = time(); $fields[DAO_FeedbackEntry::WORKER_ID] = $active_worker->id; } if (empty($id)) { // create $id = DAO_FeedbackEntry::create($fields); // Post-create actions if (!empty($source_extension_id) && !empty($source_id)) { switch ($source_extension_id) { case 'feedback.source.ticket': // Create a ticket comment about the feedback (to prevent dupes) if (null == ($worker_address = DAO_Address::lookupAddress($active_worker->email))) { break; } $comment_text = sprintf("== Capture Feedback ==\n" . "Author: %s\n" . "Mood: %s\n" . "\n" . "%s\n", !empty($author_address) ? $author_address->email : 'Anonymous', empty($mood) ? 'Neutral' : (1 == $mood ? 'Praise' : 'Criticism'), $quote); $fields = array(DAO_TicketComment::ADDRESS_ID => $worker_address->id, DAO_TicketComment::COMMENT => $comment_text, DAO_TicketComment::CREATED => time(), DAO_TicketComment::TICKET_ID => intval($source_id)); DAO_TicketComment::create($fields); break; } } } else { // modify DAO_FeedbackEntry::update($id, $fields); } // Custom field saves @($field_ids = DevblocksPlatform::importGPC($_POST['field_ids'], 'array', array())); DAO_CustomFieldValue::handleFormPost(ChCustomFieldSource_FeedbackEntry::ID, $id, $field_ids); DevblocksPlatform::redirect(new DevblocksHttpResponse(array('iphone', 'feedback', 'display', $id))); }