* @license PHP License * @package Wombat * @subpackage Ticket System */ /** * Load classes */ WBClass::load('WBITS' , 'WBUser'); /** * Wombat Incident Ticket System: Ticket * * @version 1.4.3 * @package Wombat * @subpackage Ticket System */ class WBITS_Ticket extends WBStdClass { /** * Ticket Data * @var array */ private $data = array(); /** * Ticket Id * @var string */ private $id = ''; /** * Ticket obscure string * @var string */ private $obscure = ''; /** * Tell whether ticket has to be treated as preg-generated * @bool */ private $pregen = false; /** * Table access * @var WBDatasource_Table_ITS */ private $table; /** * relation table * @var WBDatasource_RelationTable */ private $relTable = null; /** * ITS configuration * Loaded using WBITS::getConfig * @var array */ private $config = array(); /** * Filter Clause * @var array */ private $clause = array(); /** * Switch: Trigger Events, usually do it! * @var bool */ private $triggerEvent = true; /** * Constructor * * @param array $parameter */ public function __construct($parameter = array()) { // load general config $this->config = WBITS::getConfig(); // load relation config $config = WBClass::create('WBConfig'); if ($config->load('its/relation')) { $this->setRelationConfig($config->get('relation', array())); } if (isset($parameter['namespace'])) { $this->config['namespace'] = WBITS::selectNamespace($parameter['namespace']); } if (isset($parameter['table']) && $parameter['table']) { $this->table = $parameter['table']; return; } // Event trigger switch if (isset($parameter['triggerevent'])) { $this->triggerEvent = (0 < $parameter['triggerevent']); } $this->table = WBClass::create('WBDatasource_Table_ITS'); $this->table->switchTranslation(false); } /** * Set Filter Clause * * @param array $clause * @param bool $append */ public function setClause($clause, $append = false) { if ($append) { $this->clause[] = $clause; return; } $this->clause = $clause; } /** * Configure Relation Table * * Use relation configuration to set up related tables * * @see WBDatasource_RelationTable * @param array $config */ private function setRelationConfig($conf = array()) { if (empty($conf)) { $this->relTable = null; return; } if (!$this->relTable) { $this->relTable = WBClass::create('WBDatasource_RelationTable'); $this->relTable->setTable($this->table); } $this->relTable->setConfig(WBITS::TABLE_TICKET, $conf); } /** * Load ticket by obscureId * * @see loadById() * @param string $obscureId * @param string $namespace * @return WBITS_Ticket */ public function loadByObscureId($obscureId, $namespace = '') { $obscureId = trim($obscureId); // pregen obscureid if (0 == strncmp($obscureId, 'pregen:', 7)) { $this->pregen = true; $obscureId = trim(substr($obscureId, 7)); } $match = $this->extractObscureId($obscureId); if (empty($match)) { $this->flush(); $this->pregen = false; return $this; } $this->loadById($match[2]); if (empty($this->id)) { $this->pregen = false; return $this; } // ignore cases for check if ($this->obscure != $obscureId) { $this->flush(); $this->pregen = false; return $this; } // don't check namspace for pregen ticket ids if ($this->pregen) { return $this; } // check namespace? if (empty($namespace)) { return $this; } if ($this->data['namespace'] != $namespace) { $this->flush(); $this->pregen = false; return $this; } return $this; } /** * Extract ObscureId * * Match pattern for obscure code. Return match in array * * @see preg_match() * @return array */ public function extractObscureId($obscureId) { $obscureId = strtoupper($obscureId); // extract id and load by id $pattern = '/^([A-Z0-9]{'. ($this->config['obscurelength'] - 1) .'}[A-Z])(\d+)$/'; if (!preg_match($pattern, $obscureId, $match)) { return array(); } return $match; } /** * Load ticket by id * * Only for internal use - otherwise protection with obscure id will be lost. * * @param string $id * @return WBITS_Ticket */ public function loadById($id) { $this->flush(); $options = array( 'limit' => 2, 'callback' => WBClass::create('WBITS_DatasourceCallback', $this->config) ); $list = $this->table->get(WBITS::TABLE_TICKET, $id, null, $this->clause, $options); if (1 != count($list)) { return $this; } $this->id = $id; $this->obscure = $list[0]['obscureid']; // add related data if ($this->relTable) { $this->relTable->inject($list[0]);; } unset($list[0]['id']); unset($list[0][$this->table->getIdentifier(WBITS::TABLE_TICKET)]); $this->data = $list[0]; return $this; } /** * Tell Whether Current Ticket is PreGenerated * * @return bool */ public function isPregen() { return $this->pregen; } /** * Get Ticket Id * * @return string */ public function getId() { return $this->id; } /** * Get Ticket Obscure Id * * @return string */ public function getObscureId() { return $this->obscure; } /** * Get ticket data * * @return array */ public function getData() { return $this->data; } /** * Create Empty Ticket * * Allow to create ticket id with obscure string to be filled afterwards * * @see save() * @return array */ public function pregenerate() { $data = array(); $data['namespace'] = $this->config['namespace']; $data['obscure'] = strtoupper(WBDatasource_ObscureCode::mkRandObscure($this->config['obscurelength'], true, true)); $data['status'] = 'new'; $data['statuschanged'] = gmdate('Y-m-d H:i:s'); $data['assigneduid'] = 0; $this->id = $this->table->save(WBITS::TABLE_TICKET, '__new', $data); $this->obscure = $data['obscure'] . $this->id; $this->pregen = true; $data['id'] = $this->id; $data['obscureid'] = $this->obscure; return $data; } /** * Save ticket Data in table * * @param array $data */ public function save($data) { $event = 'its:ticket:new:'; $msg = 'Created ticket'; $eData = array(); $diff = array(); // add current user's id $uPrimary = $this->table->getIdentifier(WBDatasource::TABLE_USER, true); $data[$uPrimary] = WBUser::getCurrent()->getId(); if (empty($data[$uPrimary])) { $data[$uPrimary] = 0; } // extract comment $comment = ''; if (isset($data['comment'])) { $comment = trim($data['comment']); unset($data['comment']); } // strip
at the end of a paragraph if (isset($data['blurb'])) { $data['blurb'] = trim($data['blurb']); if (!empty($data['blurb'])) { $data['blurb'] = str_replace('

', '

', $data['blurb']); // force

-tags $data['blurb'] = trim($data['blurb']); if (0 != strncmp('

', $data['blurb'], 3)) { $data['blurb'] = '

' . $data['blurb'] . '

'; } } } if (empty($this->id)) { $id = '__new'; // default values if (!isset($data['namespace']) || empty($data['namespace'])) { $ns = $this->config['namespace']; $data['namespace'] = $ns; } else { $ns = $data['namespace']; } $event .= $ns; if (!isset($data['assigneduid']) || empty($data['assigneduid'])) { $data['assigneduid'] = 0; } if (empty($data['status'])) { $data['status'] = 'new'; } $data['statuschanged'] = gmdate('Y-m-d H:i:s'); WBClass::load('WBDatasource_ObscureCode'); $data['obscure'] = strtoupper(WBDatasource_ObscureCode::mkRandObscure($this->config['obscurelength'], true, true)); $this->obscure = $data['obscure']; } else { $id = $this->id; $ns = $this->data['namespace']; // ignore these keys because they refer to auto generated data $ignore = array( 'created', 'changed', 'statuschanged', 'obscureid', 'deleted', 'obscure', $uPrimary ); // avoid empty status if (empty($data['status'])) { unset($data['status']); } // check what was changed foreach ($this->data as $k => $v) { if (in_array($k, $ignore)) { continue; } if (!isset($data[$k])) { continue; } if ($data[$k] == $v) { continue; } if (is_array($v)) { if (!is_array($data[$k])) { $data[$k] = array(); } $diff[$k] = array(); foreach ($v as $tmp) { if (!in_array($tmp, $data[$k])) { $diff[$k][] = array( 'value' => $tmp, 'action' => 'deleted' ); } } foreach ($data[$k] as $tmp) { if (!in_array($tmp, $v)) { $diff[$k][] = array( 'value' => $tmp, 'action' => 'added' ); } } continue; } $diff[$k] = $v; } // don't save ticket if (empty($diff)) { // skip the rest if (!$this->triggerEvent) { return; } // trigger comment if (!empty($comment)) { $event = 'its:ticket:commented:' . $ns; $msg = 'Ticket commented'; $eData = array_merge($eData, $this->data); $eData['id'] = $this->id; $eData['obscureid'] = $this->obscure; $eData['comment'] = $comment; WBEvent::trigger($event, $msg, $eData); } return; } $eData['diffkeys'] = implode(', ', array_keys($diff)); $eData['comment'] = $comment; $data = array_intersect_key($data, $this->data); $msg = 'Ticket data changed ({DIFFKEYS})'; // change of namespace - happens only rarely if (isset($data['namespace'])) { $ns = $data['namespace']; } // event name if ($this->pregen) { $this->pregen = false; $event = 'its:ticket:new:' . $ns; $data['statuschanged'] = gmdate('Y-m-d H:i:s'); } else { $event = 'its:ticket:saved:' . $ns; // status changes if (isset($data['status']) && $data['status'] != $this->data['status']) { $event = 'its:ticket:statuschanged:' . $data['status'] . ':' . $ns; $msg = 'Ticket status changed from {STATUSOLD} to {STATUS}'; $eData['statusold'] = $this->data['status']; $data['statuschanged'] = gmdate('Y-m-d H:i:s'); } // assignee changes else if (isset($data['assigneduid']) && $data['assigneduid'] != $this->data['assigneduid']) { $event = 'its:ticket:reassigned:' . $ns; $msg = 'Removed assignment from {ASSIGNEDUIDOLD}, Set assignment to {ASSIGNEDUID}.'; } } // add assigneduidold anyway if (isset($data['assigneduid']) && $data['assigneduid'] != $this->data['assigneduid']) { $eData['assigneduidold']= $this->data['assigneduid']; } } if ($this->relTable) { $this->relTable->extract($data); if ('__new' != $id) { $this->relTable->delete($this->id, true); } } $this->id = $this->table->save(WBITS::TABLE_TICKET, $id, $data); if ('__new' == $id) { $this->obscure .= $this->id; } $this->data = array_merge($this->data, $data); if ($this->relTable) { $this->relTable->add($this->id); // clean up current data $this->data[$this->table->getIdentifier(WBITS::TABLE_TICKET)] = $this->id; $this->relTable->inject($this->data); unset($this->data[$this->table->getIdentifier(WBITS::TABLE_TICKET)]); } // skip the rest if (!$this->triggerEvent) { return; } $this->data['obscure'] = substr($this->obscure, 0, $this->config['obscurelength']); /** @var WBITS_DatasourceCallback */ $cb = WBClass::create('WBITS_DatasourceCallback', $this->config); $cb->onDatasourceGet(WBITS::TABLE_TICKET, $this->id, $this->data); $eData = array_merge($eData, $this->data); $eData['id'] = $this->id; $eData['obscureid'] = $this->obscure; $eData['diff'] = $diff; if (!empty($comment)) { $eData['comment'] = $comment; } WBEvent::trigger($event, $msg, $eData); } /** * Flush ticket data */ private function flush() { $this->data = array(); $this->obscure = ''; $this->id = ''; } }