diff --git a/bootstrap.php b/bootstrap.php index d16f10feefc17f3e4c462a7f792f553208cf00fb..354c9bd208acc4ecde10f0d75df299d53c0e4357 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -92,6 +92,7 @@ class Bootstrap { define('THREAD_TABLE', $prefix.'thread'); define('THREAD_ENTRY_TABLE', $prefix.'thread_entry'); + define('THREAD_ENTRY_ATTACHMENT_TABLE', $prefix.'thread_entry_attachment'); define('THREAD_ENTRY_EMAIL_TABLE', $prefix.'thread_entry_email'); define('TICKET_TABLE',$prefix.'ticket'); diff --git a/include/class.attachment.php b/include/class.attachment.php index a1ecfd97af55ea66d0ea9c684bb07ef96ab0ba03..e7e4c073f016a8f3f46179c030aaa353e5220b5e 100644 --- a/include/class.attachment.php +++ b/include/class.attachment.php @@ -23,19 +23,27 @@ class Attachment { var $ht; var $object; - function Attachment($id) { + function Attachment($id, $tid=0) { - $sql = 'SELECT a.* FROM '.ATTACHMENT_TABLE.' a ' - . 'WHERE a.id='.db_input($id); - if (!($res=db_query($sql)) || !db_num_rows($res)) - return; + $sql = ' SELECT * FROM '.THREAD_ENTRY_ATTACHMENT_TABLE.' WHERE id='.db_input($id); + if($tid) + $sql.=' AND thread_entry_id='.db_input($tid); - $this->ht = db_fetch_array($res); - $this->file = $this->object = null; + if(!($res=db_query($sql)) || !db_num_rows($res)) + return false; + + $this->ht=db_fetch_array($res); + + $this->id=$this->ht['id']; + $this->file_id=$this->ht['file_id']; + + $this->file=null; + + return true; } function getId() { - return $this->ht['id']; + return $this->id; } function getFileId() { @@ -57,32 +65,24 @@ class Attachment { return $this->getHashtable(); } - function getObject() { - - if (!isset($this->object)) - $this->object = ObjectModel::lookup( - $this->ht['object_id'], $this->ht['type']); - - return $this->object; - } - - static function getIdByFileHash($hash, $objectId=0) { - $sql='SELECT a.id FROM '.ATTACHMENT_TABLE.' a ' + /* Static functions */ + function getIdByFileHash($hash, $tid=0) { + $sql='SELECT a.id FROM '.THREAD_ENTRY_ATTACHMENT_TABLE.' a ' .' INNER JOIN '.FILE_TABLE.' f ON(f.id=a.file_id) ' .' WHERE f.`key`='.db_input($hash); - if ($objectId) - $sql.=' AND a.object_id='.db_input($objectId); + if($tid) + $sql.=' AND a.thread_entry_id='.db_input($tid); return db_result(db_query($sql)); } - static function lookup($var, $objectId=0) { + function lookup($var, $tid=0) { - $id = is_numeric($var) ? $var : self::getIdByFileHash($var, $oid); + $id = is_numeric($var) ? $var : self::getIdByFileHash($var, $tid); return ($id && is_numeric($id) - && ($attach = new Attachment($id, $oid)) + && ($attach = new Attachment($id, $tid)) && $attach->getId()==$id ) ? $attach : null; } diff --git a/include/class.file.php b/include/class.file.php index caafae2bf9cab7018b6aee5d4a8503d2f7d84295..74434bcdad9028e894334f4bd02949b834d3d409 100644 --- a/include/class.file.php +++ b/include/class.file.php @@ -35,7 +35,7 @@ class AttachmentFile { .' FROM '.FILE_TABLE.' f ' .' LEFT JOIN '.ATTACHMENT_TABLE.' a ON(a.file_id=f.id) ' - .' LEFT JOIN '.ATTACHMENT_TABLE.' t + .' LEFT JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' t ON(t.file_id = f.id) ' .' WHERE f.id='.db_input($id) .' GROUP BY f.id'; @@ -581,6 +581,8 @@ class AttachmentFile { // table and are not logos //FIXME: Just user straight up left join $sql = 'SELECT id FROM '.FILE_TABLE.' WHERE id NOT IN (' + .'SELECT file_id FROM '.THREAD_ENTRY_ATTACHMENT_TABLE + .' UNION ' .'SELECT file_id FROM '.ATTACHMENT_TABLE .") AND `ft` = 'T' AND TIMESTAMPDIFF(DAY, `created`, CURRENT_TIMESTAMP) > 1"; diff --git a/include/class.thread.php b/include/class.thread.php index 9b7be1bf9a0ef0d1feeccb77de144e5df84e34ba..7c5635bd420aa9b64b5b4dc8d7202507f474210f 100644 --- a/include/class.thread.php +++ b/include/class.thread.php @@ -32,13 +32,13 @@ class Thread { return null; $sql='SELECT thread.* ' - .' ,count(DISTINCT a.id) as attachments ' + .' ,count(DISTINCT attach.id) as attachments ' .' ,count(DISTINCT entry.id) as entries ' .' FROM '.THREAD_TABLE.' thread ' .' LEFT JOIN '.THREAD_ENTRY_TABLE.' entry ON (entry.thread_id = thread.id) ' - .' LEFT JOIN '.ATTACHMENT_TABLE.' a - ON (a.object_id=entry.id AND a.`type` = "H") ' + .' LEFT JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' attach + ON (attach.thread_entry_id=entry.id) ' .' WHERE thread.id='.db_input($id) .' GROUP BY thread.id'; @@ -65,17 +65,8 @@ class Thread { return $this->ht['object_type']; } - function getObject() { - - if (!$this->_object) - $this->_object = ObjectModel::lookup( - $this->getObjectId(), $this->getObjectType()); - - return $this->_object; - } - - function getNumAttachments() { - return $this->ht['attachments']; + function getObjectId() { + return $this->ht['object_id']; } function getNumEntries() { @@ -84,8 +75,8 @@ class Thread { function getEntries($criteria) { - if (!$criteria['order'] || !in_array($criteria['order'], array('DESC','ASC'))) - $criteria['order'] = 'ASC'; + if (!$order || !in_array($order, array('DESC','ASC'))) + $order='ASC'; $sql='SELECT entry.* , COALESCE(user.name, @@ -98,21 +89,17 @@ class Thread { ON (entry.user_id=user.id) ' .' LEFT JOIN '.STAFF_TABLE.' staff ON (entry.staff_id=staff.staff_id) ' - .' LEFT JOIN '.ATTACHMENT_TABLE.' attach - ON (attach.object_id = entry.id AND attach.`type`="H") ' + .' LEFT JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' attach + ON (attach.thread_entry_id = entry.id) ' .' WHERE entry.thread_id='.db_input($this->getId()); - if ($criteria['type'] && is_array($criteria['type'])) - $sql.=' AND entry.`type` IN (' - .implode(',', db_input($criteria['type'])).')'; - elseif ($criteria['type']) - $sql.=' AND entry.`type` = '.db_input($criteria['type']); + if($type && is_array($type)) + $sql.=' AND entry.`type` IN ('.implode(',', db_input($type)).')'; + elseif($type) + $sql.=' AND entry.`type` = '.db_input($type); $sql.=' GROUP BY entry.id ' - .' ORDER BY entry.created '.$criteria['order']; - - if ($criteria['limit']) - $sql.=' LIMIT '.$criteria['limit']; + .' ORDER BY entry.created '.$order; $entries = array(); if(($res=db_query($sql)) && db_num_rows($res)) { @@ -133,9 +120,9 @@ class Thread { function deleteAttachments() { // Clear reference table - $sql = 'DELETE FROM '.ATTACHMENT_TABLE. ' a ' + $sql = 'DELETE FROM '.THREAD_ENTRY_ATTACHMENT_TABLE. ' a ' . 'INNER JOIN '.THREAD_ENTRY_TABLE.' e - ON(e.id = a.object_id AND a.`type`= "H") ' + ON(e.id = a.thread_entry_id) ' . ' WHERE e.thread_id='.db_input($this->getId()); $deleted=0; @@ -171,6 +158,19 @@ class Thread { return true; } + function getVar($name) { + switch ($name) { + case 'original': + return Message::firstByTicketId($this->ticket->getId()) + ->getBody(); + break; + case 'last_message': + case 'lastmessage': + return $this->ticket->getLastMessage()->getBody(); + break; + } + } + static function create($vars) { if (!$vars || !$vars['object_id'] || !$vars['object_type']) @@ -216,8 +216,8 @@ Class ThreadEntry { .' FROM '.THREAD_ENTRY_TABLE.' entry ' .' LEFT JOIN '.THREAD_ENTRY_EMAIL_TABLE.' email ON (email.thread_entry_id=entry.id) ' - .' LEFT JOIN '.ATTACHMENT_TABLE.' attach - ON (attach.object_id=entry.id AND attach.`type` = "H") ' + .' LEFT JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' attach + ON (attach.thread_entry_id=entry.id) ' .' WHERE entry.id='.db_input($id); if ($type) @@ -233,7 +233,8 @@ Class ThreadEntry { $this->ht = db_fetch_array($res); $this->id = $this->ht['id']; - $this->attachments = new GenericAttachments($this->id, 'H'); + + $this->attachments = array(); return true; } @@ -491,7 +492,20 @@ Class ThreadEntry { $inline = is_array($file) && @$file['inline']; - return $this->attachments->save($file, $inline); + // TODO: Add a unique index to THREAD_ENTRY_ATTACHMENT_TABLE (file_id, + // thread_entry_id), and remove this block + if ($id = db_result(db_query('SELECT id FROM '.THREAD_ENTRY_ATTACHMENT_TABLE + .' WHERE file_id='.db_input($fileId) + .' AND thread_entry_id=' .db_input($this->getId())))) + + return $id; + + $sql ='INSERT IGNORE INTO '.THREAD_ENTRY_ATTACHMENT_TABLE.' SET created=NOW() ' + .' ,file_id='.db_input($fileId) + .' ,thread_entry_id='.db_input($this->getId()) + .' ,inline='.db_input($inline ? 1 : 0); + + return (db_query($sql) && ($id=db_insert_id()))?$id:0; } function saveAttachments($files) { @@ -504,15 +518,32 @@ Class ThreadEntry { } function getAttachments() { - return $this->attachments->getAll(true, false); + + if ($this->attachments) + return $this->attachments; + + //XXX: inner join the file table instead? + $sql='SELECT a.id, f.id as file_id, f.size, lower(f.`key`) as file_hash, f.name, a.inline ' + .' FROM '.FILE_TABLE.' f ' + .' INNER JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' a + ON(a.file_id=f.id) ' + .' WHERE a.thread_entry_id='.db_input($this->getId()); + + $this->attachments = array(); + if (($res=db_query($sql)) && db_num_rows($res)) { + while ($rec=db_fetch_array($res)) + $this->attachments[] = $rec; + } + + return $this->attachments; } function getAttachmentUrls($script='image.php') { $json = array(); foreach ($this->getAttachments() as $att) { - $json[$att['key']] = array( - 'download_url' => sprintf('attachment.php?id=%d&h=%s', - $att['attach_id'], $att['download']), + $json[$att['file_hash']] = array( + 'download_url' => sprintf('attachment.php?id=%d&h=%s', $att['id'], + strtolower(md5($att['file_id'].session_id().$att['file_hash']))), 'filename' => $att['name'], ); } @@ -530,13 +561,7 @@ Class ThreadEntry { $size=sprintf('<em>(%s)</em>', Format::file_size($att['size'])); $str.=sprintf('<a class="Icon file no-pjax" href="%s?id=%d&h=%s" target="%s">%s</a>%s %s', - $file, - $att['attach_id'], - $att['download'], - $target, - Format::htmlchars($att['name']), - $size, - $separator); + $file, $attachment['id'], $hash, $target, Format::htmlchars($attachment['name']), $size, $separator); } return $str; @@ -1276,8 +1301,30 @@ class MessageThreadEntry extends ThreadEntry { )?$m:null; } + //TODO: redo shit below. + + function lastByTicketId($ticketId) { + return self::byTicketId($ticketId); + } + + function firstByTicketId($ticketId) { + return self::byTicketId($ticketId, false); + } + + function byTicketId($ticketId, $last=true) { + + $sql=' SELECT thread.id FROM '.TICKET_THREAD_TABLE.' thread ' + .' WHERE thread_type=\'M\' AND thread.ticket_id = '.db_input($ticketId) + .sprintf(' ORDER BY thread.id %s LIMIT 1', $last ? 'DESC' : 'ASC'); + + if (($res = db_query($sql)) && ($id = db_result($res))) + return Message::lookup($id); + + return null; + } } + /* thread entry of type response */ class ResponseThreadEntry extends ThreadEntry { @@ -1409,42 +1456,15 @@ class TicketThread extends Thread { } function getMessages() { - return $this->getEntries(array( - 'type' => MessageThreadEntry::ENTRY_TYPE)); - } - - function getLastMessage() { - - $criteria = array( - 'type' => MessageThreadEntry::ENTRY_TYPE, - 'order' => 'DESC', - 'limit' => 1); - - return $this->getEntry($criteria); - } - - function getEntry($var) { - - if (is_numeric($var)) - $id = $var; - else { - $criteria = array_merge($var, array('limit' => 1)); - $entries = $this->getEntries($criteria); - if ($entries && $entries[0]) - $id = $entries[0]['id']; - } - - return $id ? parent::getEntry($id) : null; + return $this->getEntries(MessageThreadEntry::ENTRY_TYPE); } function getResponses() { - return $this->getEntries(array( - 'type' => ResponseThreadEntry::ENTRY_TYPE)); + return $this->getEntries(ResponseThreadEntry::ENTRY_TYPE); } function getNotes() { - return $this->getEntries(array( - 'type' => NoteThreadEntry::ENTRY_TYPE)); + return $this->getEntries(NoteThreadEntry::ENTRY_TYPE); } function addNote($vars, &$errors) { @@ -1470,26 +1490,15 @@ class TicketThread extends Thread { return ResponseThreadEntry::create($vars, $errors); } + //TODO: revisit function getVar($name) { switch ($name) { - case 'original': - $entries = $this->getEntries(array( - 'type' => MessageThreadEntry::ENTRY_TYPE, - 'order' => 'ASC', - 'limit' => 1)); - if ($entries && $entries[0]) - return (string) $entries[0]['body']; - + case 'original': + return MessageThreadEntry::first($this->getId())->getBody(); break; case 'last_message': case 'lastmessage': - $entries = $this->getEntries(array( - 'type' => MessageThreadEntry::ENTRY_TYPE, - 'order' => 'DESC', - 'limit' => 1)); - if ($entries && $entries[0]) - return (string) $entries[0]['body']; - + return $this->ticket->getLastMessage()->getBody(); break; } } diff --git a/include/class.ticket.php b/include/class.ticket.php index 43c1ed87593ebfbd3acd76e592396288b3c6ae80..3031e03d74df062d71366eb63adc678ba5320167 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -197,8 +197,8 @@ class Ticket { ON ( thread.object_id = ticket.ticket_id AND thread.object_type="T" ) ' .' LEFT JOIN '.THREAD_ENTRY_TABLE.' entry ON ( entry.thread_id = thread.id ) ' - .' LEFT JOIN '.ATTACHMENT_TABLE.' attach - ON ( attach.object_id = entry.id AND attach.`type` = "H") ' + .' LEFT JOIN '.THREAD_ENTRY_ATTACHMENT_TABLE.' attach + ON ( attach.thread_entry_id = entry.id ) ' .' WHERE ticket.ticket_id='.db_input($id) .' GROUP BY ticket.ticket_id'; diff --git a/include/upgrader/streams/core.sig b/include/upgrader/streams/core.sig index 252eb31ba78793be662ab22a616f2973246af4b4..90b6d124264702bc778822fabde3b0ffcf306508 100644 --- a/include/upgrader/streams/core.sig +++ b/include/upgrader/streams/core.sig @@ -1 +1 @@ -4b4daf9cf5e199673885f5ef58e743d1 +186868f53bf747f3d04a5ad701101690 diff --git a/include/upgrader/streams/core/b26f29a6-186868f5.cleanup.sql b/include/upgrader/streams/core/b26f29a6-186868f5.cleanup.sql new file mode 100644 index 0000000000000000000000000000000000000000..27dca7f84160f3916b8e4bbb968af9622ae89979 --- /dev/null +++ b/include/upgrader/streams/core/b26f29a6-186868f5.cleanup.sql @@ -0,0 +1,9 @@ +ALTER TABLE `%TABLE_PREFIX%thread` + DROP COLUMN `tid`; + +ALTER TABLE `%TABLE_PREFIX%thread_entry` + DROP COLUMN `ticket_id`; + +OPTIMIZE TABLE `%TABLE_PREFIX%ticket`; +OPTIMIZE TABLE `%TABLE_PREFIX%thread`; +OPTIMIZE TABLE `%TABLE_PREFIX%thread_entry`; diff --git a/include/upgrader/streams/core/b26f29a6-186868f5.patch.sql b/include/upgrader/streams/core/b26f29a6-186868f5.patch.sql new file mode 100644 index 0000000000000000000000000000000000000000..7f1898fc6b35b62f35c6af30193337950f81d94f --- /dev/null +++ b/include/upgrader/streams/core/b26f29a6-186868f5.patch.sql @@ -0,0 +1,80 @@ +/** + * @version v1.9.5 + * @signature 2257f6f22ca4b31bea6045b8b7d59d56 + * @title Threads revisited + * + * This patch adds ability to thread anything + * + */ + +-- Add thread table +DROP TABLE IF EXISTS `%TABLE_PREFIX%thread`; +CREATE TABLE IF NOT EXISTS `%TABLE_PREFIX%thread` ( + `id` int(11) unsigned NOT NULL AUTO_INCREMENT, + `object_id` int(11) unsigned NOT NULL, + `object_type` char(1) NOT NULL, + `extra` text, + `created` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `object_id` (`object_id`), + KEY `object_type` (`object_type`) +) DEFAULT CHARSET=utf8; + +-- create threads +INSERT INTO `%TABLE_PREFIX%thread` + (`object_id`, `object_type`, `created`) + SELECT t1.ticket_id, 'T', t1.created + FROM `%TABLE_PREFIX%ticket_thread` t1 + JOIN ( + SELECT ticket_id, MIN(id) as id + FROM `%TABLE_PREFIX%ticket_thread` + WHERE `thread_type` = 'M' + GROUP BY ticket_id + ) t2 + ON (t1.ticket_id=t2.ticket_id and t1.id=t2.id) + ORDER BY t1.created; + +ALTER TABLE `%TABLE_PREFIX%ticket_thread` + ADD `thread_id` INT( 11 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `pid` , + ADD INDEX ( `thread_id` ); + +UPDATE `%TABLE_PREFIX%ticket_thread` t1 + LEFT JOIN `%TABLE_PREFIX%thread` t2 ON ( t2.object_id = t1.ticket_id ) + SET t1.thread_id = t2.id; + +-- convert ticket_thread to thread_entry +ALTER TABLE `%TABLE_PREFIX%ticket_thread` + CHANGE `thread_type` `type` CHAR( 1 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + ADD INDEX ( `type` ); + +RENAME TABLE `%TABLE_PREFIX%ticket_thread` TO `%TABLE_PREFIX%thread_entry` ; + +-- add thread id to ticket table +ALTER TABLE `%TABLE_PREFIX%ticket` + ADD `thread_id` INT( 11 ) UNSIGNED NOT NULL DEFAULT '0' AFTER `number` , + ADD INDEX ( `thread_id` ); + +UPDATE `%TABLE_PREFIX%ticket` t1 + LEFT JOIN `%TABLE_PREFIX%thread` t2 ON ( t2.object_id = t1.ticket_id ) + SET t1.thread_id = t2.id; + +-- convert ticket_attachment to thread_entry_attachment +ALTER TABLE `%TABLE_PREFIX%ticket_attachment` + CHANGE `attach_id` `id` INT( 11 ) UNSIGNED NOT NULL AUTO_INCREMENT, + CHANGE `ref_id` `thread_entry_id` INT( 11 ) UNSIGNED NOT NULL DEFAULT '0'; + DROP `ticket_id`; + +RENAME TABLE `%TABLE_PREFIX%ticket_attachment` TO `%TABLE_PREFIX%thread_entry_attachment`; + +-- convert ticket_email_info to thread_entry_mid +ALTER TABLE `%TABLE_PREFIX%ticket_email_info` + CHANGE `thread_id` `thread_entry_id` INT( 11 ) UNSIGNED NOT NULL, + CHANGE `email_mid` `mid` VARCHAR( 255 ) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL, + ADD INDEX ( `thread_entry_id` ); + +RENAME TABLE `%TABLE_PREFIX%ticket_email_info` TO `%TABLE_PREFIX%thread_entry_email`; + +-- Set new schema signature +UPDATE `%TABLE_PREFIX%config` + SET `value` = '2257f6f22ca4b31bea6045b8b7d59d56' + WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql index d91ce79ab86f5fed2f4d628a84c9a2b5ca1ec40a..7f5fcedbbb1b1c92a29faab10e1374e372face0f 100644 --- a/setup/inc/streams/core/install-mysql.sql +++ b/setup/inc/streams/core/install-mysql.sql @@ -646,6 +646,18 @@ CREATE TABLE `%TABLE_PREFIX%thread_entry` ( KEY `type` (`type`) ) DEFAULT CHARSET=utf8; +DROP TABLE IF EXISTS `%TABLE_PREFIX%thread_entry_attachment`; +CREATE TABLE `%TABLE_PREFIX%thread_entry_attachment` ( + `id` int(11) unsigned NOT NULL auto_increment, + `file_id` int(10) unsigned NOT NULL default '0', + `thread_entry_id` int(11) unsigned NOT NULL default '0', + `inline` tinyint(1) NOT NULL default '0', + `created` datetime NOT NULL, + PRIMARY KEY (`id`), + KEY `file_id` (`file_id`), + KEY `ref_id` (`thread_entry_id`) +) DEFAULT CHARSET=utf8; + DROP TABLE IF EXISTS `%TABLE_PREFIX%thread_entry_email`; CREATE TABLE `%TABLE_PREFIX%thread_entry_email` ( `id` int(11) unsigned NOT NULL auto_increment,