diff --git a/include/ajax.forms.php b/include/ajax.forms.php index b6e5d2903b3ea7a7f41afbb3c666a3656a550e05..9a026e83c5861f5621678dcf0864f008efd44e3a 100644 --- a/include/ajax.forms.php +++ b/include/ajax.forms.php @@ -14,10 +14,17 @@ class DynamicFormsAjaxAPI extends AjaxController { } function getFormsForHelpTopic($topic_id, $client=false) { - $topic = Topic::lookup($topic_id); - if ($topic->ht['form_id'] - && ($form = DynamicForm::lookup($topic->ht['form_id']))) - $form->render(!$client); + if (!($topic = Topic::lookup($topic_id))) + Http::response(404, 'No such help topic'); + + if ($_GET || isset($_SESSION[':form-data'])) { + if (!is_array($_SESSION[':form-data'])) + $_SESSION[':form-data'] = array(); + $_SESSION[':form-data'] = array_merge($_SESSION[':form-data'], $_GET); + } + + if ($form = $topic->getForm()) + $form->getForm($_SESSION[':form-data'])->render(!$client); } function getClientFormsForHelpTopic($topic_id) { diff --git a/include/class.config.php b/include/class.config.php index b0373f6e9d9679b2ec93e8b8179efbdf2b2167fc..2cdce1351937f1838b0710e43b72b42574c7c522 100644 --- a/include/class.config.php +++ b/include/class.config.php @@ -165,6 +165,8 @@ class OsticketConfig extends Config { 'clients_only' => false, 'client_registration' => 'closed', 'accept_unregistered_email' => true, + 'default_help_topic' => 0, + 'help_topic_sort_mode' => 'a', ); function OsticketConfig($section=null) { @@ -426,6 +428,34 @@ class OsticketConfig extends Config { return $this->defaultPriority; } + function getDefaultTopicId() { + return $this->get('default_help_topic'); + } + + function getDefaultTopic() { + return Topic::lookup($this->getDefaultTopicId()); + } + + function getTopicSortMode() { + return $this->get('help_topic_sort_mode'); + } + + function setTopicSortMode($mode) { + $modes = static::allTopicSortModes(); + if (!isset($modes[$mode])) + throw new InvalidArgumentException($mode + .': Unsupport help topic sort mode'); + + $this->update('help_topic_sort_mode', $mode); + } + + static function allTopicSortModes() { + return array( + 'a' => 'Alphabetically', + 'm' => 'Manually', + ); + } + function getDefaultTemplateId() { return $this->get('default_template_id'); } @@ -901,7 +931,7 @@ class OsticketConfig extends Config { if($vars['enable_captcha']) { if (!extension_loaded('gd')) - $errors['enable_captcha']='The GD extension required'; + $errors['enable_captcha']='The GD extension is required'; elseif(!function_exists('imagepng')) $errors['enable_captcha']='PNG support required for Image Captcha'; } @@ -927,7 +957,11 @@ class OsticketConfig extends Config { $errors['max_staff_file_uploads']='Invalid selection. Must be less than '.$maxfileuploads; } - + if ($vars['default_help_topic'] + && ($T = Topic::lookup($vars['default_help_topic'])) + && !$T->isActive()) { + $errors['default_help_topic'] = 'Default help topic must be set to active'; + } if(!Validator::process($f, $vars, $errors) || $errors) return false; @@ -938,6 +972,7 @@ class OsticketConfig extends Config { return $this->updateAll(array( 'random_ticket_ids'=>$vars['random_ticket_ids'], 'default_priority_id'=>$vars['default_priority_id'], + 'default_help_topic'=>$vars['default_help_topic'], 'default_sla_id'=>$vars['default_sla_id'], 'max_open_tickets'=>$vars['max_open_tickets'], 'autolock_minutes'=>$vars['autolock_minutes'], diff --git a/include/class.email.php b/include/class.email.php index aa6facef4bdc65069d159e27aaca430160a54c1f..79b307dca3edaebedfcbdb8d63e68c31ed90e7b8 100644 --- a/include/class.email.php +++ b/include/class.email.php @@ -86,6 +86,16 @@ class Email { return $this->dept; } + function getTopicId() { + return $this->ht['topic_id']; + } + + function getTopic() { + // Topic::lookup will do validation on the ID, no need to duplicate + // code here + return Topic::lookup($this->getTopicId()); + } + function autoRespond() { return (!$this->ht['noautoresp']); } diff --git a/include/class.ticket.php b/include/class.ticket.php index c3f49246957ab049d5cc504b7f9e5abb94fb3bd8..b46f151b001ad30366fb5be4686ae694e3d6edb9 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -248,7 +248,7 @@ class Ticket { function getHelpTopic() { if(!$this->ht['helptopic'] && ($topic=$this->getTopic())) - $this->ht['helptopic'] = $topic->getName(); + $this->ht['helptopic'] = $topic->getFullName(); return $this->ht['helptopic']; } @@ -2403,6 +2403,11 @@ class Ticket { $form->setAnswer('priority', null, $email->getPriorityId()); if ($autorespond) $autorespond = $email->autoRespond(); + if (!isset($topic) + && ($T = $email->getTopic()) + && ($T->isActive())) { + $topic = $T; + } $email = null; $source = 'Email'; } @@ -2416,6 +2421,11 @@ class Ticket { $vars['teamId'] = substr($code, 1); } + if (!isset($topic)) { + // This may return NULL, no big deal + $topic = $cfg->getDefaultTopic(); + } + // Intenal mapping magic...see if we need to override anything if (isset($topic)) { $deptId = $deptId ?: $topic->getDeptId(); diff --git a/include/class.topic.php b/include/class.topic.php index ad3b21a416f147ba443d2417c786f1bb7c6299b2..c415b157aa1e0cf063ae7d912cd2905b1e90dfd9 100644 --- a/include/class.topic.php +++ b/include/class.topic.php @@ -23,20 +23,23 @@ class Topic { var $page; var $form; + const DISPLAY_DISABLED = 2; + + const FORM_USE_PARENT = 4294967295; + function Topic($id) { $this->id=0; $this->load($id); } function load($id=0) { + global $cfg; if(!$id && !($id=$this->getId())) return false; $sql='SELECT ht.* ' - .', IF(ht.topic_pid IS NULL, ht.topic, CONCAT_WS(" / ", ht2.topic, ht.topic)) as name ' .' FROM '.TOPIC_TABLE.' ht ' - .' LEFT JOIN '.TOPIC_TABLE.' ht2 ON(ht2.topic_id=ht.topic_pid) ' .' WHERE ht.topic_id='.db_input($id); if(!($res=db_query($sql)) || !db_num_rows($res)) @@ -47,6 +50,10 @@ class Topic { $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; } @@ -75,7 +82,16 @@ class Topic { } function getName() { - return $this->ht['name']; + return $this->ht['topic']; + } + + function getFullName() { + return self::getTopicName($this->getId()); + } + + static function getTopicName($id) { + $names = static::getHelpTopics(false, true); + return $names[$id]; } function getDeptId() { @@ -114,8 +130,12 @@ class Topic { } function getForm() { - if(!$this->form && $this->getFormId()) - $this->form = DynamicForm::lookup($this->getFormId()); + $id = $this->getFormId(); + + if ($id == self::FORM_USE_PARENT && ($p = $this->getParent())) + $this->form = $p->getForm(); + elseif ($id && !$this->form) + $this->form = DynamicForm::lookup($id); return $this->form; } @@ -125,11 +145,33 @@ class Topic { } function isEnabled() { - return ($this->ht['isactive']); + return $this->isActive(); } - function isActive() { - return $this->isEnabled(); + /** + * Determine if the help topic is currently enabled. The ancestry of + * this topic will be considered to see if any of the parents are + * disabled. If any are disabled, then this topic will be considered + * disabled. + * + * Parameters: + * $chain - array<id:bool> recusion chain used to detect loops. The + * chain should be maintained and passed to a parent's ::isActive() + * method. When consulting a parent, if the local topic ID is a key + * in the chain, then this topic has already been considered, and + * there is a loop in the ancestry + */ + function isActive(array $chain=array()) { + if (!$this->ht['isactive']) + return false; + + if (!isset($chain[$this->getId()]) && ($p = $this->getParent())) { + $chain[$this->getId()] = true; + return $p->isActive($chain); + } + else { + return $this->ht['isactive']; + } } function isPublic() { @@ -144,6 +186,16 @@ class Topic { return $this->getHashtable(); } + 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); + } + // Noop + return true; + } + function update($vars, &$errors) { if(!$this->save($this->getId(), $vars, $errors)) @@ -154,6 +206,10 @@ class Topic { } 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())) { @@ -169,29 +225,64 @@ class Topic { return self::save(0, $vars, $errors); } - function getHelpTopics($publicOnly=false) { - - $topics=array(); - $sql='SELECT ht.topic_id, CONCAT_WS(" / ", ht2.topic, ht.topic) as name ' - .' FROM '.TOPIC_TABLE. ' ht ' - .' LEFT JOIN '.TOPIC_TABLE.' ht2 ON(ht2.topic_id=ht.topic_pid) ' - .' WHERE ht.isactive=1'; - - if($publicOnly) - $sql.=' AND ht.ispublic=1'; + static function getHelpTopics($publicOnly=false, $disabled=false) { + global $cfg; + static $topics, $names; + + if (!$names) { + $sql = 'SELECT topic_id, topic_pid, ispublic, isactive, topic FROM '.TOPIC_TABLE + . ' ORDER BY `sort`'; + $res = db_query($sql); + + // Fetch information for all topics, in declared sort order + $topics = array(); + while (list($id, $pid, $pub, $act, $topic) = db_fetch_row($res)) + $topics[$id] = array('pid'=>$pid, 'public'=>$pub, + 'disabled'=>!$act, 'topic'=>$topic); + + // Resolve parent names + foreach ($topics as $id=>$info) { + $name = $info['topic']; + $loop = array($id=>true); + $parent = false; + while ($info['pid'] && ($info = $topics[$info['pid']])) { + $name = sprintf('%s / %s', $info['topic'], $name); + if ($parent && $parent['disabled']) + // Cascade disabled flag + $topics[$id]['disabled'] = true; + if (isset($loop[$info['pid']])) + break; + $loop[$info['pid']] = true; + $parent = $info; + } + $names[$id] = $name; + } + } - $sql.=' ORDER BY name'; - if(($res=db_query($sql)) && db_num_rows($res)) - while(list($id, $name)=db_fetch_row($res)) - $topics[$id]=$name; + // Apply requested filters + $requested_names = array(); + foreach ($names as $id=>$n) { + $info = $topics[$id]; + if ($publicOnly && !$info['public']) + continue; + if (!$disabled && $info['disabled']) + continue; + if ($disabled === self::DISPLAY_DISABLED && $info['disabled']) + $n .= " — (disabled)"; + $requested_names[$id] = $n; + } - return $topics; + return $requested_names; } function getPublicHelpTopics() { return self::getHelpTopics(true); } + function getAllHelpTopics() { + return self::getHelpTopics(false, true); + } + function getIdByName($name, $pid=0) { $sql='SELECT topic_id FROM '.TOPIC_TABLE @@ -203,11 +294,12 @@ class Topic { return $id; } - function lookup($id) { + static function lookup($id) { return ($id && is_numeric($id) && ($t= new Topic($id)) && $t->getId()==$id)?$t:null; } function save($id, $vars, &$errors) { + global $cfg; $vars['topic']=Format::striptags(trim($vars['topic'])); @@ -218,7 +310,7 @@ class Topic { $errors['topic']='Help topic required'; elseif(strlen($vars['topic'])<5) $errors['topic']='Topic is too short. 5 chars minimum'; - elseif(($tid=self::getIdByName($vars['topic'], $vars['pid'])) && $tid!=$id) + elseif(($tid=self::getIdByName($vars['topic'], $vars['topic_pid'])) && $tid!=$id) $errors['topic']='Topic already exists'; if (!is_numeric($vars['dept_id'])) @@ -226,13 +318,13 @@ class Topic { if($errors) return false; - foreach (array('sla_id','form_id','page_id','pid') as $f) + 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['pid']) + .',topic_pid='.db_input($vars['topic_pid']) .',dept_id='.db_input($vars['dept_id']) .',priority_id='.db_input($vars['priority_id']) .',sla_id='.db_input($vars['sla_id']) @@ -251,23 +343,46 @@ class Topic { else $sql.=',staff_id=0, team_id=0 '; //no auto-assignment! - if($id) { + $rv = false; + if ($id) { $sql='UPDATE '.TOPIC_TABLE.' SET '.$sql.' WHERE topic_id='.db_input($id); - if(db_query($sql)) - return true; - - $errors['err']='Unable to update topic. Internal error occurred'; + if (!($rv = db_query($sql))) + $errors['err']='Unable to update topic. Internal error occurred'; } else { if (isset($vars['topic_id'])) $sql .= ', topic_id='.db_input($vars['topic_id']); - $sql='INSERT INTO '.TOPIC_TABLE.' SET '.$sql.',created=NOW()'; - if(db_query($sql) && ($id=db_insert_id())) - return $id; + if ($vars['topic_pid'] && $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'])))); + } - $errors['err']='Unable to create the topic. Internal error'; + $sql='INSERT INTO '.TOPIC_TABLE.' SET '.$sql.',created=NOW()'; + if (db_query($sql) && ($id = db_insert_id())) + $rv = $id; + else + $errors['err']='Unable to create the topic. Internal error'; } + if ($cfg->getTopicSortMode() == 'a') { + static::updateSortOrder(); + } + return $rv; + } - return false; + static function updateSortOrder() { + // Fetch (un)sorted names + $names = static::getHelpTopics(false, true); + uasort($names, function($a, $b) { return strcmp($a, $b); }); + + $update = array_keys($names); + foreach ($update as $idx=>&$id) { + $id = sprintf("(%s,%s)", db_input($id), db_input($idx+1)); + } + // Thanks, http://stackoverflow.com/a/3466 + $sql = sprintf('INSERT INTO `%s` (topic_id,`sort`) VALUES %s + ON DUPLICATE KEY UPDATE `sort`=VALUES(`sort`)', + TOPIC_TABLE, implode(',', $update)); + db_query($sql); } } diff --git a/include/client/open.inc.php b/include/client/open.inc.php index 26b6d30edf9ab857dd1fb5b25f63bcfbc5a22c3e..9cb1bb65739a4c3504ef701ae128378c943f91b0 100644 --- a/include/client/open.inc.php +++ b/include/client/open.inc.php @@ -10,6 +10,9 @@ if($thisclient && $thisclient->isValid()) { $info=($_POST && $errors)?Format::htmlchars($_POST):$info; $form = null; +if (!$info['topicId']) + $info['topicId'] = $cfg->getDefaultTopicId(); + if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) { $form = $topic->getForm(); if ($_POST && $form) { @@ -30,8 +33,9 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) { <td class="required">Help Topic:</td> <td> <select id="topicId" name="topicId" onchange="javascript: + var data = $(':input[name]', '#dynamic-form').serialize(); $('#dynamic-form').load( - 'ajax.php/form/help-topic/' + this.value); + 'ajax.php/form/help-topic/' + this.value, data); "> <option value="" selected="selected">— Select a Help Topic —</option> <?php diff --git a/include/staff/email.inc.php b/include/staff/email.inc.php index e8d3a430e74c3f66a40dc374c1974b2dd3a9fa6d..8f8e683121a80ca852c4b65073c899bbbbe658a2 100644 --- a/include/staff/email.inc.php +++ b/include/staff/email.inc.php @@ -127,16 +127,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <td> <span> <select name="topic_id"> - <option value="0" selected="selected">— None —</option> + <option value="0" selected="selected">— System Default —</option> <?php - $sql='SELECT topic_id, topic FROM '.TOPIC_TABLE.' T ORDER by topic'; - if(($res=db_query($sql)) && db_num_rows($res)){ - while(list($id,$name)=db_fetch_row($res)){ - $selected=($info['topic_id'] && $id==$info['topic_id'])?'selected="selected"':''; - echo sprintf('<option value="%d" %s>%s</option>',$id,$selected,$name); - } - } - ?> + $topics = Topic::getHelpTopics(); + while (list($id,$topic) = each($topics)) { ?> + <option value="<?php echo $id; ?>"<?php echo ($info['topic_id']==$id)?'selected':''; ?>><?php echo $topic; ?></option> + <?php + } ?> </select> <i class="help-tip icon-question-sign" href="#new_ticket_help_topic"></i> </span> diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php index 6e4317cb15d890de7732766139f9673f87cd0c9e..00f06d38d0ca604b2adf81daa606132110fd7359 100644 --- a/include/staff/helptopic.inc.php +++ b/include/staff/helptopic.inc.php @@ -16,6 +16,7 @@ if($topic && $_REQUEST['a']!='add') { $submit_text='Add Topic'; $info['isactive']=isset($info['isactive'])?$info['isactive']:1; $info['ispublic']=isset($info['ispublic'])?$info['ispublic']:1; + $info['form_id'] = Topic::FORM_USE_PARENT; $qstr.='&a='.$_REQUEST['a']; } $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); @@ -70,19 +71,15 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); Parent Topic: </td> <td> - <select name="pid"> - <option value="">— Select Parent Topic —</option> + <select name="topic_pid"> + <option value="">— Top-Level Topic —</option><?php + $topics = Topic::getAllHelpTopics(); + while (list($id,$topic) = each($topics)) { + if ($id == $info['topic_id']) + continue; ?> + <option value="<?php echo $id; ?>"<?php echo ($info['topic_pid']==$id)?'selected':''; ?>><?php echo $topic; ?></option> <?php - $sql='SELECT topic_id, topic FROM '.TOPIC_TABLE - .' WHERE topic_pid=0 ' - .' ORDER by topic'; - if(($res=db_query($sql)) && db_num_rows($res)) { - while(list($id, $name)=db_fetch_row($res)) { - echo sprintf('<option value="%d" %s>%s</option>', - $id, (($info['pid'] && $id==$info['pid'])?'selected="selected"':'') ,$name); - } - } - ?> + } ?> </select> <i class="help-tip icon-question-sign" href="#parent_topic"></i> <span class="error"> <?php echo $errors['pid']; ?></span> </td> @@ -92,9 +89,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <tr> <td><strong>Custom Form</strong>:</td> <td><select name="form_id"> - <option value="0">— No Extra Fields —</option> + <option value="0" <?php +if ($info['form_id'] == '0') echo 'selected="selected"'; + ?>>— None —</option> + <option value="<?php echo Topic::FORM_USE_PARENT; ?>" <?php +if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"'; + ?>>— Use Parent Form —</option> <?php foreach (DynamicForm::objects()->filter(array('type'=>'G')) as $group) { ?> - <option value="<?php echo $group->get('id'); ?>" + <option value="<?php echo $group->get('id'); ?>" <?php if ($group->get('id') == $info['form_id']) echo 'selected="selected"'; ?>> <?php echo $group->get('title'); ?> diff --git a/include/staff/helptopics.inc.php b/include/staff/helptopics.inc.php index 8395a29e3d723ed881405cfb8d06f96bbad728bc..04d3b5238189b00f1d49b7c1e2706cf7c703ac42 100644 --- a/include/staff/helptopics.inc.php +++ b/include/staff/helptopics.inc.php @@ -1,51 +1,35 @@ <?php if(!defined('OSTADMININC') || !$thisstaff->isAdmin()) die('Access Denied'); -$qstr=''; $sql='SELECT topic.* ' - .', IF(ptopic.topic_pid IS NULL, topic.topic, CONCAT_WS(" / ", ptopic.topic, topic.topic)) as name ' .', dept.dept_name as department ' .', priority_desc as priority ' .' FROM '.TOPIC_TABLE.' topic ' - .' LEFT JOIN '.TOPIC_TABLE.' ptopic ON (ptopic.topic_id=topic.topic_pid) ' .' LEFT JOIN '.DEPT_TABLE.' dept ON (dept.dept_id=topic.dept_id) ' .' LEFT JOIN '.TICKET_PRIORITY_TABLE.' pri ON (pri.priority_id=topic.priority_id) '; $sql.=' WHERE 1'; -$sortOptions=array('name'=>'name','status'=>'topic.isactive','type'=>'topic.ispublic', - 'dept'=>'department','priority'=>'priority','updated'=>'topic.updated'); -$orderWays=array('DESC'=>'DESC','ASC'=>'ASC'); -$sort=($_REQUEST['sort'] && $sortOptions[strtolower($_REQUEST['sort'])])?strtolower($_REQUEST['sort']):'name'; -//Sorting options... -if($sort && $sortOptions[$sort]) { - $order_column =$sortOptions[$sort]; -} -$order_column=$order_column?$order_column:'topic.topic'; +$order_by = ($cfg->getTopicSortMode() == 'm' ? '`sort`' : '`topic_id`'); -if($_REQUEST['order'] && $orderWays[strtoupper($_REQUEST['order'])]) { - $order=$orderWays[strtoupper($_REQUEST['order'])]; -} -$order=$order?$order:'ASC'; - -if($order_column && strpos($order_column,',')){ - $order_column=str_replace(','," $order,",$order_column); -} -$x=$sort.'_sort'; -$$x=' class="'.strtolower($order).'" '; -$order_by="$order_column $order "; - -$total=db_count('SELECT count(*) FROM '.TOPIC_TABLE.' topic '); $page=($_GET['p'] && is_numeric($_GET['p']))?$_GET['p']:1; -$pageNav=new Pagenate($total, $page, PAGE_LIMIT); -$pageNav->setURL('helptopics.php',$qstr.'&sort='.urlencode($_REQUEST['sort']).'&order='.urlencode($_REQUEST['order'])); //Ok..lets roll...create the actual query -$qstr.='&order='.($order=='DESC'?'ASC':'DESC'); -$query="$sql GROUP BY topic.topic_id ORDER BY $order_by LIMIT ".$pageNav->getStart().",".$pageNav->getLimit(); +$query="$sql ORDER BY $order_by"; $res=db_query($query); if($res && ($num=db_num_rows($res))) - $showing=$pageNav->showing().' help topics'; + $showing="Showing $num help topics"; else $showing='No help topic found!'; +// Get the full names and filter for this page +$topics = array(); +while ($row = db_fetch_array($res)) + $topics[] = $row; + +foreach ($topics as &$t) + $t['name'] = Topic::getTopicName($t['topic_id']); + +if ($cfg->getTopicSortMode() == 'a') + usort($topics, function($a, $b) { return strcmp($a['name'], $b['name']); }); + ?> <div style="width:700px;padding-top:5px; float:left;"> <h2>Help Topics</h2> @@ -56,28 +40,44 @@ else <form action="helptopics.php" method="POST" name="topics"> <?php csrf_token(); ?> <input type="hidden" name="do" value="mass_process" > -<input type="hidden" id="action" name="a" value="" > +<input type="hidden" id="action" name="a" value="sort" > <table class="list" border="0" cellspacing="1" cellpadding="0" width="940"> - <caption><?php echo $showing; ?></caption> + <caption><span style="display:inline-block;vertical-align:middle"><?php + echo $showing; ?></span> + <div class="pull-right">Sorting Mode: + <select name="help_topic_sort_mode" onchange="javascript: + var $form = $(this).closest('form'); + $form.find('input[name=a]').val('sort'); + $form.submit(); +"> +<?php foreach (OsticketConfig::allTopicSortModes() as $i=>$m) + echo sprintf('<option value="%s"%s>%s</option>', + $i, $i == $cfg->getTopicSortMode() ? ' selected="selected"' : '', $m); ?> + </select> + </div> + </caption> <thead> <tr> - <th width="7"> </th> - <th width="320"><a <?php echo $name_sort; ?> href="helptopics.php?<?php echo $qstr; ?>&sort=name">Help Topic</a></th> - <th width="80"><a <?php echo $status_sort; ?> href="helptopics.php?<?php echo $qstr; ?>&sort=status">Status</a></th> - <th width="100"><a <?php echo $type_sort; ?> href="helptopics.php?<?php echo $qstr; ?>&sort=type">Type</a></th> - <th width="100"><a <?php echo $priority_sort; ?> href="helptopics.php?<?php echo $qstr; ?>&sort=priority">Priority</a></th> - <th width="200"><a <?php echo $dept_sort; ?> href="helptopics.php?<?php echo $qstr; ?>&sort=dept">Department</a></th> - <th width="150" nowrap><a <?php echo $updated_sort; ?>href="helptopics.php?<?php echo $qstr; ?>&sort=updated">Last Updated</a></th> + <th width="7" style="height:20px;"> </th> + <th style="padding-left:4px;vertical-align:middle" width="360">Help Topic</th> + <th style="padding-left:4px;vertical-align:middle" width="80">Status</th> + <th style="padding-left:4px;vertical-align:middle" width="100">Type</th> + <th style="padding-left:4px;vertical-align:middle" width="100">Priority</th> + <th style="padding-left:4px;vertical-align:middle" width="160">Department</th> + <th style="padding-left:4px;vertical-align:middle" width="150" nowrap>Last Updated</th> </tr> </thead> - <tbody> + <tbody class="<?php if ($cfg->getTopicSortMode() == 'm') echo 'sortable-rows'; ?>" + data-sort="sort-"> <?php $total=0; $ids=($errors && is_array($_POST['ids']))?$_POST['ids']:null; - if($res && db_num_rows($res)): + if (count($topics)): $defaultDept = $cfg->getDefaultDept(); $defaultPriority = $cfg->getDefaultPriority(); - while ($row = db_fetch_array($res)) { + $sort = 0; + foreach($topics as $row) { + $sort++; // Track initial order for transition $sel=false; if($ids && in_array($row['topic_id'],$ids)) $sel=true; @@ -93,10 +93,16 @@ else ?> <tr id="<?php echo $row['topic_id']; ?>"> <td width=7px> + <input type="hidden" name="sort-<?php echo $row['topic_id']; ?>" value="<?php + echo $row['sort'] ?: $sort; ?>"/> <input type="checkbox" class="ckb" name="ids[]" value="<?php echo $row['topic_id']; ?>" <?php echo $sel?'checked="checked"':''; ?>> </td> - <td><a href="helptopics.php?id=<?php echo $row['topic_id']; ?>"><?php echo $row['name']; ?></a> </td> + <td> +<?php if ($cfg->getTopicSortMode() == 'm') { ?> + <i class="icon-sort"></i> +<?php } ?> +<a href="helptopics.php?id=<?php echo $row['topic_id']; ?>"><?php echo $row['name']; ?></a> </td> <td><?php echo $row['isactive']?'Active':'<b>Disabled</b>'; ?></td> <td><?php echo $row['ispublic']?'Public':'<b>Private</b>'; ?></td> <td><?php echo $row['priority']; ?></td> @@ -123,12 +129,14 @@ else </table> <?php if($res && $num): //Show options.. - echo '<div> Page:'.$pageNav->getPageLinks().' </div>'; ?> <p class="centered" id="actions"> - <input class="button" type="submit" name="enable" value="Enable" > - <input class="button" type="submit" name="disable" value="Disable"> - <input class="button" type="submit" name="delete" value="Delete"> +<?php if ($cfg->getTopicSortMode() != 'a') { ?> + <input class="button no-confirm" type="submit" name="sort" value="Save"/> +<?php } ?> + <button class="button" type="submit" name="enable" value="Enable" >Enable</button> + <button class="button" type="submit" name="disable" value="Disable">Disable</button> + <button class="button" type="submit" name="delete" value="Delete">Delete</button> </p> <?php endif; diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php index 37bc2af8561a8e12252dca25e9ef10f1167e3c7e..a7960a02b16552d72bd5377a138ccc049b535d46 100644 --- a/include/staff/settings-tickets.inc.php +++ b/include/staff/settings-tickets.inc.php @@ -63,6 +63,20 @@ if(!($maxfileuploads=ini_get('max_file_uploads'))) <span class="error">* <?php echo $errors['default_priority_id']; ?></span> <i class="help-tip icon-question-sign" href="#default_priority"></i> </td> </tr> + <tr> + <td width="180">Default Help Topic:</td> + <td> + <select name="default_help_topic"> + <option value="0">— None —</option><?php + $topics = Topic::getHelpTopics(false, Topic::DISPLAY_DISABLED); + while (list($id,$topic) = each($topics)) { ?> + <option value="<?php echo $id; ?>"<?php echo ($config['default_help_topic']==$id)?'selected':''; ?>><?php echo $topic; ?></option> + <?php + } ?> + </select><br/> + <span class="error"><?php echo $errors['default_help_topic']; ?></span> + </td> + </tr> <tr> <td>Maximum <b>Open</b> Tickets:</td> <td> @@ -88,7 +102,7 @@ if(!($maxfileuploads=ini_get('max_file_uploads'))) <td>Claim on Response:</td> <td> <input type="checkbox" name="auto_claim_tickets" <?php echo $config['auto_claim_tickets']?'checked="checked"':''; ?>> - Enable <i class="help-tip icon-question-sign" href="#claim_tickets"></i> + Enable <i class="help-tip icon-question-sign" href="#claim_tickets"></i> </td> </tr> <tr> diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php index d14f7e98e393e6ed10d5a03c9ce5523e8fdbe6a8..bfd0571bdbc0ffb660831a54f2c463ba095e43b7 100644 --- a/include/staff/ticket-open.inc.php +++ b/include/staff/ticket-open.inc.php @@ -3,6 +3,9 @@ if(!defined('OSTSCPINC') || !$thisstaff || !$thisstaff->canCreateTickets()) die( $info=array(); $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); +if (!$info['topicId']) + $info['topicId'] = $cfg->getDefaultTopicId(); + $form = null; if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) { $form = $topic->getForm(); @@ -124,8 +127,9 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) { </td> <td> <select name="topicId" onchange="javascript: + var data = $(':input[name]', '#dynamic-form').serialize(); $('#dynamic-form').load( - 'ajax.php/form/help-topic/' + this.value); + 'ajax.php/form/help-topic/' + this.value, data); "> <?php if ($topics=Topic::getHelpTopics()) { diff --git a/open.php b/open.php index e0bc8fdcfb1ba75a1efca191e0a31cc06ac36930..fe454521b8b6561f616a125d0ee3b699239ebcc0 100644 --- a/open.php +++ b/open.php @@ -38,6 +38,8 @@ if ($_POST) { //Ticket::create...checks for errors.. if(($ticket=Ticket::create($vars, $errors, SOURCE))){ $msg='Support ticket request created'; + // Drop session-backed form data + unset($_SESSION[':form-data']); //Logged in...simply view the newly created ticket. if($thisclient && $thisclient->isValid()) { session_write_close(); diff --git a/scp/css/scp.css b/scp/css/scp.css index e1721f6e353256052bd87c85a48749c6f61f676e..e99d9c36bce30f4c7e0b57f83e410dc0efc2ddf8 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -454,9 +454,9 @@ table.list tbody td { } table.list tbody td { background: #fff; padding: 1px; padding-left:2px; vertical-align: top; } -table.list tbody tr.odd td { background-color: #f0faff; } +table.list tbody tr:nth-child(2n+1) td { background-color: #f0faff; } table.list tbody tr:hover td { background: #ffe; } -table.list tbody tr.odd:hover td { background: #ffd; } +table.list tbody tr:nth-child(2n+1):hover td { background: #ffd; } /* row highlighting on hover + select */ table.list tbody tr:hover td, table.list tbody tr.highlight td { background: #FFFFDD; } /* disabled highlighting on nohover */ @@ -530,7 +530,7 @@ a.print { color:#000; } -.button { padding:1px 5px; margin-right:10px; color:#777; font-weight:bold;} +#actions button, .button { padding:2px 5px 3px; margin-right:10px; color:#777;} .btn_sm { padding:2px 5px; @@ -1642,6 +1642,10 @@ div.selected-signature .inner { right: 5px; } +.sortable-rows tr td:hover { + cursor: move; +} + .sortable-row-item { border: 1px solid rgba(0, 0, 0, 0.7); padding: 9px; diff --git a/scp/helptopics.php b/scp/helptopics.php index 0847257cfc0d3da5c7d95a79773f0d34062c0b33..b33cad9a5e79b77fae4adce850a6af88c36dc943 100644 --- a/scp/helptopics.php +++ b/scp/helptopics.php @@ -41,9 +41,15 @@ if($_POST){ } break; case 'mass_process': - if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) { - $errors['err'] = 'You must select at least one help topic'; - } else { + switch(strtolower($_POST['a'])) { + case 'sort': + // Pass + break; + default: + if(!$_POST['ids'] || !is_array($_POST['ids']) || !count($_POST['ids'])) + $errors['err'] = 'You must select at least one help topic'; + } + if (!$errors) { $count=count($_POST['ids']); switch(strtolower($_POST['a'])) { @@ -62,7 +68,8 @@ if($_POST){ break; case 'disable': $sql='UPDATE '.TOPIC_TABLE.' SET isactive=0 ' - .' WHERE topic_id IN ('.implode(',', db_input($_POST['ids'])).')'; + .' WHERE topic_id IN ('.implode(',', db_input($_POST['ids'])).')' + .' AND topic_id <> '.db_input($cfg->getDefaultTopicId()); if(db_query($sql) && ($num=db_affected_rows())) { if($num==$count) $msg = 'Selected help topics disabled'; @@ -86,6 +93,23 @@ if($_POST){ elseif(!$errors['err']) $errors['err'] = 'Unable to delete selected help topics'; + break; + case 'sort': + try { + $cfg->setTopicSortMode($_POST['help_topic_sort_mode']); + if ($cfg->getTopicSortMode() == 'm') { + foreach ($_POST as $k=>$v) { + if (strpos($k, 'sort-') === 0 + && is_numeric($v) + && ($t = Topic::lookup(substr($k, 5)))) + $t->setSortOrder($v); + } + } + $msg = 'Successfully set sorting configuration'; + } + catch (Exception $ex) { + $errors['err'] = 'Unable to set sorting mode'; + } break; default: $errors['err']='Unknown action - get technical help.'; diff --git a/scp/js/scp.js b/scp/js/scp.js index b2e0ba77b9a9a1d95555194884d08441b86f3d1a..24efdcab291c6a2711ebe2cdce89c7d3e6bd0350 100644 --- a/scp/js/scp.js +++ b/scp/js/scp.js @@ -32,7 +32,6 @@ function checkbox_checker(formObj, min, max) { var scp_prep = function() { $("input:not(.dp):visible:enabled:first").focus(); - $('table.list tbody tr:odd').addClass('odd'); $('table.list input:checkbox').bind('click, change', function() { $(this) .parents("tr:first") @@ -76,7 +75,7 @@ var scp_prep = function() { return false; }); - $('#actions input:submit.button').bind('click', function(e) { + $('#actions :submit.button:not(.no-confirm)').bind('click', function(e) { var formObj = $(this).closest('form'); e.preventDefault(); @@ -437,6 +436,7 @@ var scp_prep = function() { // Sortable tables for dynamic forms objects $('.sortable-rows').sortable({ 'helper': fixHelper, + 'cursor': 'move', 'stop': function(e, ui) { var attr = ui.item.parent('tbody').data('sort'); warnOnLeave(ui.item); diff --git a/scp/tickets.php b/scp/tickets.php index 82f0e569fd6926ba949f1d41aa6458e3c647e170..16b9d4cde3518968d6fe70a1b345c83c539d5d37 100644 --- a/scp/tickets.php +++ b/scp/tickets.php @@ -497,6 +497,7 @@ if($_POST && !$errors): if (!$ticket->checkStaffAccess($thisstaff) || $ticket->isClosed()) $ticket=null; Draft::deleteForNamespace('ticket.staff%', $thisstaff->getId()); + unset($_SESSION[':form-data']); } elseif(!$errors['err']) { $errors['err']='Unable to create the ticket. Correct the error(s) and try again'; }