From 07e18dc1ac53162ce0b45dca10f845f8433fae6d Mon Sep 17 00:00:00 2001 From: Jared Hancock <jared@osticket.com> Date: Fri, 12 Dec 2014 16:25:07 -0600 Subject: [PATCH] Help topics have much better form configurations Help topics can now specify one or more additional forms to be included on the help topic and can also specify the sort order of those forms. Furthermore, individual fields can be disabled per help topic, so that unnecessary fields can be omitted when necessary, per help topic. The disabled flag is recorded along side the field data so that the field will not be accidentally added to the form later automatically. There is no interface in this commit to enable a field which was disabled by the help topic when ticket was created. --- bootstrap.php | 1 + include/ajax.forms.php | 24 +- include/class.dynamic_forms.php | 35 ++- include/class.ticket.php | 47 ++- include/class.topic.php | 99 +++++- include/client/open.inc.php | 50 +-- include/staff/helptopic.inc.php | 286 ++++++++++++------ include/staff/templates/dynamic-form.tmpl.php | 2 + include/staff/ticket-open.inc.php | 24 +- .../core/5cd0a25a-00000000.cleanup.sql | 8 + .../streams/core/5cd0a25a-00000000.patch.sql | 45 +++ scp/ajax.php | 3 +- scp/css/scp.css | 25 ++ setup/inc/streams/core/install-mysql.sql | 14 +- 14 files changed, 491 insertions(+), 172 deletions(-) create mode 100644 include/upgrader/streams/core/5cd0a25a-00000000.cleanup.sql create mode 100644 include/upgrader/streams/core/5cd0a25a-00000000.patch.sql diff --git a/bootstrap.php b/bootstrap.php index d2afa2243..63d71fe87 100644 --- a/bootstrap.php +++ b/bootstrap.php @@ -116,6 +116,7 @@ class Bootstrap { define('FORM_ANSWER_TABLE',$prefix.'form_entry_values'); define('TOPIC_TABLE',$prefix.'help_topic'); + define('TOPIC_FORM_TABLE',$prefix.'help_topic_form'); define('SLA_TABLE', $prefix.'sla'); define('EMAIL_TABLE',$prefix.'email'); diff --git a/include/ajax.forms.php b/include/ajax.forms.php index 6c35f1bb2..169290c94 100644 --- a/include/ajax.forms.php +++ b/include/ajax.forms.php @@ -24,13 +24,13 @@ class DynamicFormsAjaxAPI extends AjaxController { $_SESSION[':form-data'] = array_merge($_SESSION[':form-data'], $_GET); } - if ($form = $topic->getForm()) { + foreach ($topic->getForms() as $form) { ob_start(); $form->getForm($_SESSION[':form-data'])->render(!$client); - $html = ob_get_clean(); + $html .= ob_get_clean(); ob_start(); print $form->getMedia(); - $media = ob_get_clean(); + $media .= ob_get_clean(); } return $this->encode(array( 'media' => $media, @@ -140,5 +140,23 @@ class DynamicFormsAjaxAPI extends AjaxController { array('id'=>$field->ajaxUpload(true)) ); } + + function getAllFields($id) { + global $thisstaff; + + if (!$thisstaff) + Http::response(403, 'Login required'); + elseif (!$form = DynamicForm::lookup($id)) + Http::response(400, 'No such form'); + + ob_start(); + include STAFFINC_DIR . 'templates/dynamic-form-fields-view.tmpl.php'; + $html = ob_get_clean(); + + return $this->encode(array( + 'success'=>true, + 'html' => $html, + )); + } } ?> diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index 181c1bdbd..cdc621722 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -32,6 +32,11 @@ class DynamicForm extends VerySimpleModel { 'table' => FORM_SEC_TABLE, 'ordering' => array('title'), 'pk' => array('id'), + 'joins' => array( + 'fields' => array( + 'reverse' => 'DynamicFormField.form', + ), + ), ); // Registered form types @@ -112,6 +117,14 @@ class DynamicForm extends VerySimpleModel { return $this->get('deletable'); } + function disableFields(array $ids) { + foreach ($this->getFields() as $F) { + if (in_array($F->get('id'), $ids)) { + $F->disable(); + } + } + } + function instanciate($sort=1) { return DynamicFormEntry::create(array( 'form_id'=>$this->get('id'), 'sort'=>$sort)); @@ -422,6 +435,7 @@ class DynamicFormField extends VerySimpleModel { ); var $_field; + var $_disabled = false; const FLAG_ENABLED = 0x00001; const FLAG_EXT_STORED = 0x00002; // Value stored outside of form_entry_value @@ -525,6 +539,12 @@ class DynamicFormField extends VerySimpleModel { function isEditable() { return $this->hasFlag(self::FLAG_MASK_EDIT); } + function disable() { + $this->_disabled = true; + } + function isEnabled() { + return !$this->_disabled && $this->hasFlag(self::FLAG_ENABLED); + } function hasFlag($flag) { return (isset($this->flags) && ($this->flags & $flag) != 0); @@ -638,19 +658,19 @@ class DynamicFormField extends VerySimpleModel { return $this->hasFlag(self::FLAG_CLOSE_REQUIRED); } function isEditableToStaff() { - return $this->hasFlag(self::FLAG_ENABLED) + return $this->isEnabled() && $this->hasFlag(self::FLAG_AGENT_EDIT); } function isVisibleToStaff() { - return $this->hasFlag(self::FLAG_ENABLED) + return $this->isEnabled() && $this->hasFlag(self::FLAG_AGENT_VIEW); } function isEditableToUsers() { - return $this->hasFlag(self::FLAG_ENABLED) + return $this->isEnabled() && $this->hasFlag(self::FLAG_CLIENT_EDIT); } function isVisibleToUsers() { - return $this->hasFlag(self::FLAG_ENABLED) + return $this->isEnabled() && $this->hasFlag(self::FLAG_CLIENT_VIEW); } @@ -722,7 +742,7 @@ class DynamicFormEntry extends VerySimpleModel { 'pk' => array('id'), 'select_related' => array('form'), 'fields' => array('id', 'form_id', 'object_type', 'object_id', - 'sort', 'updated', 'created'), + 'sort', 'extra', 'updated', 'created'), 'joins' => array( 'form' => array( 'null' => true, @@ -785,6 +805,10 @@ class DynamicFormEntry extends VerySimpleModel { $this->_form = DynamicForm::lookup($this->get('form_id')); if ($this->_form && isset($this->id)) $this->_form->data($this); + if (isset($this->extra)) { + $x = JsonDataParser::decode($this->extra) ?: array(); + $this->_form->disableFields($x['disable'] ?: array()); + } } return $this->_form; } @@ -958,6 +982,7 @@ class DynamicFormEntry extends VerySimpleModel { } } if (!$found && ($fImpl = $field->getImpl($field)) + && $field->isEnabled() && !$fImpl->isPresentationOnly()) { $a = DynamicFormEntryAnswer::create( array('field_id'=>$field->get('id'), 'entry_id'=>$this->id)); diff --git a/include/class.ticket.php b/include/class.ticket.php index 1dd62e97c..dc6f5fe31 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -2758,20 +2758,43 @@ class Ticket { if (!$errors) { - # Perform ticket filter actions on the new ticket arguments - $__form = null; + // Handle the forms associate with the help topics. Instanciate the + // entries, disable and track the requested disabled fields. + $topic_forms = array(); if ($vars['topicId']) { - if (($__topic=Topic::lookup($vars['topicId'])) - && ($__form = $__topic->getForm()) - ) { - $__form = $__form->instanciate(); - $__form->setSource($vars); + if ($__topic=Topic::lookup($vars['topicId'])) { + foreach ($__topic->getForms() as $idx=>$__F) { + $disabled = array(); + foreach ($__F->getFields() as $field) { + if (!$field->isEnabled() && $field->hasFlag(DynamicFormField::FLAG_ENABLED)) + $disabled[] = $field->get('id'); + } + // Special handling for the ticket form — disable fields + // requested to be disabled as per the help topic. + if ($__F->get('type') == 'T') { + foreach ($form->getFields() as $field) { + if (false !== array_search($field->get('id'), $disabled)) + $field->disable(); + } + $form->sort = $idx; + $__F = $form; + } + else { + $__F = $__F->instanciate($idx); + $__F->setSource($vars); + $topic_forms[] = $__F; + } + // Track fields currently disabled + $__F->extra = JsonDataEncoder::encode(array( + 'disable' => $disabled + )); + } } } try { $vars = self::filterTicketData($origin, $vars, - array($form, $__form), $user); + array_merge(array($form), $topic_forms), $user); } catch (RejectedException $ex) { return $reject_ticket( @@ -2825,10 +2848,8 @@ class Ticket { if ($vars['topicId']) { if ($topic=Topic::lookup($vars['topicId'])) { - if ($topic_form = $topic->getForm()) { - $TF = $topic_form->getForm($vars); - $topic_form = $topic_form->instanciate(); - $topic_form->setSource($vars); + foreach ($topic_forms as $topic_form) { + $TF = $topic_form->getForm()->getForm($vars); if (!$TF->isValid($field_filter('topic'))) $errors = array_merge($errors, $TF->errors()); } @@ -2972,7 +2993,7 @@ class Ticket { $form->save(); // Save the form data from the help-topic form, if any - if ($topic_form) { + foreach ($topic_forms as $topic_form) { $topic_form->setTicketId($id); $topic_form->save(); } diff --git a/include/class.topic.php b/include/class.topic.php index 5adc57563..9f02858ef 100644 --- a/include/class.topic.php +++ b/include/class.topic.php @@ -52,11 +52,15 @@ class Topic extends VerySimpleModel { 'priority_id' => 'Priority.priority_id', ), ), + 'forms' => array( + 'reverse' => 'TopicFormModel.topic', + 'null' => true, + ), ), ); var $page; - var $form; + var $_forms; const DISPLAY_DISABLED = 2; @@ -129,19 +133,15 @@ class Topic extends VerySimpleModel { return $this->page; } - function getFormId() { - return $this->form_id; - } - - function getForm() { - $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; + function getForms() { + if (!isset($this->_forms)) { + foreach ($this->forms->select_related('form') as $F) { + $extra = JsonDataParser::decode($F->extra) ?: array(); + $F->form->disableFields($extra['disable'] ?: array()); + $this->_forms[] = $F->form; + } + } + return $this->_forms; } function autoRespond() { @@ -426,12 +426,63 @@ class Topic extends VerySimpleModel { $errors['err']=sprintf(__('Unable to update %s.'), __('this help topic')) .' '.__('Internal error occurred'); } - if (!$cfg || $cfg->getTopicSortMode() == 'a') { - static::updateSortOrder(); + if ($rv) { + if (!$cfg || $cfg->getTopicSortMode() == 'a') { + static::updateSortOrder(); + } + $this->updateForms($vars, $errors); } return $rv; } + function updateForms($vars, &$errors) { + $find_disabled = function($form) use ($vars) { + $fields = $vars['fields']; + $disabled = array(); + foreach ($form->fields->values_flat('id') as $row) { + list($id) = $row; + if (false === ($idx = array_search($id, $fields))) { + $disabled[] = $id; + } + } + return $disabled; + }; + + // Consider all the forms in the request + if (is_array($form_ids = $vars['forms'])) { + $forms = TopicFormModel::objects() + ->select_related('form') + ->filter(array('topic_id' => $this->getId())); + foreach ($forms as $F) { + if (false !== ($idx = array_search($F->form_id, $form_ids))) { + $F->sort = $idx + 1; + $F->extra = JsonDataEncoder::encode( + array('disable' => $find_disabled($F->form)) + ); + $F->save(); + unset($form_ids[$idx]); + } + elseif ($F->form->get('type') != 'T') { + $F->delete(); + } + } + foreach ($form_ids as $sort=>$id) { + if (!($form = DynamicForm::lookup($id))) { + continue; + } + TopicFormModel::create(array( + 'topic_id' => $this->getId(), + 'form_id' => $id, + 'sort' => $sort + 1, + 'extra' => JsonDataEncoder::encode( + array('disable' => $find_disabled($form)) + ) + ))->save(); + } + } + return true; + } + function save($refetch=false) { if ($this->dirty) $this->updated = SqlFunction::NOW(); @@ -473,3 +524,19 @@ class Topic extends VerySimpleModel { // Add fields from the standard ticket form to the ticket filterable fields Filter::addSupportedMatches(/* @trans */ 'Help Topic', array('topicId' => 'Topic ID'), 100); + +class TopicFormModel extends VerySimpleModel { + static $meta = array( + 'table' => TOPIC_FORM_TABLE, + 'pk' => array('id'), + 'ordering' => array('sort'), + 'joins' => array( + 'topic' => array( + 'constraint' => array('topic_id' => 'Topic.topic_id'), + ), + 'form' => array( + 'constraint' => array('form_id' => 'DynamicForm.id'), + ), + ), + ); +} diff --git a/include/client/open.inc.php b/include/client/open.inc.php index 821aa6ef9..715295165 100644 --- a/include/client/open.inc.php +++ b/include/client/open.inc.php @@ -13,11 +13,14 @@ $form = null; if (!$info['topicId']) $info['topicId'] = $cfg->getDefaultTopicId(); +$forms = array(); if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) { - $form = $topic->getForm(); - if ($_POST && $form) { - $form = $form->instanciate(); - $form->isValidForClient(); + foreach ($topic->getForms() as $F) { + if ($_POST) { + $F = $F->instanciate(); + $F->isValidForClient(); + } + $forms[] = $F; } } @@ -29,9 +32,26 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) { <input type="hidden" name="a" value="open"> <table width="800" cellpadding="1" cellspacing="0" border="0"> <tbody> +<?php + if (!$thisclient) { + $uform = UserForm::getUserForm()->getForm($_POST); + if ($_POST) $uform->isValid(); + $uform->render(false); + } + else { ?> + <tr><td colspan="2"><hr /></td></tr> + <tr><td><?php echo __('Email'); ?>:</td><td><?php echo $thisclient->getEmail(); ?></td></tr> + <tr><td><?php echo __('Client'); ?>:</td><td><?php echo $thisclient->getName(); ?></td></tr> + <?php } ?> + </tbody> + <tbody> + <tr><td colspan="2"><hr /> + <div class="form-header" style="margin-bottom:0.5em"> + <b><?php echo __('Help Topic'); ?></b> + </div> + </td></tr> <tr> - <td class="required"><?php echo __('Help Topic');?>:</td> - <td> + <td colspan="2"> <select id="topicId" name="topicId" onchange="javascript: var data = $(':input[name]', '#dynamic-form').serialize(); $.ajax( @@ -59,28 +79,12 @@ if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) { <font class="error">* <?php echo $errors['topicId']; ?></font> </td> </tr> -<?php - if (!$thisclient) { - $uform = UserForm::getUserForm()->getForm($_POST); - if ($_POST) $uform->isValid(); - $uform->render(false); - } - else { ?> - <tr><td colspan="2"><hr /></td></tr> - <tr><td><?php echo __('Email'); ?>:</td><td><?php echo $thisclient->getEmail(); ?></td></tr> - <tr><td><?php echo __('Client'); ?>:</td><td><?php echo $thisclient->getName(); ?></td></tr> - <?php } ?> </tbody> <tbody id="dynamic-form"> - <?php if ($form) { + <?php foreach ($forms as $form) { include(CLIENTINC_DIR . 'templates/dynamic-form.tmpl.php'); } ?> </tbody> - <tbody><?php - $tform = TicketForm::getInstance()->getForm($_POST); - if ($_POST) $tform->isValid(); - $tform->render(false); ?> - </tbody> <tbody> <?php if($cfg && $cfg->isCaptchaEnabled() && (!$thisclient || !$thisclient->isValid())) { diff --git a/include/staff/helptopic.inc.php b/include/staff/helptopic.inc.php index 6fb6371bc..c9702d210 100644 --- a/include/staff/helptopic.inc.php +++ b/include/staff/helptopic.inc.php @@ -10,6 +10,7 @@ if($topic && $_REQUEST['a']!='add') { $info['pid']=$topic->getPid(); $trans['name'] = $topic->getTranslateTag('name'); $qs += array('id' => $topic->getId()); + $forms = $topic->getForms(); } else { $title=__('Add New Help Topic'); $action='create'; @@ -21,22 +22,31 @@ if($topic && $_REQUEST['a']!='add') { } $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); ?> + +<h2 style="font-weight: normal"><?php echo $title; ?> + <i class="help-tip icon-question-sign" href="#help_topic_information"></i> + </h2> +<?php if ($topic) { ?> + <div class="big"><strong><?php echo $topic->getLocal('topic'); ?></strong></div> +<?php } ?> + +<br/> + +<ul class="tabs" id="topic-tabs"> + <li class="active"><a href="#info"><i class="icon-info-sign"></i> Help Topic Information</a></li> + <li><a href="#routing"><i class="icon-ticket"></i> New ticket options</a></li> + <li><a href="#forms"><i class="icon-paste"></i> Forms</a></li> +</ul> + <form action="helptopics.php?<?php echo Http::build_query($qs); ?>" method="post" id="save"> <?php csrf_token(); ?> <input type="hidden" name="do" value="<?php echo $action; ?>"> <input type="hidden" name="a" value="<?php echo Format::htmlchars($_REQUEST['a']); ?>"> <input type="hidden" name="id" value="<?php echo $info['id']; ?>"> - <h2><?php echo __('Help Topic');?></h2> - <table class="form_table" width="940" border="0" cellspacing="0" cellpadding="2"> - <thead> - <tr> - <th colspan="2"> - <h4><?php echo $title; ?></h4> - <em><?php echo __('Help Topic Information');?> - <i class="help-tip icon-question-sign" href="#help_topic_information"></i></em> - </th> - </tr> - </thead> + +<div id="topic-tabs_container"> +<div class="tab_content" id="info"> + <table class="table" border="0" cellspacing="0" cellpadding="2"> <tbody> <tr> <td width="180" class="required"> @@ -53,8 +63,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <?php echo __('Status');?>: </td> <td> - <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>><?php echo __('Active'); ?> - <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>><?php echo __('Disabled'); ?> + <input type="radio" name="isactive" value="1" <?php echo $info['isactive']?'checked="checked"':''; ?>> <?php echo __('Active'); ?> + <input type="radio" name="isactive" value="0" <?php echo !$info['isactive']?'checked="checked"':''; ?>> <?php echo __('Disabled'); ?> <span class="error">* </span> <i class="help-tip icon-question-sign" href="#status"></i> </td> </tr> @@ -63,8 +73,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <?php echo __('Type');?>: </td> <td> - <input type="radio" name="ispublic" value="1" <?php echo $info['ispublic']?'checked="checked"':''; ?>><?php echo __('Public'); ?> - <input type="radio" name="ispublic" value="0" <?php echo !$info['ispublic']?'checked="checked"':''; ?>><?php echo __('Private/Internal'); ?> + <input type="radio" name="ispublic" value="1" <?php echo $info['ispublic']?'checked="checked"':''; ?>> <?php echo __('Public'); ?> + <input type="radio" name="ispublic" value="0" <?php echo !$info['ispublic']?'checked="checked"':''; ?>> <?php echo __('Private/Internal'); ?> <span class="error">* </span> <i class="help-tip icon-question-sign" href="#type"></i> </td> </tr> @@ -87,28 +97,26 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </td> </tr> - <tr><th colspan="2"><em><?php echo __('New ticket options');?></em></th></tr> - <tr> - <td><strong><?php echo __('Custom Form'); ?></strong>:</td> - <td><select name="form_id"> - <option value="0" <?php -if ($info['form_id'] == '0') echo 'selected="selected"'; - ?>>— <?php echo __('None'); ?> —</option> - <option value="<?php echo Topic::FORM_USE_PARENT; ?>" <?php -if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"'; - ?>>— <?php echo __('Use Parent Form'); ?> —</option> - <?php foreach (DynamicForm::objects()->filter(array('type'=>'G')) as $group) { ?> - <option value="<?php echo $group->get('id'); ?>" - <?php if ($group->get('id') == $info['form_id']) - echo 'selected="selected"'; ?>> - <?php echo $group->get('title'); ?> - </option> - <?php } ?> - </select> - <span class="error"> <?php echo $errors['form_id']; ?></span> - <i class="help-tip icon-question-sign" href="#custom_form"></i> - </td> - </tr> + </tbody> + </table> + + <div style="padding:8px 3px;border-bottom: 2px dotted #ddd;"> + <strong class="big"><?php echo __('Internal Notes');?></strong><br/> + <?php echo __("be liberal, they're internal.");?> + </div> + + <textarea class="richtext no-bar" name="notes" cols="21" + rows="8" style="width: 80%;"><?php echo $info['notes']; ?></textarea> + +</div> + +<div class="hidden tab_content" id="routing"> +<div style="padding:8px 0;border-bottom: 2px dotted #ddd;"> +<div><b class="big"><?php echo __('New ticket options');?></b></div> +</div> + + <table class="table" border="0" cellspacing="0" cellpadding="2"> + <tbody> <tr> <td width="180" class="required"> <?php echo __('Department'); ?>: @@ -127,6 +135,62 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"'; <i class="help-tip icon-question-sign" href="#department"></i> </td> </tr> + <tr class="border"> + <td> + <?php echo __('Ticket Number Format'); ?>: + </td> + <td> + <label> + <input type="radio" name="custom-numbers" value="0" <?php echo !$info['custom-numbers']?'checked="checked"':''; ?> + onchange="javascript:$('#custom-numbers').hide();"> <?php echo __('System Default'); ?> + </label> <label> + <input type="radio" name="custom-numbers" value="1" <?php echo $info['custom-numbers']?'checked="checked"':''; ?> + onchange="javascript:$('#custom-numbers').show(200);"> <?php echo __('Custom'); ?> + </label> <i class="help-tip icon-question-sign" href="#custom_numbers"></i> + </td> + </tr> + </tbody> + <tbody id="custom-numbers" style="<?php if (!$info['custom-numbers']) echo 'display:none'; ?>"> + <tr> + <td style="padding-left:20px"> + <?php echo __('Format'); ?>: + </td> + <td> + <input type="text" name="number_format" value="<?php echo $info['number_format']; ?>"/> + <span class="faded"><?php echo __('e.g.'); ?> <span id="format-example"><?php + if ($info['custom-numbers']) { + if ($info['sequence_id']) + $seq = Sequence::lookup($info['sequence_id']); + if (!isset($seq)) + $seq = new RandomSequence(); + echo $seq->current($info['number_format']); + } ?></span></span> + <div class="error"><?php echo $errors['number_format']; ?></div> + </td> + </tr> + <tr> +<?php $selected = 'selected="selected"'; ?> + <td style="padding-left:20px"> + <?php echo __('Sequence'); ?>: + </td> + <td> + <select name="sequence_id"> + <option value="0" <?php if ($info['sequence_id'] == 0) echo $selected; + ?>>— <?php echo __('Random'); ?> —</option> +<?php foreach (Sequence::objects() as $s) { ?> + <option value="<?php echo $s->id; ?>" <?php + if ($info['sequence_id'] == $s->id) echo $selected; + ?>><?php echo $s->name; ?></option> +<?php } ?> + </select> + <button class="action-button pull-right" onclick="javascript: + $.dialog('ajax.php/sequence/manage', 205); + return false; + "><i class="icon-gear"></i> <?php echo __('Manage'); ?></button> + </td> + </tr> + </tbody> + <tbody> <tr> <td width="180"> <?php echo __('Status'); ?>: @@ -266,75 +330,90 @@ if ($info['form_id'] == Topic::FORM_USE_PARENT) echo 'selected="selected"'; <i class="help-tip icon-question-sign" href="#ticket_auto_response"></i> </td> </tr> - <tr> - <td> - <?php echo __('Ticket Number Format'); ?>: - </td> - <td> - <label> - <input type="radio" name="custom-numbers" value="0" <?php echo !$info['custom-numbers']?'checked="checked"':''; ?> - onchange="javascript:$('#custom-numbers').hide();"> <?php echo __('System Default'); ?> - </label> <label> - <input type="radio" name="custom-numbers" value="1" <?php echo $info['custom-numbers']?'checked="checked"':''; ?> - onchange="javascript:$('#custom-numbers').show(200);"> <?php echo __('Custom'); ?> - </label> <i class="help-tip icon-question-sign" href="#custom_numbers"></i> - </td> - </tr> </tbody> - <tbody id="custom-numbers" style="<?php if (!$info['custom-numbers']) echo 'display:none'; ?>"> - <tr> - <td style="padding-left:20px"> - <?php echo __('Format'); ?>: - </td> - <td> - <input type="text" name="number_format" value="<?php echo $info['number_format']; ?>"/> - <span class="faded"><?php echo __('e.g.'); ?> <span id="format-example"><?php - if ($info['custom-numbers']) { - if ($info['sequence_id']) - $seq = Sequence::lookup($info['sequence_id']); - if (!isset($seq)) - $seq = new RandomSequence(); - echo $seq->current($info['number_format']); - } ?></span></span> - <div class="error"><?php echo $errors['number_format']; ?></div> - </td> - </tr> + </table> +</div> + +<div class="hidden tab_content" id="forms"> + <table id="topic-forms" class="table" border="0" cellspacing="0" cellpadding="2"> + +<?php foreach ($forms as $F) { ?> + <tbody data-form-id="<?php echo $F->get('id'); ?>"> <tr> -<?php $selected = 'selected="selected"'; ?> - <td style="padding-left:20px"> - <?php echo __('Sequence'); ?>: - </td> - <td> - <select name="sequence_id"> - <option value="0" <?php if ($info['sequence_id'] == 0) echo $selected; - ?>>— <?php echo __('Random'); ?> —</option> -<?php foreach (Sequence::objects() as $s) { ?> - <option value="<?php echo $s->id; ?>" <?php - if ($info['sequence_id'] == $s->id) echo $selected; - ?>><?php echo $s->name; ?></option> + <td class="handle" colspan="6"> + <input type="hidden" name="forms[]" value="<?php echo $F->get('id'); ?>" /> + <div class="pull-right"> + <i class="icon-2x icon-move icon-muted"></i> +<?php if ($F->get('type') != 'T') { ?> + <a href="#" title="<?php echo __('Delete'); ?>" onclick="javascript: + if (confirm(__('You sure?'))) + var tbody = $(this).closest('tbody'); + tbody.fadeOut(function(){this.remove()}); + $(this).closest('form') + .find('[name=form_id] [value=' + tbody.data('formId') + ']') + .prop('disabled', false); + return false;"><i class="icon-2x icon-trash"></i></a> <?php } ?> - </select> - <button class="action-button pull-right" onclick="javascript: - $.dialog('ajax.php/sequence/manage', 205); - return false; - "><i class="icon-gear"></i> <?php echo __('Manage'); ?></button> + </div> + <div><strong><?php echo $F->getLocal('title'); ?></strong></div> + <div><?php echo $F->getLocal('instructions'); ?></div> </td> </tr> - </tbody> - <tbody> <tr> - <th colspan="2"> - <em><strong><?php echo __('Internal Notes');?></strong>: <?php echo __("be liberal, they're internal.");?></em> - </th> + <th><?php echo __('Enable'); ?></th> + <th><?php echo __('Label'); ?></th> + <th><?php echo __('Type'); ?></th> + <th><?php echo __('Visibility'); ?></th> + <th><?php echo __('Variable'); ?></th> </tr> + <?php + foreach ($F->getFields() as $f) { ?> <tr> - <td colspan=2> - <textarea class="richtext no-bar" name="notes" cols="21" - rows="8" style="width: 80%;"><?php echo $info['notes']; ?></textarea> - </td> + <td><input type="checkbox" name="fields[]" value="<?php + echo $f->get('id'); ?>" <?php + if ($f->isEnabled()) echo 'checked="checked"'; ?>/></td> + <td><?php echo $f->get('label'); ?></td> + <td><?php $t=FormField::getFieldType($f->get('type')); echo __($t[0]); ?></td> + <td><?php echo $f->getVisibilityDescription(); ?></td> + <td><?php echo $f->get('name'); ?></td> </tr> + <?php } ?> </tbody> -</table> + <?php } ?> + </table> + + <br/> + <strong><?php echo __('Add Custom Form'); ?></strong>: + <select name="form_id" onchange="javascript: + event.preventDefault(); + var $this = $(this), + val = $this.val(); + if (!val) return; + $.ajax({ + url: 'ajax.php/form/' + val + '/fields/view', + dataType: 'json', + success: function(json) { + if (json.success) { + $(json.html).appendTo('#topic-forms').effect('highlight'); + $this.find(':selected').prop('disabled', true); + } + } + });"> + <option value=""><?php echo '— '.__('Add a custom form') . ' —'; ?></option> + <?php foreach (DynamicForm::objects()->filter(array('type'=>'G')) as $F) { ?> + <option value="<?php echo $F->get('id'); ?>" + <?php if ($F->get('id') == $info['form_id']) + echo 'selected="selected"'; ?>> + <?php echo $F->getLocal('title'); ?> + </option> + <?php } ?> + </select> + <span class="error"> <?php echo $errors['form_id']; ?></span> + <i class="help-tip icon-question-sign" href="#custom_form"></i> +</div> + +</div> + <p style="text-align:center;"> <input type="submit" name="submit" value="<?php echo $submit_text; ?>"> <input type="reset" name="reset" value="<?php echo __('Reset');?>"> @@ -355,4 +434,19 @@ $(function() { $('[name=sequence_id]').on('change', update_example); $('[name=number_format]').on('keyup', update_example); }); +$('table#topic-forms').sortable({ + items: 'tbody', + handle: 'td.handle', + tolerance: 'pointer', + forcePlaceholderSize: true, + helper: function(e, ui) { + ui.children().each(function() { + $(this).children().each(function() { + $(this).width($(this).width()); + }); + }); + ui=ui.clone().css({'background-color':'white', 'opacity':0.8}); + return ui; + } +}); </script> diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php index 487ce9a56..6fc7b8978 100644 --- a/include/staff/templates/dynamic-form.tmpl.php +++ b/include/staff/templates/dynamic-form.tmpl.php @@ -38,6 +38,8 @@ if (isset($options['entry']) && $options['mode'] == 'edit') { ?> } foreach ($form->getFields() as $field) { try { + if (!$field->isEnabled()) + continue; if ($options['mode'] == 'edit' && !$field->isEditableToStaff()) continue; } diff --git a/include/staff/ticket-open.inc.php b/include/staff/ticket-open.inc.php index fcebc13d4..50750cdd0 100644 --- a/include/staff/ticket-open.inc.php +++ b/include/staff/ticket-open.inc.php @@ -9,12 +9,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); if (!$info['topicId']) $info['topicId'] = $cfg->getDefaultTopicId(); -$form = null; +$forms = array(); if ($info['topicId'] && ($topic=Topic::lookup($info['topicId']))) { - $form = $topic->getForm(); - if ($_POST && $form) { - $form = $form->instanciate(); - $form->isValid(); + foreach ($topic->getForms() as $F) { + if ($_POST) { + $F = $F->instanciate(); + $F->isValidForClient(); + } + $forms[] = $F; } } @@ -156,9 +158,9 @@ if ($_POST) $id, ($info['topicId']==$id)?'selected="selected"':'', $selected, $name); } - if (count($topics) == 1 && !$form) { + if (count($topics) == 1 && !$forms) { if (($T = Topic::lookup($id))) - $form = $T->getForm(); + $forms = $T->getForms(); } } ?> @@ -260,18 +262,12 @@ if ($_POST) </tbody> <tbody id="dynamic-form"> <?php - if ($form) { + foreach ($forms as $form) { print $form->getForm()->getMedia(); include(STAFFINC_DIR . 'templates/dynamic-form.tmpl.php'); } ?> </tbody> - <tbody> <?php - $tform = TicketForm::getInstance()->getForm($_POST); - if ($_POST) $tform->isValid(); - $tform->render(true); - ?> - </tbody> <tbody> <?php //is the user allowed to post replies?? diff --git a/include/upgrader/streams/core/5cd0a25a-00000000.cleanup.sql b/include/upgrader/streams/core/5cd0a25a-00000000.cleanup.sql new file mode 100644 index 000000000..3b391fc4d --- /dev/null +++ b/include/upgrader/streams/core/5cd0a25a-00000000.cleanup.sql @@ -0,0 +1,8 @@ +/** + * @signature 0e47d678f50874fa0d33e1e3759f657e + * @version v1.9.6 + * @title Make fields disable-able per help topic + */ + +ALTER TABLE `%TABLE_PREFIX%help_topic` + DROP `form_id` int(10) unsigned NOT NULL default '0'; diff --git a/include/upgrader/streams/core/5cd0a25a-00000000.patch.sql b/include/upgrader/streams/core/5cd0a25a-00000000.patch.sql new file mode 100644 index 000000000..58e4fdfc2 --- /dev/null +++ b/include/upgrader/streams/core/5cd0a25a-00000000.patch.sql @@ -0,0 +1,45 @@ +/** + * @signature 0e47d678f50874fa0d33e1e3759f657e + * @version v1.9.6 + * @title Make fields disable-able per help topic + */ + +ALTER TABLE `%TABLE_PREFIX%form` + ADD `pid` int(10) unsigned DEFAULT NULL AFTER `id`, + ADD `name` varchar(64) NOT NULL DEFAULT '' AFTER `instructions`; + +ALTER TABLE `%TABLE_PREFIX%form_entry` + ADD `extra` text AFTER `sort`; + +CREATE TABLE `%TABLE_PREFIX%help_topic_form` ( + `id` int(11) unsigned NOT NULL auto_increment, + `topic_id` int(11) unsigned NOT NULL default 0, + `form_id` int(10) unsigned NOT NULL default 0, + `sort` int(10) unsigned NOT NULL default 1, + `extra` text, + PRIMARY KEY (`topic_id`, `form_id`) +) DEFAULT CHARSET=utf8; + +insert into `%TABLE_PREFIX%help_topic_form` + (`topic_id`, `form_id`, `sort`) + select A1.topic_id, case + when A2.form_id = 4294967295 then A3.form_id + when A1.form_id = 4294967295 then A2.form_id + else A1.form_id end as form_id, 1 as `sort` + from `%TABLE_PREFIX%help_topic` A1 + left join `%TABLE_PREFIX%help_topic` A2 on (A2.topic_pid = A1.topic_id) + left join `%TABLE_PREFIX%help_topic` A3 on (A3.topic_pid = A2.topic_id) + having `form_id` > 0 + union + select A2.topic_id, id as `form_id`, 2 as `sort` + from `%TABLE_PREFIX%form` A1 + join `%TABLE_PREFIX%help_topic` A2 + where A1.`type` = 'T'; + +ALTER TABLE `%TABLE_PREFIX%help_topic` + DROP `form_id` int(10) unsigned NOT NULL default '0'; + +-- Finished with patch +UPDATE `%TABLE_PREFIX%config` + SET `value` = '0e47d678f50874fa0d33e1e3759f657e' + WHERE `key` = 'schema_signature' AND `namespace` = 'core'; diff --git a/scp/ajax.php b/scp/ajax.php index 131243ade..79dd8e1b3 100644 --- a/scp/ajax.php +++ b/scp/ajax.php @@ -57,7 +57,8 @@ $dispatcher = patterns('', url_post('^field-config/(?P<id>\d+)$', 'saveFieldConfiguration'), url_delete('^answer/(?P<entry>\d+)/(?P<field>\d+)$', 'deleteAnswer'), url_post('^upload/(\d+)?$', 'upload'), - url_post('^upload/(\w+)?$', 'attach') + url_post('^upload/(\w+)?$', 'attach'), + url_get('^(?P<id>\d+)/fields/view$', 'getAllFields') )), url('^/list/', patterns('ajax.forms.php:DynamicFormsAjaxAPI', url_get('^(?P<list>\w+)/item/(?P<id>\d+)/properties$', 'getListItemProperties'), diff --git a/scp/css/scp.css b/scp/css/scp.css index 0d76bf2ba..477368083 100644 --- a/scp/css/scp.css +++ b/scp/css/scp.css @@ -51,6 +51,10 @@ div#header a { clear:both; } +.big { + font-size: 110%; +} + .faded { color:#666; } @@ -589,6 +593,27 @@ a.print { padding:2px; } +.table { + width: 100%; + border-collapse: collapse; + margin-top:3px; +} + +.table tr.header td, +.table th { + font-weight: bold; + text-align: left; + height: 24px; + background: #f0f0f0; +} + +.table tr { + border-bottom:1px dotted #ddd; +} +.table td:not(:empty) { + height: 24px; +} + .form_table { margin-top:3px; border-left:1px solid #ddd; diff --git a/setup/inc/streams/core/install-mysql.sql b/setup/inc/streams/core/install-mysql.sql index 124b65afb..738df56ce 100644 --- a/setup/inc/streams/core/install-mysql.sql +++ b/setup/inc/streams/core/install-mysql.sql @@ -115,10 +115,12 @@ INSERT INTO `%TABLE_PREFIX%config` (`namespace`, `key`, `value`) VALUES DROP TABLE IF EXISTS `%TABLE_PREFIX%form`; CREATE TABLE `%TABLE_PREFIX%form` ( `id` int(11) unsigned NOT NULL auto_increment, + `pid` int(10) unsigned DEFAULT NULL, `type` varchar(8) NOT NULL DEFAULT 'G', `deletable` tinyint(1) NOT NULL DEFAULT 1, `title` varchar(255) NOT NULL, `instructions` varchar(512), + `name` varchar(64) NOT NULL DEFAULT '', `notes` text, `created` datetime NOT NULL, `updated` datetime NOT NULL, @@ -151,6 +153,7 @@ CREATE TABLE `%TABLE_PREFIX%form_entry` ( `object_id` int(11) unsigned, `object_type` char(1) NOT NULL DEFAULT 'T', `sort` int(11) unsigned NOT NULL DEFAULT 1, + `extra` text, `created` datetime NOT NULL, `updated` datetime NOT NULL, PRIMARY KEY (`id`), @@ -443,7 +446,6 @@ CREATE TABLE `%TABLE_PREFIX%help_topic` ( `team_id` int(10) unsigned NOT NULL default '0', `sla_id` int(10) unsigned NOT NULL default '0', `page_id` int(10) unsigned NOT NULL default '0', - `form_id` int(10) unsigned NOT NULL default '0', `sequence_id` int(10) unsigned NOT NULL DEFAULT '0', `sort` int(10) unsigned NOT NULL default '0', `topic` varchar(32) NOT NULL default '', @@ -461,6 +463,16 @@ CREATE TABLE `%TABLE_PREFIX%help_topic` ( KEY `page_id` (`page_id`) ) DEFAULT CHARSET=utf8; +DROP TABLE IF EXISTS `%TABLE_PREFIX%help_topic_form`; +CREATE TABLE `%TABLE_PREFIX%help_topic_form` ( + `id` int(11) unsigned NOT NULL auto_increment, + `topic_id` int(11) unsigned NOT NULL default 0, + `form_id` int(10) unsigned NOT NULL default 0, + `sort` int(10) unsigned NOT NULL default 1, + `extra` text, + PRIMARY KEY (`topic_id`, `form_id`) +) DEFAULT CHARSET=utf8; + DROP TABLE IF EXISTS `%TABLE_PREFIX%organization`; CREATE TABLE `%TABLE_PREFIX%organization` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, -- GitLab