diff --git a/include/class.faq.php b/include/class.faq.php index 83b4f8053072f1ae30e599ef317e6dae4f15fc9e..a9d0a5349371a05fa70fe1d94d40c6bfc84f7ace 100644 --- a/include/class.faq.php +++ b/include/class.faq.php @@ -63,27 +63,22 @@ class FAQ extends VerySimpleModel { function getCategory() { return $this->category; } function getHelpTopicsIds() { - if ($topics=$this->getHelpTopics()) { - return array_keys($topics); - } - return array(); + $ids = array(); + foreach ($this->getHelpTopics() as $topic) + $ids[] = $topic->getId(); + return $ids; } function getHelpTopics() { //XXX: change it to obj (when needed)! if (!isset($this->topics)) { - $this->topics = array(); - $sql='SELECT t.topic_id, CONCAT_WS(" / ", pt.topic, t.topic) as name FROM '.TOPIC_TABLE.' t ' - .' INNER JOIN '.FAQ_TOPIC_TABLE.' ft ON(ft.topic_id=t.topic_id AND ft.faq_id='.db_input($this->getId()).') ' - .' LEFT JOIN '.TOPIC_TABLE.' pt ON(pt.topic_id=t.topic_pid) ' - .' ORDER BY t.topic'; - if (($res=db_query($sql)) && db_num_rows($res)) { - while(list($id,$name) = db_fetch_row($res)) - $this->topics[$id]=$name; - } + $this->topics = Topic::objects()->filter(array( + 'topic_id__in' => FaqTopic::objects()->filter(array( + 'faq_id' => $this->getId(), + ))->values('topic_id'), + )); } - return $this->topics; } @@ -384,4 +379,24 @@ class FAQ extends VerySimpleModel { } } FAQ::_inspect(); + +class FaqTopic extends VerySimpleModel { + + static $meta = array( + 'table' => FAQ_TOPIC_TABLE, + 'pk' => array('faq_id', 'topic_id'), + 'joins' => array( + 'faq' => array( + 'constraint' => array( + 'faq_id' => 'FAQ.faq_id', + ), + ), + 'topic' => array( + 'constraint' => array( + 'topic_id' => 'Topic.topic_id', + ), + ), + ), + ); +} ?> diff --git a/include/class.i18n.php b/include/class.i18n.php index bfdfdfbc3cbbcfca5eaeb6bb6236f3b377223cc9..317c851cf5480c42789ebac1aeb0228316309fc5 100644 --- a/include/class.i18n.php +++ b/include/class.i18n.php @@ -55,7 +55,7 @@ class Internationalization { 'list.yaml' => 'DynamicList::create', // Note that department, sla, and forms are required for // help_topic - 'help_topic.yaml' => 'Topic::create', + 'help_topic.yaml' => 'Topic::__create', 'filter.yaml' => 'Filter::create', 'team.yaml' => 'Team::create', // Organization diff --git a/include/class.orm.php b/include/class.orm.php index 3ae84a2b2cf43582f6de7a983736fa54414d7ddd..07f6131041c0f69f715c9b95e24fd6fefc4b8184 100644 --- a/include/class.orm.php +++ b/include/class.orm.php @@ -132,6 +132,7 @@ class VerySimpleModel { } function __onload() {} + static function __oninspect() {} static function _inspect() { if (!static::$meta['table']) @@ -163,6 +164,9 @@ class VerySimpleModel { $j['fkey'] = explode('.', $foreign); $j['local'] = $keys[0]; } + + // Let the model participate + static::__oninspect(); } static function objects() { diff --git a/include/class.topic.php b/include/class.topic.php index 6cac47112efeb2dd41d1ac8a979c6e445fce98a6..ae84b36ffad113186d14427040260a046c93f1ad 100644 --- a/include/class.topic.php +++ b/include/class.topic.php @@ -16,12 +16,22 @@ require_once INCLUDE_DIR . 'class.sequence.php'; -class Topic { - var $id; +class Topic extends VerySimpleModel { + + static $meta = array( + 'table' => TOPIC_TABLE, + 'pk' => array('topic_id'), + 'ordering' => array('topic'), + 'joins' => array( + 'parent' => array( + 'list' => false, + 'constraint' => array( + 'topic_pid' => 'Topic.topic_id', + ), + ), + ), + ); - var $ht; - - var $parent; var $page; var $form; @@ -31,39 +41,13 @@ class Topic { const FLAG_CUSTOM_NUMBERS = 0x0001; - function Topic($id) { - $this->id=0; - $this->load($id); - } - - function load($id=0) { + static function __oninspect() { global $cfg; - if(!$id && !($id=$this->getId())) - return false; - - $sql='SELECT ht.* ' - .' FROM '.TOPIC_TABLE.' ht ' - .' WHERE ht.topic_id='.db_input($id); - - if(!($res=db_query($sql)) || !db_num_rows($res)) - return false; - - $this->ht = db_fetch_array($res); - $this->id = $this->ht['topic_id']; - - $this->page = $this->form = null; - // Handle upgrade case where sort has not yet been defined if (!$this->ht['sort'] && $cfg->getTopicSortMode() == 'a') { static::updateSortOrder(); } - - return true; - } - - function reload() { - return $this->load(); } function asVar() { @@ -71,22 +55,23 @@ class Topic { } function getId() { - return $this->id; + return $this->topic_id; } function getPid() { - return $this->ht['topic_pid']; + return $this->topic_pid; } function getParent() { - if(!$this->parent && $this->getPid()) - $this->parent = self::lookup($this->getPid()); - return $this->parent; } function getName() { - return $this->ht['topic']; + return $this->topic; + } + + function getLocalName() { + return $this->getLocal('name'); } function getFullName() { @@ -99,31 +84,31 @@ class Topic { } function getDeptId() { - return $this->ht['dept_id']; + return $this->dept_id; } function getSLAId() { - return $this->ht['sla_id']; + return $this->sla_id; } function getPriorityId() { - return $this->ht['priority_id']; + return $this->priority_id; } function getStatusId() { - return $this->ht['status_id']; + return $this->status_id; } function getStaffId() { - return $this->ht['staff_id']; + return $this->staff_id; } function getTeamId() { - return $this->ht['team_id']; + return $this->team_id; } function getPageId() { - return $this->ht['page_id']; + return $this->page_id; } function getPage() { @@ -134,7 +119,7 @@ class Topic { } function getFormId() { - return $this->ht['form_id']; + return $this->form_id; } function getForm() { @@ -149,7 +134,7 @@ class Topic { } function autoRespond() { - return (!$this->ht['noautoresp']); + return !$this->noautoresp; } function isEnabled() { @@ -170,7 +155,7 @@ class Topic { * there is a loop in the ancestry */ function isActive(array $chain=array()) { - if (!$this->ht['isactive']) + if (!$this->isactive) return false; if (!isset($chain[$this->getId()]) && ($p = $this->getParent())) { @@ -178,12 +163,12 @@ class Topic { return $p->isActive($chain); } else { - return $this->ht['isactive']; + return $this->isactive; } } function isPublic() { - return ($this->ht['ispublic']); + return ($this->ispublic); } function getHashtable() { @@ -197,7 +182,7 @@ class Topic { } function hasFlag($flag) { - return $this->ht['flags'] & $flag != 0; + return $this->flags & $flag != 0; } function getNewTicketNumber() { @@ -206,17 +191,17 @@ class Topic { if (!$this->hasFlag(self::FLAG_CUSTOM_NUMBERS)) return $cfg->getNewTicketNumber(); - if ($this->ht['sequence_id']) - $sequence = Sequence::lookup($this->ht['sequence_id']); + if ($this->sequence_id) + $sequence = Sequence::lookup($this->sequence_id); if (!$sequence) $sequence = new RandomSequence(); - return $sequence->next($this->ht['number_format'] ?: '######', + return $sequence->next($this->number_format ?: '######', array('Ticket', 'isTicketNumberUnique')); } function getTranslateTag($subtag) { - return _H(sprintf('topic.%s.%s', $subtag, $this->id)); + return _H(sprintf('topic.%s.%s', $subtag, $this->getId())); } function getLocal($subtag) { $tag = $this->getTranslateTag($subtag); @@ -225,42 +210,47 @@ class Topic { } function setSortOrder($i) { - if ($i != $this->ht['sort']) { - $sql = 'UPDATE '.TOPIC_TABLE.' SET `sort`='.db_input($i) - .' WHERE `topic_id`='.db_input($this->getId()); - return (db_query($sql) && db_affected_rows() == 1); + if ($i != $this->sort) { + $this->sort = $i; + return $this->save(); } // Noop return true; } - function update($vars, &$errors) { - - if(!$this->save($this->getId(), $vars, $errors)) - return false; - - $this->reload(); - return true; - } - function delete() { global $cfg; if ($this->getId() == $cfg->getDefaultTopicId()) return false; - $sql='DELETE FROM '.TOPIC_TABLE.' WHERE topic_id='.db_input($this->getId()).' LIMIT 1'; - if(db_query($sql) && ($num=db_affected_rows())) { - db_query('UPDATE '.TOPIC_TABLE.' SET topic_pid=0 WHERE topic_pid='.db_input($this->getId())); + if (parent::delete()) { + self::objects()->filter(array( + 'topic_pid' => $this->getId() + ))->update(array( + 'topic_pid' => 0 + )); + FaqTopic::objects()->filter(array( + 'topic_id' => $this->getId() + ))->delete(); db_query('UPDATE '.TICKET_TABLE.' SET topic_id=0 WHERE topic_id='.db_input($this->getId())); - db_query('DELETE FROM '.FAQ_TOPIC_TABLE.' WHERE topic_id='.db_input($this->getId())); } - return $num; + return true; } + /*** Static functions ***/ - function create($vars, &$errors) { - return self::save(0, $vars, $errors); + + static function create($vars=array()) { + $topic = parent::create($vars); + $topic->created = SqlFunction::NOW(); + return $topic; + } + + static function __create($vars, &$errors) { + $topic = self::create(); + $topic->save($vars, $errors); + return $topic; } static function getHelpTopics($publicOnly=false, $disabled=false, $localize=true) { @@ -269,15 +259,18 @@ class Topic { // If localization is specifically requested, then rebuild the list. if (!$names || $localize) { - $sql = 'SELECT topic_id, topic_pid, ispublic, isactive, topic FROM '.TOPIC_TABLE - . ' ORDER BY `sort`'; - $res = db_query($sql); + $objects = self::objects()->values_flat( + 'topic_id', 'topic_pid', 'ispublic', 'isactive', 'topic' + ) + ->order_by('sort'); // Fetch information for all topics, in declared sort order $topics = array(); - while (list($id, $pid, $pub, $act, $topic) = db_fetch_row($res)) + foreach ($objects as $T) { + list($id, $pid, $pub, $act, $topic) = $T; $topics[$id] = array('pid'=>$pid, 'public'=>$pub, 'disabled'=>!$act, 'topic'=>$topic); + } $localize_this = function($id, $default) use ($localize) { if (!$localize) @@ -329,42 +322,38 @@ class Topic { return $requested_names; } - function getPublicHelpTopics() { + static function getPublicHelpTopics() { return self::getHelpTopics(true); } - function getAllHelpTopics($localize=false) { + static function getAllHelpTopics($localize=false) { return self::getHelpTopics(false, true, $localize); } - function getIdByName($name, $pid=0) { - - $sql='SELECT topic_id FROM '.TOPIC_TABLE - .' WHERE topic='.db_input($name) - .' AND topic_pid='.db_input($pid); - if(($res=db_query($sql)) && db_num_rows($res)) - list($id) = db_fetch_row($res); + static function getIdByName($name, $pid=0) { + $list = self::objects()->filter(array( + 'topic'=>$name, + 'topic_pid'=>$pid, + ))->values_flat('topic_id')->one(); - return $id; + if ($list) + return $list[0]; } - static function lookup($id) { - return ($id && is_numeric($id) && ($t= new Topic($id)) && $t->getId()==$id)?$t:null; - } - - function save($id, $vars, &$errors) { + function update($vars, &$errors) { global $cfg; - $vars['topic']=Format::striptags(trim($vars['topic'])); + $vars['topic'] = Format::striptags(trim($vars['topic'])); - if($id && $id!=$vars['id']) + if (isset($this->topic_id) && $this->getId() != $vars['id']) $errors['err']=__('Internal error occurred'); - if(!$vars['topic']) + if (!$vars['topic']) $errors['topic']=__('Help topic name is required'); - elseif(strlen($vars['topic'])<5) + elseif (strlen($vars['topic'])<5) $errors['topic']=__('Topic is too short. Five characters minimum'); - elseif(($tid=self::getIdByName($vars['topic'], $vars['topic_pid'])) && $tid!=$id) + elseif (($tid=self::getIdByName($vars['topic'], $vars['topic_pid'])) + && $tid!=$this->getId()) $errors['topic']=__('Topic already exists'); if (!is_numeric($vars['dept_id'])) @@ -374,60 +363,53 @@ class Topic { $errors['number_format'] = 'Ticket number format requires at least one hash character (#)'; - if($errors) return false; - - foreach (array('sla_id','form_id','page_id','topic_pid') as $f) - if (!isset($vars[$f])) - $vars[$f] = 0; - - $sql=' updated=NOW() ' - .',topic='.db_input($vars['topic']) - .',topic_pid='.db_input($vars['topic_pid']) - .',dept_id='.db_input($vars['dept_id']) - .',priority_id='.db_input($vars['priority_id']) - .',status_id='.db_input($vars['status_id']) - .',sla_id='.db_input($vars['sla_id']) - .',form_id='.db_input($vars['form_id']) - .',page_id='.db_input($vars['page_id']) - .',isactive='.db_input($vars['isactive']) - .',ispublic='.db_input($vars['ispublic']) - .',sequence_id='.db_input($vars['custom-numbers'] ? $vars['sequence_id'] : 0) - .',number_format='.db_input($vars['custom-numbers'] ? $vars['number_format'] : '') - .',flags='.db_input($vars['custom-numbers'] ? self::FLAG_CUSTOM_NUMBERS : 0) - .',noautoresp='.db_input(isset($vars['noautoresp']) && $vars['noautoresp']?1:0) - .',notes='.db_input(Format::sanitize($vars['notes'])); + if ($errors) + return false; + + $this->topic = $vars['topic']; + $this->topic_pid = $vars['topic_pid'] ?: 0; + $this->dept_id = $vars['dept_id']; + $this->priority_id = $vars['priority_id'] ?: 0; + $this->status_id = $vars['status_id'] ?: 0; + $this->sla_id = $vars['sla_id'] ?: 0; + $this->form_id = $vars['form_id'] ?: 0; + $this->page_id = $vars['page_id'] ?: 0; + $this->isactive = !!$vars['isactive']; + $this->ispublic = !!$vars['ispublic']; + $this->sequence_id = $vars['custom-numbers'] ? $vars['sequence_id'] : 0; + $this->number_format = $vars['custom-numbers'] ? $vars['number_format'] : ''; + $this->flags = $vars['custom-numbers'] ? self::FLAG_CUSTOM_NUMBERS : 0; + $this->noautoresp = !!$vars['noautoresp']; + $this->notes = Format::sanitize($vars['notes']); //Auto assign ID is overloaded... - if($vars['assign'] && $vars['assign'][0]=='s') - $sql.=',team_id=0, staff_id='.db_input(preg_replace("/[^0-9]/", "", $vars['assign'])); - elseif($vars['assign'] && $vars['assign'][0]=='t') - $sql.=',staff_id=0, team_id='.db_input(preg_replace("/[^0-9]/", "", $vars['assign'])); - else - $sql.=',staff_id=0, team_id=0 '; //no auto-assignment! + if ($vars['assign'] && $vars['assign'][0] == 's') { + $this->team_id = 0; + $this->staff_id = preg_replace("/[^0-9]/", "", $vars['assign']); + } + elseif ($vars['assign'] && $vars['assign'][0] == 't') { + $this->staff_id = 0; + $this->team_id = preg_replace("/[^0-9]/", "", $vars['assign']); + } + else { + $this->staff_id = 0; + $this->team_id = 0; + } $rv = false; - if ($id) { - $sql='UPDATE '.TOPIC_TABLE.' SET '.$sql.' WHERE topic_id='.db_input($id); - if (!($rv = db_query($sql))) - $errors['err']=sprintf(__('Unable to update %s.'), __('this help topic')) - .' '.__('Internal error occurred'); - } else { - if (isset($vars['topic_id'])) - $sql .= ', topic_id='.db_input($vars['topic_id']); - // If in manual sort mode, place the new item directly below the - // parent item - if ($vars['topic_pid'] && $cfg && $cfg->getTopicSortMode() != 'a') { - $sql .= ', `sort`='.db_input( - db_result(db_query('SELECT COALESCE(`sort`,0)+1 FROM '.TOPIC_TABLE - .' WHERE `topic_id`='.db_input($vars['topic_pid'])))); + if ($this->__new__) { + if (isset($this->topic_pid) + && ($parent = Topic::lookup($this->topic_pid))) { + $this->sort = ($parent->sort ?: 0) + 1; } - - $sql='INSERT INTO '.TOPIC_TABLE.' SET '.$sql.',created=NOW()'; - if (db_query($sql) && ($id = db_insert_id())) - $rv = $id; - else + if (!($rv = $this->save())) { $errors['err']=sprintf(__('Unable to create %s.'), __('this help topic')) .' '.__('Internal error occurred'); + } + } + elseif (!($rv = $this->save())) { + $errors['err']=sprintf(__('Unable to update %s.'), __('this help topic')) + .' '.__('Internal error occurred'); } if (!$cfg || $cfg->getTopicSortMode() == 'a') { static::updateSortOrder(); @@ -435,6 +417,12 @@ class Topic { return $rv; } + function save($refetch=false) { + if ($this->dirty) + $this->updated = SqlFunction::NOW(); + return parent::save($refetch || $this->dirty); + } + static function updateSortOrder() { global $cfg; diff --git a/scp/helptopics.php b/scp/helptopics.php index 44af9f9c9bb9fa233e6259837b845d8166ad814d..c447675fda8d796d8fe4528c8e2b77f9a09c3154 100644 --- a/scp/helptopics.php +++ b/scp/helptopics.php @@ -35,7 +35,8 @@ if($_POST){ } break; case 'create': - if(($id=Topic::create($_POST,$errors))){ + $topic = Topic::create(); + if ($topic->update($_POST, $errors)) { $msg=sprintf(__('Successfully added %s'), Format::htmlchars($_POST['topic'])); $_REQUEST['a']=null; }elseif(!$errors['err']){ @@ -58,10 +59,13 @@ if($_POST){ switch(strtolower($_POST['a'])) { case 'enable': - $sql='UPDATE '.TOPIC_TABLE.' SET isactive=1 ' - .' WHERE topic_id IN ('.implode(',', db_input($_POST['ids'])).')'; + $num = Topic::objects()->filter(array( + 'topic_id__in' => $_POST['ids'], + ))->update(array( + 'isactive' => true, + )); - if(db_query($sql) && ($num=db_affected_rows())) { + if ($num > 0) { if($num==$count) $msg = sprintf(__('Successfully enabled %s'), _N('selected help topic', 'selected help topics', $count)); @@ -74,10 +78,14 @@ if($_POST){ } break; case 'disable': - $sql='UPDATE '.TOPIC_TABLE.' SET isactive=0 ' - .' WHERE topic_id IN ('.implode(',', db_input($_POST['ids'])).')' - .' AND topic_id <> '.db_input($cfg->getDefaultTopicId()); - if(db_query($sql) && ($num=db_affected_rows())) { + $num = Topic::objects()->filter(array( + 'topic_id__in'=>$_POST['ids'], + ))->exclude(array( + 'topic_id'=>$cfg->getDefaultTopicId(), + ))->update(array( + 'isactive' => false, + )); + if ($num > 0) { if($num==$count) $msg = sprintf(__('Successfully diabled %s'), _N('selected help topic', 'selected help topics', $count)); @@ -90,11 +98,9 @@ if($_POST){ } break; case 'delete': - $i=0; - foreach($_POST['ids'] as $k=>$v) { - if(($t=Topic::lookup($v)) && $t->delete()) - $i++; - } + $i = Topic::objects()->filter(array( + 'topic_id__in'=>$_POST['ids'] + ))->delete(); if($i && $i==$count) $msg = sprintf(__('Successfully deleted %s'),