* @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('
-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 = ''; } }