From e642c5508620bf90f32e9bb09dad34cf6c31e3d6 Mon Sep 17 00:00:00 2001 From: Jared Hancock <jared@osticket.com> Date: Thu, 4 Dec 2014 11:14:25 -0600 Subject: [PATCH] forms: Implement more granular visibility settings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Allow fields to be configured for view / edit / required for both agents and end users. Fields can also be disabled now so that the field remains in the form but is no longer displayed for new entries. Allow tickets to be created without a subject — use the help topic full name instead. --- account.php | 2 +- include/Spyc.php | 2 + include/ajax.forms.php | 26 ++- include/class.auth.php | 2 +- include/class.config.php | 5 - include/class.dynamic_forms.php | 137 +++++++++++----- include/class.forms.php | 5 + include/class.ticket.php | 23 ++- .../client/templates/dynamic-form.tmpl.php | 14 +- include/client/view.inc.php | 8 +- include/i18n/en_US/form.yaml | 68 +++----- include/i18n/en_US/list.yaml | 6 +- include/staff/dynamic-form.inc.php | 16 +- include/staff/settings-tickets.inc.php | 8 - .../templates/dynamic-field-config.tmpl.php | 152 ++++++++++++++++++ include/staff/templates/dynamic-form.tmpl.php | 6 +- tickets.php | 4 +- 17 files changed, 345 insertions(+), 139 deletions(-) diff --git a/account.php b/account.php index 97c8b5efd..81f542eb2 100644 --- a/account.php +++ b/account.php @@ -58,7 +58,7 @@ elseif ($_POST) { $user_form->getField('email')->value = $thisclient->getEmail(); } - if (!$user_form->isValid(function($f) { return !$f->get('private'); })) + if (!$user_form->isValid(function($f) { return !$f->isVisibleToUsers(); })) $errors['err'] = __('Incomplete client information'); elseif (!$_POST['backend'] && !$_POST['passwd1']) $errors['passwd1'] = __('New password is required'); diff --git a/include/Spyc.php b/include/Spyc.php index 1c0fc31a8..e92a4bece 100644 --- a/include/Spyc.php +++ b/include/Spyc.php @@ -617,6 +617,8 @@ class Spyc { if (is_numeric($value)) { if ($value === '0') return 0; + if (stripos($value, '0x') === 0) + $value = hexdec($value); if (rtrim ($value, 0) === $value) $value = (float)$value; return $value; diff --git a/include/ajax.forms.php b/include/ajax.forms.php index e53b91348..6c35f1bb2 100644 --- a/include/ajax.forms.php +++ b/include/ajax.forms.php @@ -48,7 +48,31 @@ class DynamicFormsAjaxAPI extends AjaxController { } function saveFieldConfiguration($field_id) { - $field = DynamicFormField::lookup($field_id); + if (!($field = DynamicFormField::lookup($field_id))) + Http::response(404, 'No such field'); + + $DFF = 'DynamicFormField'; + + // Capture flags which should remain unchanged + $p_mask = $DFF::MASK_MASK_ALL; + if ($field->isPrivacyForced()) { + $p_mask |= $DFF::FLAG_CLIENT_VIEW | $DFF::FLAG_AGENT_VIEW; + } + if ($field->isRequirementForced()) { + $p_mask |= $DFF::FLAG_CLIENT_REQUIRED | $DFF::FLAG_AGENT_REQUIRED; + } + if ($field->hasFlag($DFF::FLAG_MASK_DISABLE)) { + $p_mask |= $DFF::FLAG_ENABLED; + } + + // Capture current state of immutable flags + $preserve = $field->flags & $p_mask; + + // Set admin-configured flag states + $flags = array_reduce($_POST['flags'], + function($a, $b) { return $a | $b; }, 0); + $field->flags = $flags | $preserve; + if (!$field->setConfiguration()) { include STAFFINC_DIR . 'templates/dynamic-field-config.tmpl.php'; return; diff --git a/include/class.auth.php b/include/class.auth.php index fb055a5c3..e6fed10c5 100644 --- a/include/class.auth.php +++ b/include/class.auth.php @@ -130,7 +130,7 @@ class ClientCreateRequest { if ($bk->supportsInteractiveAuthentication()) // User can only be authenticated against this backend $defaults['backend'] = $bk::$id; - if ($this_form->isValid(function($f) { return !$f->get('private'); }) + if ($this_form->isValid(function($f) { return !$f->isVisibleToUsers(); }) && ($U = User::fromVars($this_form->getClean())) && ($acct = ClientAccount::createForUser($U, $defaults)) // Confirm and save the account diff --git a/include/class.config.php b/include/class.config.php index d47d4144f..164171a92 100644 --- a/include/class.config.php +++ b/include/class.config.php @@ -156,7 +156,6 @@ class OsticketConfig extends Config { 'auto_claim_tickets'=> true, 'system_language' => 'en_US', 'default_storage_bk' => 'D', - 'allow_client_updates' => false, 'message_autoresponder_collabs' => true, 'add_email_collabs' => true, 'clients_only' => false, @@ -350,10 +349,6 @@ class OsticketConfig extends Config { return $this->get('enable_html_thread'); } - function allowClientUpdates() { - return $this->get('allow_client_updates'); - } - function getClientTimeout() { return $this->getClientSessionTimeout(); } diff --git a/include/class.dynamic_forms.php b/include/class.dynamic_forms.php index 4bed6b6fd..3620cc1e8 100644 --- a/include/class.dynamic_forms.php +++ b/include/class.dynamic_forms.php @@ -423,14 +423,27 @@ class DynamicFormField extends VerySimpleModel { var $_field; - const REQUIRE_NOBODY = 0; - const REQUIRE_EVERYONE = 1; - const REQUIRE_ENDUSER = 2; - const REQUIRE_AGENT = 3; + const FLAG_ENABLED = 0x0001; + const FLAG_STORABLE = 0x0002; + const FLAG_CLOSE_REQUIRED = 0x0004; - const VISIBLE_EVERYONE = 0; - const VISIBLE_AGENTONLY = 1; - const VISIBLE_ENDUSERONLY = 2; + const FLAG_MASK_CHANGE = 0x0010; + const FLAG_MASK_DELETE = 0x0020; + const FLAG_MASK_EDIT = 0x0040; + const FLAG_MASK_DISABLE = 0x0080; + const FLAG_MASK_REQUIRE = 0x10000; + const FLAG_MASK_VIEW = 0x20000; + const FLAG_MASK_NAME = 0x40000; + + const MASK_MASK_ALL = 0x700F0; + + const FLAG_CLIENT_VIEW = 0x0100; + const FLAG_CLIENT_EDIT = 0x0200; + const FLAG_CLIENT_REQUIRED = 0x0400; + + const FLAG_AGENT_VIEW = 0x1000; + const FLAG_AGENT_EDIT = 0x2000; + const FLAG_AGENT_REQUIRED = 0x4000; // Multiple inheritance -- delegate to FormField function __call($what, $args) { @@ -481,24 +494,61 @@ class DynamicFormField extends VerySimpleModel { } function isDeletable() { - return (($this->get('edit_mask') & 1) == 0); + return !$this->hasFlag(self::FLAG_MASK_DELETE); } function isNameForced() { - return $this->get('edit_mask') & 2; + return $this->hasFlag(self::FLAG_MASK_NAME); } function isPrivacyForced() { - return $this->get('edit_mask') & 4; + return $this->hasFlag(self::FLAG_MASK_VIEW); } function isRequirementForced() { - return $this->get('edit_mask') & 8; + return $this->hasFlag(self::FLAG_MASK_REQUIRE); } function isChangeable() { - return (($this->get('edit_mask') & 16) == 0); + return $this->hasFlag(self::FLAG_MASK_CHANGE); } function isEditable() { - return (($this->get('edit_mask') & 32) == 0); + return $this->hasFlag(self::FLAG_MASK_EDIT); + } + + function hasFlag($flag) { + return ($this->flags & $flag) != 0; + } + + function getVisibilityDescription() { + $F = $this->flags; + + if (!$this->hasFlag(self::FLAG_ENABLED)) + return __('Disabled'); + + $impl = $this->getImpl(); + + $hints = array(); + $VIEW = self::FLAG_CLIENT_VIEW | self::FLAG_AGENT_VIEW; + if (($F & $VIEW) == 0) { + $hints[] = __('Hidden'); + } + elseif (~$F & self::FLAG_CLIENT_VIEW) { + $hints[] = __('Internal'); + } + elseif (~$F & self::FLAG_AGENT_VIEW) { + $hints[] = __('For EndUsers Only'); + } + if ($impl->hasData()) { + if (~$F & (self::FLAG_CLIENT_REQUIRED | self::FLAG_AGENT_REQUIRED)) { + $hints[] = __('Optional'); + } + else { + $hints[] = __('Required'); + } + if (!($F & (self::FLAG_CLIENT_EDIT | self::FLAG_AGENT_EDIT))) { + $hints[] = __('Immutable'); + } + } + return implode(', ', $hints); } function getTranslateTag($subtag) { return _H(sprintf('field.%s.%s', $subtag, $this->id)); @@ -512,19 +562,28 @@ class DynamicFormField extends VerySimpleModel { function allRequirementModes() { return array( 'a' => array('desc' => __('Optional'), - 'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_NOBODY), + 'flags' => self::FLAG_CLIENT_VIEW | self::FLAG_AGENT_VIEW + | self::FLAG_CLIENT_EDIT | self::FLAG_AGENT_EDIT), 'b' => array('desc' => __('Required'), - 'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_EVERYONE), + 'flags' => self::FLAG_CLIENT_VIEW | self::FLAG_AGENT_VIEW + | self::FLAG_CLIENT_EDIT | self::FLAG_AGENT_EDIT + | self::FLAG_CLIENT_REQUIRED | self::FLAG_AGENT_REQUIRED), 'c' => array('desc' => __('Required for EndUsers'), - 'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_ENDUSER), + 'flags' => self::FLAG_CLIENT_VIEW | self::FLAG_AGENT_VIEW + | self::FLAG_CLIENT_EDIT | self::FLAG_AGENT_EDIT + | self::FLAG_CLIENT_REQUIRED), 'd' => array('desc' => __('Required for Agents'), - 'private' => self::VISIBLE_EVERYONE, 'required' => self::REQUIRE_AGENT), + 'flags' => self::FLAG_CLIENT_VIEW | self::FLAG_AGENT_VIEW + | self::FLAG_CLIENT_EDIT | self::FLAG_AGENT_EDIT + | self::FLAG_AGENT_REQUIRED), 'e' => array('desc' => __('Internal, Optional'), - 'private' => self::VISIBLE_AGENTONLY, 'required' => self::REQUIRE_NOBODY), + 'flags' => self::FLAG_AGENT_VIEW | self::FLAG_AGENT_EDIT), 'f' => array('desc' => __('Internal, Required'), - 'private' => self::VISIBLE_AGENTONLY, 'required' => self::REQUIRE_EVERYONE), + 'flags' => self::FLAG_AGENT_VIEW | self::FLAG_AGENT_EDIT + | self::FLAG_AGENT_REQUIRED), 'g' => array('desc' => __('For EndUsers Only'), - 'private' => self::VISIBLE_ENDUSERONLY, 'required' => self::REQUIRE_ENDUSER), + 'flags' => self::FLAG_CLIENT_VIEW | self::FLAG_CLIENT_EDIT + | self::FLAG_CLIENT_REQUIRED), ); } @@ -533,7 +592,7 @@ class DynamicFormField extends VerySimpleModel { if ($this->isPrivacyForced()) { // Required to be internal foreach ($modes as $m=>$info) { - if ($info['private'] != $this->get('private')) + if ($info['flags'] & (self::FLAG_CLIENT_VIEW | self::FLAG_AGENT_VIEW)) unset($modes[$m]); } } @@ -541,47 +600,43 @@ class DynamicFormField extends VerySimpleModel { if ($this->isRequirementForced()) { // Required to be required foreach ($modes as $m=>$info) { - if ($info['required'] != $this->get('required')) + if ($info['flags'] & (self::FLAG_CLIENT_REQUIRED | self::FLAG_AGENT_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']); + $this->set('flags', $info['flags']); } function isRequiredForStaff() { - return in_array($this->get('required'), - array(self::REQUIRE_EVERYONE, self::REQUIRE_AGENT)); + return $this->hasFlag(self::FLAG_AGENT_REQUIRED); } function isRequiredForUsers() { - return in_array($this->get('required'), - array(self::REQUIRE_EVERYONE, self::REQUIRE_ENDUSER)); + return $this->hasFlag(self::FLAG_CLIENT_REQUIRED); + } + function isEditableToStaff() { + return $this->hasFlag(self::FLAG_ENABLED) + && $this->hasFlag(self::FLAG_AGENT_EDIT); } function isVisibleToStaff() { - return in_array($this->get('private'), - array(self::VISIBLE_EVERYONE, self::VISIBLE_AGENTONLY)); + return $this->hasFlag(self::FLAG_ENABLED) + && $this->hasFlag(self::FLAG_AGENT_VIEW); + } + function isEditableToUsers() { + return $this->hasFlag(self::FLAG_ENABLED) + && $this->hasFlag(self::FLAG_CLIENT_EDIT); } function isVisibleToUsers() { - return in_array($this->get('private'), - array(self::VISIBLE_EVERYONE, self::VISIBLE_ENDUSERONLY)); + return $this->hasFlag(self::FLAG_ENABLED) + && $this->hasFlag(self::FLAG_CLIENT_VIEW); } /** diff --git a/include/class.forms.php b/include/class.forms.php index 7cebfb42f..3410477d7 100644 --- a/include/class.forms.php +++ b/include/class.forms.php @@ -1448,6 +1448,11 @@ class ThreadEntryField extends FormField { if ($cfg->getAllowedFileTypes()) $fileupload_config['extensions']->set('default', $cfg->getAllowedFileTypes()); + foreach ($fileupload_config as $C) { + $C->set('visibility', new VisibilityConstraint(new Q(array( + 'attachments__eq'=>true, + )), VisibilityConstraint::HIDDEN)); + } return array( 'attachments' => new BooleanField(array( 'label'=>__('Enable Attachments'), diff --git a/include/class.ticket.php b/include/class.ticket.php index 7caac30d9..33400acbd 100644 --- a/include/class.ticket.php +++ b/include/class.ticket.php @@ -791,6 +791,15 @@ class Ticket { return $this->recipients; } + function hasClientEditableFields() { + $forms = DynamicFormEntry::forTicket($this->getId()); + foreach ($forms as $form) { + foreach ($form->getFields() as $field) { + if ($field->isEditableToUsers()) + return true; + } + } + } function addCollaborator($user, $vars, &$errors) { @@ -2221,7 +2230,7 @@ class Ticket { if (!in_array($form->getId(), $vars['forms'])) continue; $form->setSource($_POST); - if (!$form->isValid()) + if (!$form->isValidForStaff()) $errors = array_merge($errors, $form->errors()); } @@ -2456,10 +2465,9 @@ class Ticket { case 'staff': // Required 'Contact Information' fields aren't required // when staff open tickets - return $type != 'user' - || in_array($f->get('name'), array('name','email')); + return $f->isVisibleToStaff(); case 'web': - return !$f->get('private'); + return $f->isVisibleToUsers(); default: return true; } @@ -2772,6 +2780,13 @@ class Ticket { /* -------------------- POST CREATE ------------------------ */ // Save the (common) dynamic form + // Ensure we have a subject + $subject = $form->getAnswer('subject'); + if ($subject && !$subject->getValue()) { + if ($topic) { + $form->setAnswer('subject', $topic->getFullName()); + } + } $form->setTicketId($id); $form->save(); diff --git a/include/client/templates/dynamic-form.tmpl.php b/include/client/templates/dynamic-form.tmpl.php index 66b957a49..0672b2263 100644 --- a/include/client/templates/dynamic-form.tmpl.php +++ b/include/client/templates/dynamic-form.tmpl.php @@ -5,7 +5,7 @@ ?> <tr><td colspan="2"><hr /> <div class="form-header" style="margin-bottom:0.5em"> - <?php print ($form instanceof DynamicFormEntry) + <?php print ($form instanceof DynamicFormEntry) ? $form->getForm()->getMedia() : $form->getMedia(); ?> <h3><?php echo Format::htmlchars($form->getTitle()); ?></h3> <em><?php echo Format::htmlchars($form->getInstructions()); ?></em> @@ -16,19 +16,18 @@ // 'private' are not included in the output for clients global $thisclient; foreach ($form->getFields() as $field) { - if (!$field->isVisibleToUsers()) + if (!$field->isEditableToUsers()) continue; ?> <tr> <td colspan="2" style="padding-top:8px;"> <?php if (!$field->isBlockLevel()) { ?> <label for="<?php echo $field->getFormName(); ?>"><span class="<?php - if ($field->get('required')) echo 'required'; ?>"> + if ($field->isRequiredForUsers()) echo 'required'; ?>"> <?php echo Format::htmlchars($field->getLocal('label')); ?> - <?php if ($field->get('required')) { ?> + <?php if ($field->isRequiredForUsers()) { ?> <span class="error">*</span> - <?php - } + <?php } ?></span><?php if ($field->get('hint')) { ?> <br /><em style="color:gray;display:inline-block"><?php @@ -41,8 +40,7 @@ $field->render('client'); ?></label><?php foreach ($field->errors() as $e) { ?> - <br /> - <font class="error"><?php echo $e; ?></font> + <div class="error"><?php echo $e; ?></div> <?php } $field->renderExtras('client'); ?> diff --git a/include/client/view.inc.php b/include/client/view.inc.php index cd0b2ccd3..6fb76a9d9 100644 --- a/include/client/view.inc.php +++ b/include/client/view.inc.php @@ -32,12 +32,12 @@ if ($thisclient && $thisclient->isGuest() <td colspan="2" width="100%"> <h1> <?php echo sprintf(__('Ticket #%s'), $ticket->getNumber()); ?> - <a href="tickets.php?id=<?php echo $ticket->getId(); ?>" title="Reload"><span class="Icon refresh"> </span></a> -<?php if ($cfg->allowClientUpdates() + <a href="tickets.php?id=<?php echo $ticket->getId(); ?>" title="<?php echo __('Reload'); ?>"><span class="Icon refresh"> </span></a> +<?php if ($ticket->hasClientEditableFields() // Only ticket owners can edit the ticket details (and other forms) && $thisclient->getId() == $ticket->getUserId()) { ?> <a class="action-button pull-right" href="tickets.php?a=edit&id=<?php - echo $ticket->getId(); ?>"><i class="icon-edit"></i> Edit</a> + echo $ticket->getId(); ?>"><i class="icon-edit"></i> <?php echo __('Edit'); ?></a> <?php } ?> </h1> </td> @@ -88,7 +88,7 @@ foreach (DynamicFormEntry::forTicket($ticket->getId()) as $idx=>$form) { <?php foreach ($answers as $answer) { if (in_array($answer->getField()->get('name'), array('name', 'email', 'subject'))) continue; - elseif ($answer->getField()->get('private')) + elseif (!$answer->getField()->isVisibleToUsers()) continue; ?> <tr> diff --git a/include/i18n/en_US/form.yaml b/include/i18n/en_US/form.yaml index d8759c8fc..85c8fc00a 100644 --- a/include/i18n/en_US/form.yaml +++ b/include/i18n/en_US/form.yaml @@ -16,10 +16,15 @@ # will be used to retrieve the data from the field. # hint: Help text shown with the field # flags: Bit mask for settings & options -# edit_mask: Mask out edits to the field (1=>delete, 2=>change name, -# 4=>privacy setting, 8=>requirement setting) -# private: True if the field should be hidden from the client -# required: True if entry for the field is required +# # From class DynamicFormField +# const FLAG_MASK_CHANGE = 0x0010; # Type cannot change +# const FLAG_MASK_DELETE = 0x0020; # Cannot be deleted +# const FLAG_MASK_EDIT = 0x0040; # Data cannot be edited +# const FLAG_MASK_DISABLE = 0x0080; # Field cannot be disabled +# const FLAG_MASK_REQUIRE = 0x10000; # Requirement cannot be changed +# const FLAG_MASK_VIEW = 0x20000; # View settings cannot be changed +# const FLAG_MASK_NAME = 0x40000; # Name cannot be changed +# # configuration: Field-specific configuration # size: (text) width of the field # length: (text) maximum size of the data in the field @@ -35,10 +40,8 @@ - type: text # notrans name: email # notrans label: Email Address - required: true sort: 1 - flags: 3 - edit_mask: 15 + flags: 0x777A3 configuration: size: 40 length: 64 @@ -46,25 +49,21 @@ - type: text # notrans name: name # notrans label: Full Name - required: true sort: 2 - flags: 3 - edit_mask: 15 + flags: 0x777A3 configuration: size: 40 length: 64 - type: phone # notrans name: phone # notrans label: Phone Number - required: false sort: 3 - flags: 1 + flags: 0x3301 - type: memo # notrans name: notes label: Internal Notes - required: false - private: true sort: 4 + flags: 0x3001 configuration: rows: 4 cols: 40 @@ -82,10 +81,8 @@ type: text # notrans name: subject # notrans label: Issue Summary - required: true - edit_mask: 15 sort: 1 - flags: 1 + flags: 0x77721 configuration: size: 40 length: 50 @@ -94,18 +91,13 @@ name: message # notrans label: Issue Details hint: Details on the reason(s) for opening the ticket. - required: true - edit_mask: 15 sort: 2 - flags: 3 + flags: 0x75523 - id: 22 type: priority # notrans name: priority # notrans label: Priority Level - required: false - private: true - edit_mask: 3 - sort: 3 + sort: 0x430A3 flags: 1 - type: C # notrans title: Company Information @@ -115,10 +107,8 @@ - type: text # notrans name: name # notrans label: Company Name - required: true sort: 1 - flags: 1 - edit_mask: 3 + flags: 0x471A1 configuration: size: 40 length: 64 @@ -126,24 +116,22 @@ name: website # notrans label: Website sort: 2 - flags: 1 + flags: 0x3101 configuration: size: 40 length: 64 - type: phone # notrans name: phone # notrans label: Phone Number - required: false sort: 3 - flags: 1 + flags: 0x3101 configuration: ext: false - type: memo # notrans name: address label: Address - required: false sort: 4 - flags: 1 + flags: 0x3101 configuration: rows: 2 cols: 40 @@ -157,19 +145,16 @@ - type: text # notrans name: name # notrans label: Name - required: true sort: 1 - flags: 3 - edit_mask: 15 + flags: 0x777A3 configuration: size: 40 length: 64 - type: memo name: address label: Address - required: false sort: 2 - flags: 1 + flags: 0x3301 configuration: rows: 2 cols: 40 @@ -178,24 +163,21 @@ - type: phone name: phone label: Phone - required: false sort: 3 - flags: 1 + flags: 0x3301 - type: text name: website label: Website - required: false sort: 4 - flags: 1 + flags: 0x3301 configuration: size: 40 length: 0 - type: memo # notrans name: notes label: Internal Notes - required: false sort: 5 - flags: 1 + flags: 0x3001 configuration: rows: 4 cols: 40 diff --git a/include/i18n/en_US/list.yaml b/include/i18n/en_US/list.yaml index 613ca4a2f..2dbd2e7a4 100644 --- a/include/i18n/en_US/list.yaml +++ b/include/i18n/en_US/list.yaml @@ -37,17 +37,15 @@ - type: state # notrans name: state # notrans label: State - required: true sort: 1 - edit_mask: 63 + flags: 0x770F1 configuration: prompt: State of a ticket - type: memo # notrans name: description # notrans label: Description - required: false sort: 3 - edit_mask: 15 + flags: 0x73021 configuration: rows: 2 cols: 40 diff --git a/include/staff/dynamic-form.inc.php b/include/staff/dynamic-form.inc.php index b79a4320d..bf68e752f 100644 --- a/include/staff/dynamic-form.inc.php +++ b/include/staff/dynamic-form.inc.php @@ -83,17 +83,13 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); $uform = UserForm::objects()->all(); $ftypes = FormField::allTypes(); foreach ($uform[0]->getFields() as $f) { - if ($f->get('private')) continue; + if (!$f->isVisibleToUsers()) continue; ?> <tr> <td></td> <td><?php echo $f->get('label'); ?></td> <td><?php $t=FormField::getFieldType($f->get('type')); echo __($t[0]); ?></td> - <td><?php - $rmode = $f->getRequirementMode(); - $modes = $f->getAllRequirementModes(); - echo $modes[$rmode]['desc']; - ?></td> + <td><?php echo $f->getVisibilityDescription(); ?></td> <td><?php echo $f->get('name'); ?></td> <td><input type="checkbox" disabled="disabled"/></td></tr> @@ -127,7 +123,6 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); $id = $f->get('id'); $deletable = !$f->isDeletable() ? 'disabled="disabled"' : ''; $force_name = $f->isNameForced() ? 'disabled="disabled"' : ''; - $rmode = $f->getRequirementMode(); $fi = $f->getImpl(); $ferrors = $f->errors(); ?> <tr> @@ -162,12 +157,7 @@ $info=Format::htmlchars(($errors && $_POST)?$_POST:$info); "><i class="icon-edit"></i> <?php echo __('Config'); ?></a> <?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> + <?php echo $f->getVisibilityDescription(); ?> </td> <td> <input type="text" size="20" name="name-<?php echo $id; ?>" diff --git a/include/staff/settings-tickets.inc.php b/include/staff/settings-tickets.inc.php index d64c52ed1..603f43dfa 100644 --- a/include/staff/settings-tickets.inc.php +++ b/include/staff/settings-tickets.inc.php @@ -200,14 +200,6 @@ if(!($maxfileuploads=ini_get('max_file_uploads'))) <i class="help-tip icon-question-sign" href="#enable_html_ticket_thread"></i> </td> </tr> - <tr> - <td><?php echo __('Allow Client Updates'); ?>:</td> - <td> - <input type="checkbox" name="allow_client_updates" <?php - echo $config['allow_client_updates']?'checked="checked"':''; ?>> - <?php echo __('Allow clients to update ticket details via the web portal'); ?> - </td> - </tr> <tr> <th colspan="2"> <em><b><?php echo __('Attachments');?></b>: <?php echo __('Size and maximum uploads setting mainly apply to web tickets.');?></em> diff --git a/include/staff/templates/dynamic-field-config.tmpl.php b/include/staff/templates/dynamic-field-config.tmpl.php index d9db95352..65932bf65 100644 --- a/include/staff/templates/dynamic-field-config.tmpl.php +++ b/include/staff/templates/dynamic-field-config.tmpl.php @@ -3,6 +3,123 @@ <hr/> <form method="post" action="#form/field-config/<?php echo $field->get('id'); ?>"> +<ul class="tabs"> + <li><a href="#config" class="active"><i class="icon-cogs"></i> Field Setup</a></li> + <li><a href="#visibility"><i class="icon-beaker"></i> Settings</a></li> +</ul> + +<div class="tab_content" id="visibility" style="display:none"> + <div> + <div class="span4"> + <div style="margin-bottom:5px"><strong>Enabled</strong> + <i class="help-tip icon-question-sign" + data-title="Enabled" + data-content="This field can be disabled which will remove it + from the form for new entries, but will preserve the data on all + current entries."></i> + </div> + </div> + <div class="span6"> + <input type="checkbox" name="flags[]" value="<?php + echo DynamicFormField::FLAG_ENABLED; ?>" <?php + if ($field->hasFlag(DynamicFormField::FLAG_ENABLED)) echo 'checked="checked"'; + if ($field->hasFlag(DynamicFormField::FLAG_MASK_DISABLE)) echo ' disabled="disabled"'; + ?>> Enabled<br/> + </div> + <hr class="faded"/> + + <div class="span4"> + <div style="margin-bottom:5px"><strong>Visible</strong> + <i class="help-tip icon-question-sign" + data-title="Visible" + data-content="Making fields <em>visible</em> allows agents and + endusers to view and create information in this field."></i> + </div> + </div> + <div class="span3"> + <input type="checkbox" name="flags[]" value="<?php + echo DynamicFormField::FLAG_CLIENT_VIEW; ?>" <?php + if ($field->hasFlag(DynamicFormField::FLAG_CLIENT_VIEW)) echo 'checked="checked"'; + if ($field->isPrivacyForced()) echo ' disabled="disabled"'; + ?>> For Clients<br/> + </div> + <div class="span3"> + <input type="checkbox" name="flags[]" value="<?php + echo DynamicFormField::FLAG_AGENT_VIEW; ?>" <?php + if ($field->hasFlag(DynamicFormField::FLAG_AGENT_VIEW)) echo 'checked="checked"'; + if ($field->isPrivacyForced()) echo ' disabled="disabled"'; + ?>> For Agents<br/> + </div> + +<?php if ($field->getImpl()->hasData()) { ?> + <hr class="faded"/> + + <div class="span4"> + <div style="margin-bottom:5px"><strong>Required</strong> + <i class="help-tip icon-question-sign" + data-title="Required" + data-content="New entries cannot be created unless all + <em>required</em> fields have valid data."></i> + </div> + </div> + <div class="span3"> + <input type="checkbox" name="flags[]" value="<?php + echo DynamicFormField::FLAG_CLIENT_REQUIRED; ?>" <?php + if ($field->hasFlag(DynamicFormField::FLAG_CLIENT_REQUIRED)) echo 'checked="checked"'; + if ($field->isRequirementForced()) echo ' disabled="disabled"'; + ?>> For Clients<br/> + </div> + <div class="span3"> + <input type="checkbox" name="flags[]" value="<?php + echo DynamicFormField::FLAG_AGENT_REQUIRED; ?>" <?php + if ($field->hasFlag(DynamicFormField::FLAG_AGENT_REQUIRED)) echo 'checked="checked"'; + if ($field->isRequirementForced()) echo ' disabled="disabled"'; + ?>> For Agents<br/> + </div> + <hr class="faded"/> + + <div class="span4"> + <div style="margin-bottom:5px"><strong>Editable</strong> + <i class="help-tip icon-question-sign" + data-content="Fields marked editable allow agents and endusers to update the + content of this field after the form entry has been created." + data-title="Editable"></i> + </div> + </div> + + <div class="span3"> + <input type="checkbox" name="flags[]" value="<?php + echo DynamicFormField::FLAG_CLIENT_EDIT; ?>" <?php + if ($field->hasFlag(DynamicFormField::FLAG_CLIENT_EDIT)) echo 'checked="checked"'; + ?>> For Clients<br/> + </div> + <div class="span3"> + <input type="checkbox" name="flags[]" value="<?php + echo DynamicFormField::FLAG_AGENT_EDIT; ?>" <?php + if ($field->hasFlag(DynamicFormField::FLAG_AGENT_EDIT)) echo 'checked="checked"'; + ?>> For Agents<br/> + </div> + <hr class="faded"/> + + <div class="span4"> + <div style="margin-bottom:5px"><strong>Data Integrity</strong> + <i class="help-tip icon-question-sign" + data-title="Required to close a ticket" + data-content="Optionally, this field can prevent closing a + ticket until it has valid data."></i> + </div> + </div> + <div class="span6"> + <input type="checkbox" name="flags[]" value="<?php + echo DynamicFormField::FLAG_CLOSE_REQUIRED; ?>" <?php + if ($field->hasFlag(DynamicFormField::FLAG_CLOSE_REQUIRED)) echo 'checked="checked"'; + ?>> Required to close a ticket<br/> + </div> +<?php } ?> + </div> +</div> + +<div class="tab_content" id="config"> <?php echo csrf_token(); $form = $field->getConfigurationForm(); @@ -50,6 +167,7 @@ echo Format::htmlchars($field->get('hint')); ?></textarea> </div> </div> +</div> <hr> <p class="full-width"> <span class="buttons pull-left"> @@ -62,7 +180,41 @@ </p> </form> <div class="clear"></div> + <script type="text/javascript"> // Make translatable fields translatable $('input[data-translate-tag], textarea[data-translate-tag]').translatable(); </script> + +<style type="text/css"> +.span3 { + width: 22.25%; + margin: 0 1%; + display: inline-block; + vertical-align: top; +} +.span4 { + width: 30.25%; + margin: 0 1%; + display: inline-block; + vertical-align: top; +} +.span6 { + width: 47.25%; + margin: 0 1%; + display: inline-block; + vertical-align: top; +} +.span12 { + width: 97%; + margin: 0 1%; + display: inline-block; + vertical-align: top; +} +.dialog input, .dialog select { + margin: 2px; +} +hr.faded { + opacity: 0.3; +} +</style> diff --git a/include/staff/templates/dynamic-form.tmpl.php b/include/staff/templates/dynamic-form.tmpl.php index f0db379b8..4efb94a21 100644 --- a/include/staff/templates/dynamic-form.tmpl.php +++ b/include/staff/templates/dynamic-form.tmpl.php @@ -38,7 +38,7 @@ if (isset($options['entry']) && $options['mode'] == 'edit') { ?> } foreach ($form->getFields() as $field) { try { - if (!$field->isVisibleToStaff()) + if (!$field->isEditableToStaff()) continue; } catch (Exception $e) { @@ -50,14 +50,14 @@ if (isset($options['entry']) && $options['mode'] == 'edit') { ?> <?php } else { ?> - <td class="multi-line <?php if ($field->get('required')) echo 'required'; + <td class="multi-line <?php if ($field->isRequiredForStaff()) echo 'required'; ?>" style="min-width:120px;" <?php if ($options['width']) echo "width=\"{$options['width']}\""; ?>> <?php echo Format::htmlchars($field->getLocal('label')); ?>:</td> <td><div style="position:relative"><?php } $field->render(); ?> - <?php if (!$field->isBlockLevel() && $field->get('required')) { ?> + <?php if (!$field->isBlockLevel() && $field->isRequiredForStaff()) { ?> <span class="error">*</span> <?php } diff --git a/tickets.php b/tickets.php index 5b15b0015..2161b67b2 100644 --- a/tickets.php +++ b/tickets.php @@ -47,8 +47,6 @@ if($_POST && is_object($ticket) && $ticket->getId()): if(!$ticket->checkUserAccess($thisclient) //double check perm again! || $thisclient->getId() != $ticket->getUserId()) $errors['err']=__('Access Denied. Possibly invalid ticket ID'); - elseif (!$cfg || !$cfg->allowClientUpdates()) - $errors['err']=__('Access Denied. Client updates are currently disabled'); else { $forms=DynamicFormEntry::forTicket($ticket->getId()); foreach ($forms as $form) { @@ -107,7 +105,7 @@ endif; $nav->setActiveNav('tickets'); if($ticket && $ticket->checkUserAccess($thisclient)) { if (isset($_REQUEST['a']) && $_REQUEST['a'] == 'edit' - && $cfg->allowClientUpdates()) { + && $ticket->hasClientEditableFields()) { $inc = 'edit.inc.php'; if (!$forms) $forms=DynamicFormEntry::forTicket($ticket->getId()); // Auto add new fields to the entries -- GitLab