diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index b8b0407356abab13001ba16987a13660b512e403..039189a2788f365212629b66674c73b5da7a14fe 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -411,6 +411,15 @@ class DynamicFormField extends VerySimpleModel { var $_field; + const REQUIRE_NOBODY = 0; + const REQUIRE_EVERYONE = 1; + const REQUIRE_ENDUSER = 2; + const REQUIRE_AGENT = 3; + + const VISIBLE_EVERYONE = 0; + const VISIBLE_AGENTONLY = 1; + const VISIBLE_ENDUSERONLY = 2; + // Multiple inheritance -- delegate to FormField function __call($what, $args) { return call_user_func_array( @@ -480,6 +489,81 @@ class DynamicFormField extends VerySimpleModel { return (($this->get('edit_mask') & 32) == 0); } + function allRequirementModes() { + return array( + 'a' => array('desc' => __('Optional'), + 'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_NOBODY), + 'b' => array('desc' => __('Required'), + 'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_EVERYONE), + 'c' => array('desc' => __('Required for EndUsers'), + 'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_ENDUSER), + 'd' => array('desc' => __('Required for Agents'), + 'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_AGENT), + 'e' => array('desc' => __('Internal, Optional'), + 'private' => self::VISIBLE_AGENTONLY, 'required' => self::REQUIRE_NOBODY), + 'f' => array('desc' => __('Internal, Required'), + 'private' => self::VISIBLE_AGENTONLY, 'required' => self::REQUIRE_EVERYONE), + 'g' => array('desc' => __('For EndUsers Only'), + 'private' => self::VISIBLE_ENDUSERONLY, 'required' => self::REQUIRE_ENDUSER), + ); + } + + function getAllRequirementModes() { + $modes = static::allRequirementModes(); + if ($this->isPrivacyForced()) { + // Required to be internal + foreach ($modes as $m=>$info) { + if ($info['private'] != $this->get('private')) + unset($modes[$m]); + } + } + + if ($this->isRequirementForced()) { + // Required to be required + foreach ($modes as $m=>$info) { + if ($info['required'] != $this->get('required')) + unset($modes[$m]); + } + } + return $modes; + } + + function getRequirementMode() { + foreach ($this->getAllRequirementModes() as $m=>$info) { + if ($this->get('private') == $info['private'] + && $this->get('required') == $info['required']) + return $m; + } + return false; + } + + function setRequirementMode($mode) { + $modes = $this->getAllRequirementModes(); + if (!isset($modes[$mode])) + return false; + + $info = $modes[$mode]; + $this->set('required', $info['required']); + $this->set('private', $info['private']); + } + + function isRequiredForStaff() { + return in_array($this->get('required'), + array(self::REQUIRE_EVERYONE, self::REQUIRE_AGENT)); + } + function isRequiredForUsers() { + return in_array($this->get('required'), + array(self::REQUIRE_EVERYONE, self::REQUIRE_ENDUSER)); + } + function isVisibleToStaff() { + return in_array($this->get('private'), + array(self::VISIBLE_EVERYONE, self::VISIBLE_AGENTONLY)); + } + function isVisibleToUsers() { + return in_array($this->get('private'), + array(self::VISIBLE_EVERYONE, self::VISIBLE_ENDUSERONLY)); + } + /** * Used when updating the form via the admin panel. This represents * validation on the form field template, not data entered into a form @@ -494,9 +578,13 @@ class DynamicFormField extends VerySimpleModel { if ($this->get('required') && !$this->get('name')) $this->addError( __("Variable name is required for required fields" - /* `required` is a flag on fields */ + /* `required` is a visibility setting fields */ /* `variable` is used for automation. Internally it's called `name` */ ), "name"); + if (preg_match('/[.{}\'"`; ]/u', $this->get('name'))) + $this->addError(__( + 'Invalid character in variable name. Please use letters and numbers only.' + ), 'name'); return count($this->errors()) == 0; } @@ -652,11 +740,16 @@ class DynamicFormEntry extends VerySimpleModel { } function isValidForClient() { - $filter = function($f) { - return !$f->get('private'); + return !$f->isRequiredForUsers(); }; + return $this->isValid($filter); + } + function isValidForStaff() { + $filter = function($f) { + return !$f->isRequiredForStaff(); + }; return $this->isValid($filter); } diff --git a/include/class.forms.php b/include/class.forms.php index 7e9b9409efd14f3b0e9591d25e210b2bf21a741f..090ecbb2686fc0f00200abecac1b380f3e23cb4c 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -86,7 +86,7 @@ class Form { if (!$this->_clean) { $this->_clean = array(); foreach ($this->getFields() as $key=>$field) { - if (!$field->hasData()) + if ($field->isPresentationOnly()) continue; $this->_clean[$key] = $this->_clean[$field->get('name')] = $field->getClean(); @@ -179,6 +179,7 @@ class FormField { 'choices' => array( /* @trans */ 'Choices', 'ChoiceField'), 'files' => array( /* @trans */ 'File Upload', 'FileUploadField'), 'break' => array( /* @trans */ 'Section Break', 'SectionBreakField'), + 'info' => array( /* @trans */ 'Information', 'FreeTextField'), ), ); static $more_types = array(); @@ -1735,7 +1736,9 @@ class TextareaWidget extends Widget { if (isset($config['length']) && $config['length']) $maxlength = "maxlength=\"{$config['length']}\""; if (isset($config['html']) && $config['html']) { - $class = 'class="richtext no-bar small"'; + $class = array('richtext', 'no-bar'); + $class[] = @$config['size'] ?: 'small'; + $class = sprintf('class="%s"', implode(' ', $class)); $this->value = Format::viewableImages($this->value); } ?> @@ -2125,6 +2128,42 @@ class FileUploadWidget extends Widget { class FileUploadError extends Exception {} +class FreeTextField extends FormField { + static $widget = 'FreeTextWidget'; + + function getConfigurationOptions() { + return array( + 'content' => new TextareaField(array( + 'configuration' => array('html' => true, 'size'=>'large'), + 'label'=>__('Content'), 'required'=>true, 'default'=>'', + 'hint'=>__('Free text shown in the form, such as a disclaimer'), + )), + ); + } + + function hasData() { + return false; + } + + function isBlockLevel() { + return true; + } +} + +class FreeTextWidget extends Widget { + function render($mode=false) { + $config = $this->field->getConfiguration(); + ?><div class=""><h3><?php + echo Format::htmlchars($this->field->get('label')); + ?></h3><em><?php + echo Format::htmlchars($this->field->get('hint')); + ?></em><div><?php + echo Format::viewableImages($config['content']); ?></div> + </div> + <?php + } +} + class VisibilityConstraint { const HIDDEN = 0x0001; diff --git a/include/class.user.php b/include/class.user.php index 426ac8eacc4a0eeef6a57194fc952eac4a291c01..1a7317fbaf9ff600d55648c5fe4bf3df165d9cd0 100644 --- a/include/class.user.php +++ b/include/class.user.php @@ -194,12 +194,16 @@ class User extends UserModel { } static function fromForm($form) { + global $thisstaff; if(!$form) return null; //Validate the form $valid = true; - if (!$form->isValid()) + $filter = function($f) use ($thisstaff) { + return !isset($thisstaff) || $f->isRequiredForStaff(); + }; + if (!$form->isValid($filter)) $valid = false; //Make sure the email is not in-use diff --git a/include/client/templates/dynamic-form.tmpl.php b/include/client/templates/dynamic-form.tmpl.php index 57f1aa2951aed93e9e50cf6d6868c7e9f7c15041..12b3944075e71ad3788be4be515fadb5478a56e8 100644 --- a/include/client/templates/dynamic-form.tmpl.php +++ b/include/client/templates/dynamic-form.tmpl.php @@ -16,7 +16,7 @@ // 'private' are not included in the output for clients global $thisclient; foreach ($form->getFields() as $field) { - if ($field->get('private')) + if (!$field->isVisibleToUsers()) continue; ?> <tr> diff --git a/include/i18n/en_US/help/tips/forms.yaml b/include/i18n/en_US/help/tips/forms.yaml index 020bb06911a5cf1594a05f79092d9a6d41144221..2731df424f35bedeee82458aa6f3582da22c0f0a 100644 --- a/include/i18n/en_US/help/tips/forms.yaml +++ b/include/i18n/en_US/help/tips/forms.yaml @@ -48,21 +48,29 @@ field_type: - title: Custom Lists href: /scp/lists.php -field_internal: +field_visibility: title: Field Visibility content: > - Fields marked internal are hidden from your clients. Use internal - fields to track things which only your staff need to access. - -field_required: - title: Data Requirement - content: > - Forms that have required fields must have valid data before the form - can be saved. If checked, forms cannot be submitted or saved until all - required fields are satisfied.<br> - <br> - Internal fields can only be required of staff members, since they - are hidden from clients. + Choose a visibility and requirement option for this field. + <table border="1" cellpadding="2px" cellspacing="0" style="margin-top:7px" + ><tbody style="vertical-align:top;"> + <tr><th>Setting</th> + <th>Result</th></tr> + <tr><td>Optional</td> + <td>Agents and EndUsers can see the field, but neither is required to answer.</td></tr> + <tr><td>Required</td> + <td>Agents and EndUsers can see the field, and both are required to answer</td></tr> + <tr><td>Required for EndUsers</td> + <td>Agents and EndUsers can see the field, only EndUsers are required to answer</td></tr> + <tr><td>Required for Agents</td> + <td>Agents and EndUsers can see the field, only Agents are required to answer</td></tr> + <tr><td>Internal, Optional</td> + <td>Only Agents can see the field, but no answer is required.</td></tr> + <tr><td>Internal, Required</td> + <td>Only Agents can see the field, and an answer is required.</td></tr> + <tr><td>For EndUsers Only</td> + <td>Only EndUsers can see the field, and an answer is required.</td></tr> + </tbody></table> field_variable: title: Field Automation diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php index e0bf6a4552b68b761ff0d78efb3fad9502acf4fa..db879cc5f4690b226bc99370382be28b11fd62ec 100644 --- a/include/staff/dynamic-form.inc.php +++ b/include/staff/dynamic-form.inc.php @@ -69,8 +69,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <th></th> <th><?php echo __('Label'); ?></th> <th><?php echo __('Type'); ?></th> - <th><?php echo __('Internal'); ?></th> - <th><?php echo __('Required'); ?></th> + <th><?php echo __('Visibility'); ?></th> <th><?php echo __('Variable'); ?></th> <th><?php echo __('Delete'); ?></th> </tr> @@ -86,9 +85,11 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <td></td> <td><?php echo $f->get('label'); ?></td> <td><?php $t=FormField::getFieldType($f->get('type')); echo __($t[0]); ?></td> - <td><input type="checkbox" disabled="disabled"/></td> - <td><input type="checkbox" disabled="disabled" - <?php echo $f->get('required') ? 'checked="checked"' : ''; ?>/></td> + <td><?php + $rmode = $f->getRequirementMode(); + $modes = $f->getAllRequirementModes(); + echo $modes[$rmode]['desc']; + ?></td> <td><?php echo $f->get('name'); ?></td> <td><input type="checkbox" disabled="disabled"/></td></tr> @@ -109,10 +110,8 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); <i class="help-tip icon-question-sign" href="#field_label"></i></th> <th nowrap><?php echo __('Type'); ?> <i class="help-tip icon-question-sign" href="#field_type"></i></th> - <th nowrap><?php echo __('Internal'); ?> - <i class="help-tip icon-question-sign" href="#field_internal"></i></th> - <th nowrap><?php echo __('Required'); ?> - <i class="help-tip icon-question-sign" href="#field_required"></i></th> + <th nowrap><?php echo __('Visibility'); ?> + <i class="help-tip icon-question-sign" href="#field_visibility"></i></th> <th nowrap><?php echo __('Variable'); ?> <i class="help-tip icon-question-sign" href="#field_variable"></i></th> <th nowrap><?php echo __('Delete'); ?> @@ -124,8 +123,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); $id = $f->get('id'); $deletable = !$f->isDeletable() ? 'disabled="disabled"' : ''; $force_name = $f->isNameForced() ? 'disabled="disabled"' : ''; - $force_privacy = $f->isPrivacyForced() ? 'disabled="disabled"' : ''; - $force_required = $f->isRequirementForced() ? 'disabled="disabled"' : ''; + $rmode = $f->getRequirementMode(); $fi = $f->getImpl(); $ferrors = $f->errors(); ?> <tr> @@ -157,17 +155,14 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); $.dialog($(this).attr('href').substr(1), [201]); return false; "><i class="icon-edit"></i> <?php echo __('Config'); ?></a> - <?php } ?> - <div class="error" style="white-space:normal"><?php - if ($ferrors['type']) echo $ferrors['type']; - ?></div> - </td> - <td><input type="checkbox" name="private-<?php echo $id; ?>" - <?php if ($f->get('private')) echo 'checked="checked"'; ?> - <?php echo $force_privacy ?>/></td> - <td><input type="checkbox" name="required-<?php echo $id; ?>" - <?php if ($f->get('required')) echo 'checked="checked"'; ?> - <?php echo $force_required ?>/> + <?php } ?></td> + <td> + <select name="visibility-<?php echo $id; ?>"> +<?php foreach ($f->getAllRequirementModes() as $m=>$I) { ?> + <option value="<?php echo $m; ?>" <?php if ($rmode == $m) + echo 'selected="selected"'; ?>><?php echo $I['desc']; ?></option> +<?php } ?> + <select> </td> <td> <input type="text" size="20" name="name-<?php echo $id; ?>" @@ -206,12 +201,15 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); </optgroup> <?php } ?> </select></td> - <td><input type="checkbox" name="private-new-<?php echo $i; ?>" - <?php if ($info["private-new-$i"] - || (!$_POST && $form && $form->get('type') == 'U')) - echo 'checked="checked"'; ?>/></td> - <td><input type="checkbox" name="required-new-<?php echo $i; ?>" - <?php if ($info["required-new-$i"]) echo 'checked="checked"'; ?>/></td> + <td> + <select name="visibility-new-<?php echo $i; ?>"> +<?php + $rmode = $info['visibility-new-'.$i]; + foreach (DynamicFormField::allRequirementModes() as $m=>$I) { ?> + <option value="<?php echo $m; ?>" <?php if ($rmode == $m) + echo 'selected="selected"'; ?>><?php echo $I['desc']; ?></option> +<?php } ?> + <select> <td><input type="text" size="20" name="name-new-<?php echo $i; ?>" value="<?php echo $info["name-new-$i"]; ?>"/> <font class="error"><?php diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php index 79ebe981bad0798a851d2e61285be0f38613c5d3..611306d1ba831a32f54a1cca0a1d2bfe7b44b7dd 100644 --- a/include/staff/templates/dynamic-form.tmpl.php +++ b/include/staff/templates/dynamic-form.tmpl.php @@ -39,6 +39,13 @@ if (isset($options['entry']) && $options['mode'] == 'edit') { ?> <?php } foreach ($form->getFields() as $field) { + try { + if (!$field->isVisibleToStaff()) + continue; + } + catch (Exception $e) { + // Not connected to a DynamicFormField + } ?> <tr><?php if ($field->isBlockLevel()) { ?> <td colspan="2"> diff --git a/js/redactor-osticket.js b/js/redactor-osticket.js index 810fa7f9e98945d6252112e40ad2fe699debe6ed..cded2e1f3ffdc7d425428155eec3cf2a0815777d 100644 --- a/js/redactor-osticket.js +++ b/js/redactor-osticket.js @@ -214,7 +214,12 @@ $(function() { }, redact = $.redact = function(el, options) { var el = $(el), - options = $.extend({ + sizes = {'small': 75, 'medium': 150, 'large': 225}, + selectedSize = sizes['medium']; + $.each(sizes, function(k, v) { + if (el.hasClass(k)) selectedSize = v; + }); + var options = $.extend({ 'air': el.hasClass('no-bar'), 'airButtons': ['formatting', '|', 'bold', 'italic', 'underline', 'deleted', '|', 'unorderedlist', 'orderedlist', 'outdent', 'indent', '|', 'image'], 'buttons': ['html', '|', 'formatting', '|', 'bold', @@ -223,7 +228,7 @@ $(function() { 'file', 'table', 'link', '|', 'alignment', '|', 'horizontalrule'], 'autoresize': !el.hasClass('no-bar'), - 'minHeight': el.hasClass('small') ? 75 : 150, + 'minHeight': selectedSize, 'focus': false, 'plugins': [], 'imageGetJson': 'ajax.php/draft/images/browse', diff --git a/scp/forms.php b/scp/forms.php index dae8ecaf71849f4e3558f2d34f3c181ddad593e1..8b315061435edc03f5f8f4a824e5bff0088bafb4 100644 --- a/scp/forms.php +++ b/scp/forms.php @@ -38,10 +38,8 @@ if($_POST) { if (isset($_POST["name-$id"]) && !$field->isNameForced()) $field->set('name', $_POST["name-$id"]); # TODO: make sure all help topics still have all required fields - if (!$field->isRequirementForced()) - $field->set('required', $_POST["required-$id"] == 'on' ? 1 : 0); - if (!$field->isPrivacyForced()) - $field->set('private', $_POST["private-$id"] == 'on' ? 1 : 0); + $field->setRequirementMode($_POST["visibility-$id"]); + foreach (array('sort','label') as $f) { if (isset($_POST["$f-$id"])) { $field->set($f, $_POST["$f-$id"]); @@ -49,8 +47,6 @@ if($_POST) { } if (in_array($field->get('name'), $names)) $field->addError(__('Field variable name is not unique'), 'name'); - if (preg_match('/[.{}\'"`; ]/u', $field->get('name'))) - $field->addError(__('Invalid character in variable name. Please use letters and numbers only.'), 'name'); // Subject (Issue Summary) must always have data if ($form->get('type') == 'T' && $field->get('name') == 'subject') { if (($f = $field->getField(false)->getImpl()) && !$f->hasData()) @@ -115,12 +111,15 @@ if($_POST) { 'label'=>$_POST["label-new-$i"], 'type'=>$_POST["type-new-$i"], 'name'=>$_POST["name-new-$i"], - 'private'=>$_POST["private-new-$i"] == 'on' ? 1 : 0, - 'required'=>$_POST["required-new-$i"] == 'on' ? 1 : 0 )); + $field->setRequirementMode($_POST["visibility-new-$i"]); $field->setForm($form); - if ($field->isValid()) + if (in_array($field->get('name'), $names)) + $field->addError(__('Field variable name is not unique'), 'name'); + if ($field->isValid()) { $form_fields[] = $field; + $names[] = $field->get('name'); + } else $errors["new-$i"] = $field->errors(); }