From 577c393cc301e4369050d5a0e62996450e1ad42d Mon Sep 17 00:00:00 2001 From: Jay Shah Date: Fri, 31 Mar 2017 14:14:41 +0100 Subject: [PATCH] 2.1.2 release - Operator template changes --- assets/operator/css/main.css | 87 ++++++- assets/operator/css/mobile.css | 34 ++- assets/operator/css/ticket.css | 20 +- assets/operator/js/article.js | 1 + assets/operator/js/department.js | 11 + assets/operator/js/filtering.js | 5 + assets/operator/js/main.js | 14 +- assets/operator/js/newticket.js | 79 ++++-- assets/operator/js/producttour.js | 24 +- assets/operator/js/queryfiltering.js | 5 +- assets/operator/js/redactor/codeeditor.js | 20 +- assets/operator/js/redactor/mergefields.js | 6 +- assets/operator/js/redactor/replyoptions.js | 50 +++- assets/operator/js/search.js | 4 +- .../operator/js/selectize/disable_delete.js | 10 + assets/operator/js/sidebox.js | 61 +++++ assets/operator/js/ticket.js | 234 +++++++++++++----- templates/operator/default/core/email.twig | 18 ++ templates/operator/default/core/emaillog.twig | 20 +- .../operator/default/core/emailqueue.twig | 2 +- .../operator/default/core/forms/message.twig | 6 +- .../default/core/search_no_permission.twig | 17 ++ .../operator/default/core/search_tabs.twig | 10 + templates/operator/default/datatables.twig | 2 +- .../operator/default/forms/customfields.twig | 207 ++++++++-------- templates/operator/default/index.twig | 17 +- .../operator/default/report/sidebar.twig | 5 +- templates/operator/default/report/view.twig | 5 + .../default/selfservice/forms/article.twig | 1 + .../default/selfservice/forms/category.twig | 3 + .../default/ticket/forms/department.twig | 167 ++++++++++--- .../default/ticket/forms/followup.twig | 7 +- .../default/ticket/forms/message.twig | 23 +- .../ticket/forms/message_reply_options.twig | 26 +- .../default/ticket/forms/newticket_step2.twig | 50 ++-- .../operator/default/ticket/message.twig | 2 +- .../operator/default/ticket/other_draft.twig | 2 +- .../operator/default/ticket/sidebar-grid.twig | 46 +++- .../default/ticket/sidebar-ticket.twig | 41 +-- templates/operator/default/ticket/ticket.twig | 19 +- .../operator/default/ticket/ticketgrid.twig | 48 ++-- .../operator/default/user/forms/group.twig | 3 +- .../operator/default/user/forms/operator.twig | 98 +++----- .../user/forms/operator_notifications.twig | 100 ++++++++ .../default/user/forms/operator_profile.twig | 9 + .../user/forms/operator_ticket_settings.twig | 70 ++++++ .../default/user/forms/organisation_user.twig | 28 ++- .../operator/default/user/forms/user.twig | 18 +- .../operator/default/user/massemail.twig | 2 + .../default/user/personalsetting.twig | 131 +--------- templates/operator/default/user/twofa.twig | 31 +-- templates/operator/default/user/user.twig | 25 +- 52 files changed, 1277 insertions(+), 647 deletions(-) create mode 100644 assets/operator/js/selectize/disable_delete.js create mode 100644 assets/operator/js/sidebox.js create mode 100644 templates/operator/default/core/search_no_permission.twig create mode 100644 templates/operator/default/core/search_tabs.twig create mode 100644 templates/operator/default/user/forms/operator_notifications.twig create mode 100644 templates/operator/default/user/forms/operator_profile.twig create mode 100644 templates/operator/default/user/forms/operator_ticket_settings.twig diff --git a/assets/operator/css/main.css b/assets/operator/css/main.css index a1f09ba..91269e6 100644 --- a/assets/operator/css/main.css +++ b/assets/operator/css/main.css @@ -63,6 +63,16 @@ hr { margin: 25px 0; } +pre:not(.moz-signature) { + background: #f8f8f8; + border: 1px solid #ddd; + border-radius: 3px; + font-size: 90%; + margin: 0; + padding: 1em; + white-space: pre-wrap; +} + .hide { display: none; } @@ -169,7 +179,7 @@ hr { margin: 0; } -.row .item { +.row > .item { display: inline-block; font-size: 13px; padding-right: 25px; @@ -1031,10 +1041,6 @@ ul.dropdown ul li:hover > ul { margin-bottom: 0; } -#sidebar h3.collapsable div.arrow { - margin: 1px 0; -} - #sidebar .details > div { overflow: hidden; text-overflow: ellipsis; @@ -1051,6 +1057,7 @@ ul.dropdown ul li:hover > ul { } #sidebar .details ul.recent-tickets { + margin-top: 5px; white-space: nowrap; } @@ -1167,6 +1174,10 @@ ul.dropdown ul li:hover > ul { padding-left: 24px; } +#sidebar .text-shorten { + word-wrap: break-word; +} + /* Main */ #main { @@ -1345,6 +1356,38 @@ table.settings .description.message-preview { height: 22px; overflow: hidden; position: absolute; + text-overflow: ellipsis; + white-space: nowrap; + width: 95%; +} + +table.settings .ticket-metadata { + color: #666; + text-align: right; +} + +table.settings .ticket-metadata .fa { + font-size: 12px; + padding: 0; +} + +table.settings .ticket-metadata .tag { + background: #bdc3c7; + margin-right: 0; + vertical-align: top; +} + +table.settings .ticket-metadata .notes-count { + background: #f1c40f; +} + +table.settings .ticket-metadata .messages-count.with-notes { + border-radius: 15px 0 0 15px; + margin-right: 1px; +} + +table.settings .ticket-metadata .messages-count + .notes-count { + border-radius: 0 15px 15px 0; } table.settings td.relative { @@ -1454,6 +1497,12 @@ ul.tabs li .badge, ul.tabs a .badge { cursor: pointer; } +/* Departments */ + +.department-templates h3 { + text-align: center; +} + /* Filters */ .filters { @@ -1698,6 +1747,12 @@ table.dataTable > tbody > tr.child span.dtr-title { min-width: 0 !important; } +table.dataTable thead .sorting.hide-icon, +table.dataTable thead .sorting_asc.hide-icon, +table.dataTable thead .sorting_desc.hide-icon { + background-image: none; +} + /* Overwrite Sweetalert2 */ .sweet-alert { @@ -1811,6 +1866,10 @@ table.dataTable > tbody > tr.child span.dtr-title { background: #454545 !important; } +.redactor-editor pre { + margin: 0 !important; +} + /* Redactor plugin lists */ ul.redactor-search { @@ -1936,12 +1995,12 @@ ul.attached-files .progress .bar { width: 0; } -ul.attached-files .deleteAttachment { +ul.attached-files .deleteAttachment, .attachments .deleteAttachment { cursor: pointer; margin: 5px 0; } -ul.attached-files .deleteAttachment:hover { +ul.attached-files .deleteAttachment:hover, .attachments .deleteAttachment:hover { color: #000; } @@ -2029,6 +2088,20 @@ ul.attached-files .deleteAttachment:hover { opacity: 0.8; } +.selectize-dropdown-content .optgroup_header { + background: #eee; + color: #777; + padding: 1px 8px 3px; +} + +.selectize-control.plugin-remove_button [data-value].unremovable { + padding-right: 6px !important; +} + +.selectize-control.plugin-remove_button [data-value].unremovable .remove { + display: none; +} + .ldap_operator { width: 40%; display: inline-block; diff --git a/assets/operator/css/mobile.css b/assets/operator/css/mobile.css index 4123c94..11afbbe 100644 --- a/assets/operator/css/mobile.css +++ b/assets/operator/css/mobile.css @@ -286,11 +286,11 @@ html { margin-bottom: 0; } - .row .item { + .row > .item { margin-bottom: 25px; } - .row .item:last-child { + .row > .item:last-child { margin-bottom: 0; padding-right: 25px; } @@ -330,6 +330,24 @@ html { overflow-y: auto; } + .department-templates .item50:last-child { + padding-right: 0; + } + + .department-templates h3 { + text-align: left; + } + + .department-templates .form-row label { + position: static; + text-align: left; + width: auto; + } + + .department-templates .form-row .input-container { + margin-left: 0; + } + } /* @@ -356,6 +374,14 @@ html { position: absolute; } + .form-container.collapsed label { + margin-left: 20px; + } + + .form-container.collapsed .description { + display: none; + } + ul.tabs { border-bottom: 0; } @@ -380,11 +406,11 @@ html { position: static; } - .row .item { + .row > .item { padding-right: 0; } - .row .item:last-child { + .row > .item:last-child { padding: 0; } diff --git a/assets/operator/css/ticket.css b/assets/operator/css/ticket.css index 46af73b..df305d4 100644 --- a/assets/operator/css/ticket.css +++ b/assets/operator/css/ticket.css @@ -109,6 +109,7 @@ span.subject:hover:after { } .message .text img, .ticket-draft img { + height: auto; max-width: 100%; } @@ -577,6 +578,23 @@ table.ticket-details .link:hover { border-bottom: 1px solid #c0392b; } +table.ticket-details .create-new-user { + background: #fdf6ea; + border: 1px solid #ddd; + border-top: 0; + cursor: pointer; + font-size: 12px; + font-weight: normal; + line-height: 1em; + padding: 8px; + text-align: center; +} + +table.ticket-details .create-new-user .fa { + font-size: 12px; +} + + /* New ticket form */ .user-ticket { @@ -598,7 +616,7 @@ table.ticket-details .link:hover { font-weight: bold; line-height: 32px; margin: 2px 0; - width: 100px; + width: 110px; } .new-user input { diff --git a/assets/operator/js/article.js b/assets/operator/js/article.js index 36c14d6..6ee8832 100644 --- a/assets/operator/js/article.js +++ b/assets/operator/js/article.js @@ -26,6 +26,7 @@ function Article(parameters) // Initialise the visible type drop-down. $('select[name="category['+id+'][type]"]').selectize({ + plugins: ['disableDelete'], onChange: function(value) { // Hide the URL/views for this type this.$input.parents('.form-container').find('.type-url, .type-views').remove(); diff --git a/assets/operator/js/department.js b/assets/operator/js/department.js index 3d7f7de..20cbef2 100644 --- a/assets/operator/js/department.js +++ b/assets/operator/js/department.js @@ -12,6 +12,7 @@ jQuery(function($){ */ var xhr; var $parent = $('select[name=parent]').selectize({ + plugins: ['disableDelete'], valueField: 'id', labelField: 'name', searchField: 'name', @@ -255,6 +256,16 @@ jQuery(function($){ $('.email-piping').find('input[name$="[consume_all]"]').each(function () { setPipingPath(this); }); + + // Handle Disable User Replies. + $('input[name="disable_user_email_replies"]').on('change', function () { + $('#disableRepliesTemplate').toggle(); + }); + + // Convert email template dropdowns to use selectize. + $('.department-templates').find('select').selectize({ + plugins: ['disableDelete'] + }); }); /** diff --git a/assets/operator/js/filtering.js b/assets/operator/js/filtering.js index b5ae65f..6158bf4 100644 --- a/assets/operator/js/filtering.js +++ b/assets/operator/js/filtering.js @@ -2,6 +2,11 @@ $(function() { // Toggle filtering $('.toggle-filtering').click(function() { $('.conditiongroup').toggle(); + + // If we're toggling to show and it's currently empty, insert new condition + if ($('.conditiongroup').is(':visible') && ! $('.conditiongroup .condition:visible').length) { + $('.conditiongroup .add-condition').click(); + } }); // Remove any items that have an empty dropdown diff --git a/assets/operator/js/main.js b/assets/operator/js/main.js index 124e39d..74d2d60 100644 --- a/assets/operator/js/main.js +++ b/assets/operator/js/main.js @@ -65,15 +65,6 @@ $(document).ready(function () { $this.find('.hide').toggle(); }); - // For opening/collapsing sidebar boxes - $('#sidebar').on('click', 'h3.collapsable', function() { - if (!$('#sidebar').hasClass('sidebar-close') || $(window).width() > 960) { - $(this).find('.arrow .fa').toggleClass('fa-chevron-down fa-chevron-up'); - $(this).toggleClass('closed'); - $(this).next().toggle(500); - } - }); - // Toggle show/hide of the filters area $('.filter-results').on('click', function() { $('.filters').toggle(); @@ -99,6 +90,11 @@ $(document).ready(function () { }); } + // Time ago. + if (typeof timeAgo !== 'undefined') { + timeAgo.render($('time.timeago')); + } + /** * Global AJAX setup handler to add the CSRF token to ALL POST requests. */ diff --git a/assets/operator/js/newticket.js b/assets/operator/js/newticket.js index 2e716eb..6d4da0a 100644 --- a/assets/operator/js/newticket.js +++ b/assets/operator/js/newticket.js @@ -89,7 +89,8 @@ $(document).ready(function() { dataType: 'json', data: { brand_id: typeof $brand[0] !== "undefined" ? $brand[0].selectize.getValue() : null, - q: query + q: query, + operators: 0 }, error: function() { callback(); @@ -103,51 +104,47 @@ $(document).ready(function() { // Handle ticket type switching $('input[name="internal"]').change(function() { - $('.user-ticket').toggle(); + if ($(this).val() == 1) { + $('.user-ticket').hide(); + $('.user-ticket').find(':input:not([name="user_type"])').prop('disabled', true); + $('input[name="user"]').prop('disabled', false); + } else { + $('.user-ticket').show(); + $('.user-ticket').find(':input:not([name="user_type"])').prop('disabled', false); + $('input[name="user"]').prop('disabled', true); + } // Reset form validation. $('form.validate').validate().resetForm(); - - // Toggle the disabled attribute. - $('.user-ticket, .internal-ticket') - .find(':input:not([name="user_type"]):not([name="internal"])') - .prop('disabled', function(i, v) { return !v; }); }); // Handle ticket type switching $('input[name="user_type"]').change(function() { - $('.new-user, .existing-user').toggle(); + if ($(this).val() == '0') { + $('.existing-user').show(); + $('.new-user').hide(); + } else { + $('.existing-user').hide(); + $('.new-user').show(); + } // Reset form validation. $('form.validate').validate().resetForm(); }); - if ($('input[name="internal"]:last:checked').length) { - $('.user-ticket').hide(); - } else { - $('.user-ticket').show(); - } + // Run the change events on load to ensure right fields are showing/enabled + $('input[name="internal"]:checked, input[name="user_type"]:checked').change(); - if ($('input[name="user_type"]:checked').val() == '0') { - $('.existing-user').show(); - $('.new-user').hide(); - } else { - $('.existing-user').hide(); - $('.new-user').show(); + // If the brand already has a value, fetch the relevant departments. Usually happens on going back from step 2. + if ($brand.length && $brand[0].selectize.getValue() !== '') { + $brand[0].selectize.setValue($brand[0].selectize.getValue()) } - } else { // STEP 2 // Focus the subject input box. $('input[name="subject"]').focus(); - // Handling internal tickets - if (internal) { - $('.send-user-email').hide(); - $('input[name="send_operators_email"]').prop('checked', true); - } - // Tags $('select[name="tag[]"]').selectize({ plugins: ['remove_button'], @@ -228,7 +225,8 @@ $(document).ready(function() { // From email input $('select[name="department_email"]').selectize({ persist: false, - dropdownParent: 'body' + dropdownParent: 'body', + plugins: ['disableDelete'] }); // Regex for email @@ -240,6 +238,12 @@ $(document).ready(function() { delimiter: ',', persist: false, dropdownParent: 'body', + placeholder: Lang.get('ticket.enter_email_address'), + render: { + item: function(item, escape) { + return '
' + escape(item.value) + '
'; + } + }, createFilter: function(input) { var match = input.match(re); if (match) return !this.options.hasOwnProperty(match[0]); @@ -253,6 +257,19 @@ $(document).ready(function() { text: input }; } + + return false; + }, + onDelete: function(input) { + var self = this; + $.each(input, function(key, value) { + // Delete any items selected that don't have a 'unremovable' class. + if (! $('.cc-emails div[data-value="' + value + '"]').hasClass('unremovable')) { + self.removeItem(value); + } + }); + + // We handle the deletions above, no need to carry on with deleteSelect() return false; } }); @@ -261,6 +278,14 @@ $(document).ready(function() { $('.add-cc').on('click', function() { $('.cc-emails').toggle(); }); + + // Send email options, uncheck and show tooltip if disabled + $.each([ $('input[name="send_user_email"]'), $('input[name="send_operators_email"]') ], function (index, value) { + if (value.prop('disabled')) { + value.prop('checked', false); + value.parent().attr('title', Lang.get('ticket.department_template_disabled')); + } + }); } }); diff --git a/assets/operator/js/producttour.js b/assets/operator/js/producttour.js index a7213a4..fd931f8 100644 --- a/assets/operator/js/producttour.js +++ b/assets/operator/js/producttour.js @@ -82,7 +82,7 @@ var tour = { placement: 'right', showPrevButton: true, xOffset: '20px', - yOffset: '-70px', + yOffset: '-22px', multipage: true, onNext: function() { window.location = laroute.route('ticket.operator.department.edit', { department: 0 }); @@ -108,9 +108,8 @@ var tour = { { title: Lang.get('core.dept_tmpl'), content: Lang.get('core.dept_tmpl_desc'), - target: 'ticket_opened', - placement: 'right', - yOffset: '-15px', + target: 'department-templates', + placement: 'top', showPrevButton: true, onNext: function() { $('#generalSettingsBox').trigger('click'); @@ -121,8 +120,8 @@ var tour = { content: Lang.get('core.schedule_task_desc'), target: 'scheduledTaskSetting', placement: 'right', - yOffset: '-140px', - xOffset: '120px', + xOffset: '-100px', + yOffset: '-18px', showPrevButton: true, multipage: true, onNext: function() { @@ -152,7 +151,7 @@ var tour = { target: 'channelSettings', placement: 'right', xOffset: '20px', - yOffset: '-43px', + yOffset: '-22px', showPrevButton: true, multipage: true, onNext: function() { @@ -188,7 +187,7 @@ var tour = { target: 'userHeaderDropdown', placement: 'right', xOffset: '100px', - yOffset: '80px' + yOffset: '75px' }, { title: Lang.choice('general.operator', 2), @@ -196,7 +195,7 @@ var tour = { target: 'userHeaderDropdown', placement: 'right', xOffset: '100px', - yOffset: '120px', + yOffset: '110px', onNext: function() { // Open the Tickets drop down in the header $('#ticketHeaderDropdown').addClass('hover'); @@ -220,12 +219,7 @@ var tour = { title: Lang.choice('ticket.ticket', 2), content: Lang.get('core.ticket_desc2'), target: 'openNewTicket', - placement: 'bottom', - onShow: function() { - $('#adjust-columns').focus().effect("highlight", {color: 'rgba(59, 145, 206, 0.57)'}, 3000); - $('#filter-columns').focus().effect("highlight", {color: 'rgba(59, 145, 206, 0.57)'}, 3000); - $('#order-columns').focus().effect("highlight", {color: 'rgba(59, 145, 206, 0.57)'}, 3000); - } + placement: 'bottom' }, { title: Lang.get('core.ticket_toolbar'), diff --git a/assets/operator/js/queryfiltering.js b/assets/operator/js/queryfiltering.js index d391a7f..15ce5f2 100644 --- a/assets/operator/js/queryfiltering.js +++ b/assets/operator/js/queryfiltering.js @@ -28,8 +28,9 @@ $(document).ready(function() { } else if ($input.is('select')) { $input.val('-1'); } - // Trigger change to reload table - $input.change(); + + // Trigger input to reload table + $input.trigger('input'); }); $('.datepicker').pikaday({ diff --git a/assets/operator/js/redactor/codeeditor.js b/assets/operator/js/redactor/codeeditor.js index 1e4ca7a..289c778 100644 --- a/assets/operator/js/redactor/codeeditor.js +++ b/assets/operator/js/redactor/codeeditor.js @@ -342,18 +342,28 @@ // If the form has an input called brand_id, use that value else fall back to the // data-brand set on form-row. If nothing is set then it won't be included. - var brandId = redactor.$box.parents('form').find(':input[name="brand_id"]').length ? - redactor.$box.parents('form').find(':input[name="brand_id"]').val() : - redactor.$box.parents('.form-row').data('brand'); + var brandId = redactor.$box.parents('form').find(':input[name="brand_id"]').length + ? redactor.$box.parents('form').find(':input[name="brand_id"]').val() + : redactor.$box.parents('.form-row').data('brand'); + + // Get ticket ID. + var ticketId = redactor.$box.parents('form').find(':input[name="ticket_id"]').length + ? redactor.$box.parents('form').find(':input[name="ticket_id"]').val() + : null; + + // Attempt to get locale. + var locale = redactor.$box.parents('.form-container').find(':input[name$="[language]"]').length + ? redactor.$box.parents('.form-container').find(':input[name$="[language]"]').val() + : Lang.locale(); $.post( laroute.route('core.operator.emailtemplate.preview'), { 'template': codeMirror.getValue(), + 'locale': locale, 'template_id': redactor.$box.parents('form').data('templateId'), 'brand_id': brandId, - 'ticket_id': redactor.$box.parents('form').find(':input[name="ticket_id"]').length ? - redactor.$box.parents('form').find(':input[name="ticket_id"]').val() : null, + 'ticket_id': ticketId, 'is_email': (redactor.opts.syntaxEmailTemplate || false) ? 1 : 0 } ) diff --git a/assets/operator/js/redactor/mergefields.js b/assets/operator/js/redactor/mergefields.js index 7310d65..007e588 100644 --- a/assets/operator/js/redactor/mergefields.js +++ b/assets/operator/js/redactor/mergefields.js @@ -67,6 +67,10 @@ + '' + Lang.choice('ticket.ticket', 1) + ' ' + Lang.choice('ticket.department', 1) + ' ' + Lang.get('general.name') + '' + '' + '' + + '' + + '' + Lang.choice('ticket.ticket', 1) + ' ' + Lang.choice('ticket.department', 1) + ' ' + Lang.get('general.frontend') + ' ' + Lang.get('general.name') + '' + + '' + + '' + '' + '' + Lang.choice('ticket.ticket', 1) + ' ' + Lang.choice('general.status', 1) + ' ' + Lang.get('general.id') + '' + '' @@ -101,7 +105,7 @@ + '' + '' + '' + Lang.choice('ticket.ticket', 1) + ' ' + Lang.get('ticket.last_message_text') + '' - + '' + + '' + '' + '' + '' + Lang.choice('ticket.ticket', 1) + ' ' + Lang.get('operator.frontend_url') + '' diff --git a/assets/operator/js/redactor/replyoptions.js b/assets/operator/js/redactor/replyoptions.js index 4dddf8f..422dca6 100644 --- a/assets/operator/js/redactor/replyoptions.js +++ b/assets/operator/js/redactor/replyoptions.js @@ -1,6 +1,54 @@ $(function() { + // Check if send email checkbox should be checked on event based on relevant department email template + function handleEmailCheckbox(template, name) { + if (template != -1) { + $checkboxes[name].checkbox.prop('disabled', false).prop('checked', $checkboxes[name].state); + $checkboxes[name].checkbox.parent().attr('title', ''); + } else { + $checkboxes[name].checkbox.prop('checked', false).prop('disabled', true); + $checkboxes[name].checkbox.parent().attr('title', Lang.get('ticket.department_template_disabled')); + } + } + + var $checkboxes = { + 'user': {'checkbox': $('.message-form .send-user-email input[type="checkbox"]') }, + 'operator_reply': {'checkbox': $('.message-form .send-operators-email input[type="checkbox"]')}, + 'operator_note': {'checkbox': $('.notes-form .send-operators-email input[type="checkbox"]') } + }; + + $.each($checkboxes, function (index, value) { + // Save the state of the checkbox initially and on change + value.state = value.checkbox.is(':checked'); + value.checkbox.on('change', function() { + value.state = $(this).is(':checked'); + }); + + // If the checkbox is disabled, uncheck it + if (value.checkbox.prop('disabled')) { + value.checkbox.prop('checked', false); + } + }); + + // Check if 'send email to user' should show based on ticket status set, message form only + $(document).on('change', '.message-form select[name="to_status"]', function () { + if ($(this).val() == closedStatusId) { + handleEmailCheckbox(departmentTemplates.user_ticket_operatorclose, 'user'); + } else { + handleEmailCheckbox(departmentTemplates.user_ticket_reply, 'user'); + } + }); + + // Check if 'send email to operator(s)' should show based on ticket message type + $('.reply-type .option').click(function() { + if ($(this).data('type') == 0) { + handleEmailCheckbox(departmentTemplates.operator_operator_ticket_reply, 'operator_reply'); + } else { + handleEmailCheckbox(departmentTemplates.operator_ticket_note, 'operator_note'); + } + }); + // Handle expanding each option group - $(document).on('click', '.option_header', function(e) { + $(document).on('click', '.option_header', function() { $(this).next(".option_content").slideToggle(500); $(this).find(".arrow .fa").toggleClass("fa-chevron-down fa-chevron-up"); }); diff --git a/assets/operator/js/search.js b/assets/operator/js/search.js index 3a8fe76..c11f6e9 100644 --- a/assets/operator/js/search.js +++ b/assets/operator/js/search.js @@ -60,7 +60,7 @@ $(document.body).ready(function() { if (term.length > 1) { // Only search if term is two characters or more $.ajax({ - url: laroute.route('core.operator.search'), + url: laroute.route('core.operator.search_preview'), dataType: "json", data: { query: term }, method: 'POST', @@ -76,7 +76,7 @@ $(document.body).ready(function() { category: "" }); } else { - var link = laroute.route('core.operator.searchresult', { query: encodeURI(term) }); + var link = laroute.route('core.operator.search', { query: encodeURI(term) }); result.data.push({ id: "", label: Lang.get('messages.show_all_results'), diff --git a/assets/operator/js/selectize/disable_delete.js b/assets/operator/js/selectize/disable_delete.js new file mode 100644 index 0000000..24d83dd --- /dev/null +++ b/assets/operator/js/selectize/disable_delete.js @@ -0,0 +1,10 @@ +(function() { + /** + * The backspace key will not delete the selected item. + */ + Selectize.define('disableDelete', function (options) { + this.deleteSelection = function () { + // + }; + }); +})(); \ No newline at end of file diff --git a/assets/operator/js/sidebox.js b/assets/operator/js/sidebox.js new file mode 100644 index 0000000..223445e --- /dev/null +++ b/assets/operator/js/sidebox.js @@ -0,0 +1,61 @@ +$(function () { + + var $sidebar = $('#sidebar'); + + // Collapse appropriate side boxes on DOM load. + if (typeof Cookies !== 'undefined') { + toggleAllCookieState(); + } + + // Toggle side box visibility when sidebar is refreshed. + $sidebar.on('refreshedSidebar', function () { + toggleAllCookieState(); + }); + + // Toggle side box visibility when clicked. + $sidebar.on('click', 'h3.collapsable', function() { + toggleSidebox(this); + + // The sidebox must have an ID to store the cookie. + if (typeof Cookies !== 'undefined' && typeof $(this).prop('id') !== 'undefined') { + Cookies.set($(this).prop('id'), $(this).hasClass('closed') ? 'collapsed' : 'expanded'); + } + }); + + /** + * Toggle the visibility of a given side box. + * + * @param context + * @param slide (optional) If we want an animation to occur, does by default. + */ + function toggleSidebox(context, slide) { + if (! $sidebar.hasClass('sidebar-close') || $(window).width() > 960) { + $(context).find('.arrow .fa').toggleClass('fa-chevron-down fa-chevron-up'); + $(context).toggleClass('closed'); + + if (typeof slide === 'undefined' || slide) { + $(context).next().slideToggle(500); + } else { + $(context).next().toggle(); + } + } + } + + /** + * Loop over each side box and collapse it depending on the state of the cookie. By default, + * all side boxes are expected to be open on DOM load. + */ + function toggleAllCookieState() { + $sidebar.find('h3.collapsable').each(function (index) { + if (typeof $(this).prop('id') !== 'undefined') { + var cookie = Cookies.get($(this).prop('id')); + + // Side box is currently open but cookie says it should be closed. + if (! $(this).hasClass('closed') && cookie == 'collapsed') { + toggleSidebox(this, false); + } + } + }); + } + +}); \ No newline at end of file diff --git a/assets/operator/js/ticket.js b/assets/operator/js/ticket.js index ad2ca8a..7b109e3 100644 --- a/assets/operator/js/ticket.js +++ b/assets/operator/js/ticket.js @@ -189,7 +189,7 @@ function Ticket(parameters) // Redirect to the ticket grid if (response.data.redirect !== false) { - window.location.replace(response.data.redirect); + window.location.href = response.data.redirect; } }).fail(function () { showFeedback(true); @@ -592,7 +592,7 @@ $(document).ready(function() { ticketAction(laroute.route('ticket.operator.action.close')); $(document).ajaxStop(function () { // Go back to ticket grid - window.location.replace(ticketGridUrl); + window.location.href = ticketGridUrl; }); }); // Process lock button @@ -600,7 +600,7 @@ $(document).ready(function() { ticketAction(laroute.route('ticket.operator.action.lock')); $(document).ajaxStop(function () { // Go back to ticket grid - window.location.replace(ticketGridUrl); + window.location.href = ticketGridUrl; }); }); // Process unlock button @@ -686,6 +686,7 @@ $(document).ready(function() { $('.message-' + messageId).remove(); if (!$('.message').length) { // No more messages exist, ticket will likely have been deleted, redirect to grid + // We use replace() here as we don't want them to click back to ticket. window.location.replace(ticketGridUrl); } if (!$('.note').length) { @@ -753,7 +754,13 @@ $(document).ready(function() { $('.tabs #EscalationRules').hide(); } // Update due time - $('.edit-duetime').text(response.data.time); + $('.edit-duetime').html(response.data.time); + // If it says 'set a due time', hide the trash can icon, else show it + if (response.data.time == Lang.get('ticket.set_due_time')) { + $('.update-duetime .remove-duetime').hide(); + } else { + $('.update-duetime .remove-duetime').show(); + } // Update log and escalation rules tables ticket.updateLogTable(); ticket.updateEscalationsTable(); @@ -788,10 +795,8 @@ $(document).ready(function() { function(response) { if (response.status == 'success') { $('.ticket-update.success').show(500).delay(5000).hide(500); - // Update log - ticket.updateLogTable(); // Update due time and hide form - $('.edit-duetime').text(response.data); + $('.edit-duetime').html(response.data); $('.update-duetime').hide(); // If it says 'set a due time', hide the trash can icon, else show it if (response.data == Lang.get('ticket.set_due_time')) { @@ -799,6 +804,9 @@ $(document).ready(function() { } else { $('.update-duetime .remove-duetime').show(); } + // Update log and escalation rules tables + ticket.updateLogTable(); + ticket.updateEscalationsTable(); } else { $('.ticket-update.fail').show(500).delay(5000).hide(500); } @@ -1162,15 +1170,14 @@ $(document).ready(function() { // Hide reply all dropdown if clicking outside the reply-all div $(document).click(function() { - if (! $(event.target).closest('.reply-all').length) { - if ($('.reply-all .dropdown').is(":visible")) { - $('.reply-all .dropdown').hide(); - } - } + $('.reply-all .dropdown:visible').hide(); }); // Handle reply all button to show dropdown - $('.reply-all .button').on('click', function() { + $('.reply-all .button').on('click', function(e) { + // This stops it closing from the code above. + e.stopPropagation(); + $(this).parent().find('.dropdown').toggle(); }); @@ -1205,7 +1212,8 @@ $(document).ready(function() { // From email input $fromSelectize = $('select[name="department_email"]').selectize({ persist: false, - dropdownParent: 'body' + dropdownParent: 'body', + plugins: ['disableDelete'] }); // CC email input @@ -1214,6 +1222,12 @@ $(document).ready(function() { delimiter: ',', persist: false, dropdownParent: 'body', + placeholder: Lang.get('ticket.enter_email_address'), + render: { + item: function(item, escape) { + return '
' + escape(item.value) + '
'; + } + }, createFilter: function(input) { var match = input.match(re); if (match) return !this.options.hasOwnProperty(match[0]); @@ -1227,6 +1241,19 @@ $(document).ready(function() { text: input }; } + + return false; + }, + onDelete: function(input) { + var self = this; + $.each(input, function(key, value) { + // Delete any items selected that don't have a 'unremovable' class. + if (! $('.cc-emails div[data-value="' + value + '"]').hasClass('unremovable')) { + self.removeItem(value); + } + }); + + // We handle the deletions above, no need to carry on with deleteSelect() return false; } }); @@ -1247,9 +1274,12 @@ $(document).ready(function() { valueField: 'id', labelField: 'formatted_name', searchField: [ 'formatted_name', 'email' ], - placeholder: Lang.get('user.search_for_user'), + placeholder: Lang.get('user.search_for_user_operator'), create: false, render: { + optgroup_header: function(item, escape) { + return '
' + escape(item.label) + '
'; + }, option: function(item, escape) { return '
' + '  ' + @@ -1261,18 +1291,19 @@ $(document).ready(function() { load: function(query, callback) { if (!query.length) return callback(); - var data = { q: query }; + var self = this; - if ($('input[name="user_internal"]').is(":checked")) { - // If we want it to be internal, show only operators, else users - data.operator = true; - } else { - // User search, only fetch users for this ticket brand - data.brand_id = brandId; - } - - $.get(laroute.route('user.operator.search'), data) - .done(function(res) { callback(res.data); }) + $.get(laroute.route('user.operator.search'), { + q: query, + brand_id: brandId, + operators: 1 + }) + .done(function(res) { + self.addOptionGroup(Lang.choice('user.user', 1), { label: Lang.choice('user.user', 2) }); + self.addOptionGroup(Lang.choice('general.operator', 1), { label: Lang.choice('general.operator', 2) }); + self.refreshOptions(); + callback(res.data); + }) .fail(function() { callback(); }); }, onChange: function(value) { @@ -1283,9 +1314,76 @@ $(document).ready(function() { } }); - // If we want to change the ticket type when saving the user, remove all existing options from search - $('input[name="user_internal"]').on('change', function() { - $userSelectize[0].selectize.clearOptions(); + $('.create-new-user').click(function() { + // Show the alert + swal({ + title: Lang.get('ticket.create_new_user'), + html: '
' + + '' + + Lang.get('ticket.create_new_user_desc') + + (internal ? ' ' + Lang.get('ticket.convert_user_ticket_desc') : '') + + '

' + + '
' + + '
' + + '
' + + '' + + (organisationsEnabled ? '
' + + '' : '') + + '

* ' + Lang.get('messages.field_required') + '' + + '
', + showCancelButton: true, + confirmButtonText: Lang.get('general.save'), + closeOnConfirm: false, + allowOutsideClick: false + }, function(isConfirm) { + if (isConfirm) { + // Disable submit button + swal.disableButtons(); + + // Post destroy data + $.ajax({ + url: laroute.route('ticket.operator.action.newuser'), + type: 'POST', + data: $('form.new-user').serializeArray(), + success: function(response) { + if (response.status == 'success') { + // We need to update a lot of details on the page. Quick fix, refresh the page. + window.location.reload(); + + swal( + Lang.get('messages.success'), + Lang.get('messages.success_created', { item: Lang.get('general.record') }), + 'success' + ); + } else { + swal( + Lang.get('messages.error'), + response.message, + 'error' + ); + } + } + }).fail(function() { + swal( + Lang.get('messages.error'), + Lang.get('messages.error_created', { item: Lang.get('general.record') }), + 'error' + ); + }); + } + }); }); /** @@ -1418,23 +1516,6 @@ $(document).ready(function() { ticket.loadMessage($message, callback); }); - // Tooltip on status and user groups - $(document).tooltip({ - selector: '.statusIcon, .userGroup', - position: { - my: "center bottom-12", - at: "center top", - using: function( position, feedback ) { - $( this ).css( position ); - $( "
" ) - .addClass( "arrow" ) - .addClass( feedback.vertical ) - .addClass( feedback.horizontal ) - .appendTo( this ); - } - } - }); - /* * Jump to reply */ @@ -1492,7 +1573,7 @@ $(document).ready(function() { // Load table (force it) ticket.updateLogTable(true); } - }) + }); // Load escalation rules table on clicking tab for first time $(document).on('click','.tabs #EscalationRules', function () { @@ -1500,7 +1581,7 @@ $(document).ready(function() { // Load table (force it) ticket.updateEscalationsTable(true); } - }) + }); }); function htmlDecodeWithLineBreaks(html) { @@ -1562,6 +1643,9 @@ function showMessage(html) { // Update editor for editing this new message redactor(message.find('textarea')); + + // Handle email details popup if it exists + ticket.registerEmailDetails(message.find('.show-email-details')); } function updateTicket(data) { @@ -1642,23 +1726,26 @@ function changeDepartment(data) { // Poll for new replies and updates pollReplies(); - // Update department emails list - var first = null; - $fromSelectize[0].selectize.clearOptions(); - $.each(response.data.emails, function(index, value) { - if (first === null) first = index; - $fromSelectize[0].selectize.addOption({ value: index, text: value }); - $fromSelectize[0].selectize.refreshOptions(false); - }); - // Select first option - $fromSelectize[0].selectize.addItem(first, true); + // If the ticket has a department email dropdown + if ($fromSelectize.length) { + // Update department emails list + var first = null; + $fromSelectize[0].selectize.clearOptions(); + $.each(response.data.emails, function (index, value) { + if (first === null) first = index; + $fromSelectize[0].selectize.addOption({value: index, text: value}); + $fromSelectize[0].selectize.refreshOptions(false); + }); + // Select first option + $fromSelectize[0].selectize.addItem(first, true); + } // Update custom fields if (typeof response.data.customfields != 'undefined') { $('#sidebar .customfields').html(response.data.customfields); // Just check to see if we have any custom fields for this department - if ($('#sidebar .customfields').html() == '') { + if ($('#sidebar .customfields').html().trim() == '') { // None - hide custom fields box $('#sidebar .customfields').parents('.sidebox').hide(); } else { @@ -1669,6 +1756,12 @@ function changeDepartment(data) { } } + // Update department templates. + departmentTemplates = response.data.templates; + // Force run that code that checks if we can send the email to user/operators, by mocking events. + $('.message-form select[name="to_status"]').change(); + $('.reply-type .option.active').click(); + // Refresh follow up tab refreshFollowUpTab(); } else { @@ -1720,7 +1813,7 @@ function deleteTicket(block) { 'success' ); // Go back to ticket grid - window.location.replace(ticketGridUrl); + window.location.href = ticketGridUrl; } else { swal( Lang.get('messages.error'), @@ -1831,7 +1924,7 @@ function pollReplies(allMessages) { } // Update ticket details - $('.last-action').text(response.data.details.updated_at); + $('.last-action').html(response.data.details.updated_at); if (response.data.details.update) { // Update sidebar items $('.edit-user').html(response.data.details.user); @@ -1863,7 +1956,13 @@ function pollReplies(allMessages) { }); $('select[name="slaplan"]').val(response.data.details.sla_plan); - $('.edit-duetime').text(response.data.details.due_time); + $('.edit-duetime').html(response.data.details.due_time); + // If it says 'set a due time', hide the trash can icon, else show it + if (response.data.details.due_time == Lang.get('ticket.set_due_time')) { + $('.update-duetime .remove-duetime').hide(); + } else { + $('.update-duetime .remove-duetime').show(); + } // Update reply options status and if closed, hide close button if (response.data.details.status == closedStatusId) { @@ -1880,6 +1979,10 @@ function pollReplies(allMessages) { $('.lock-ticket').show(); $('h1 .fa-lock, .unlock-ticket').hide(); } + + // Update log and escalation rules tables + ticket.updateLogTable(); + ticket.updateEscalationsTable(); } // Update custom fields @@ -1888,6 +1991,13 @@ function pollReplies(allMessages) { // Enable hide/show password toggle if needed callHideShowPassword(); } + + $('#sidebar').trigger('refreshedSidebar'); + + // Refresh timeago. + if (typeof timeAgo !== 'undefined') { + timeAgo.render($('time.timeago')); + } } // Update the last poll time diff --git a/templates/operator/default/core/email.twig b/templates/operator/default/core/email.twig index 52240ea..d56d90a 100644 --- a/templates/operator/default/core/email.twig +++ b/templates/operator/default/core/email.twig @@ -76,6 +76,15 @@
{% endif %} + {% if record.reply_to is not empty %} +
+ +
+ {{ record.reply_to }} +
+
+ {% endif %} + {% if record.cc is not empty %}
@@ -85,6 +94,15 @@
{% endif %} + {% if record.bcc is not empty %} +
+ +
+ {{ implode(", ", record.bcc) }} +
+
+ {% endif %} +

{{ Lang.get('general.content') }}

diff --git a/templates/operator/default/core/emaillog.twig b/templates/operator/default/core/emaillog.twig index 8b5bb30..87a0509 100644 --- a/templates/operator/default/core/emaillog.twig +++ b/templates/operator/default/core/emaillog.twig @@ -38,7 +38,7 @@
- +
{{ form_text('sSearch_2', null, { 'placeholder': Lang.get('general.type_to_filter') }) }}{{ form_button('x', { 'class': 'reset-filter' }) }}
@@ -86,22 +86,4 @@ - - {% endblock %} \ No newline at end of file diff --git a/templates/operator/default/core/emailqueue.twig b/templates/operator/default/core/emailqueue.twig index b42001a..338591f 100644 --- a/templates/operator/default/core/emailqueue.twig +++ b/templates/operator/default/core/emailqueue.twig @@ -29,7 +29,7 @@
- +
{{ form_text('sSearch_1', null, { 'placeholder': Lang.get('general.type_to_filter') }) }}{{ form_button('x', { 'class': 'reset-filter' }) }}
diff --git a/templates/operator/default/core/forms/message.twig b/templates/operator/default/core/forms/message.twig index d6de4b1..fbdc5a6 100644 --- a/templates/operator/default/core/forms/message.twig +++ b/templates/operator/default/core/forms/message.twig @@ -17,7 +17,7 @@ {% for message in messages %}
- {% if message.user_id == auth_user.id %} + {% if message.user_id == auth_user().id %}
{{ Purifier.clean(message.message) }} diff --git a/templates/operator/default/core/search_no_permission.twig b/templates/operator/default/core/search_no_permission.twig new file mode 100644 index 0000000..e7a8037 --- /dev/null +++ b/templates/operator/default/core/search_no_permission.twig @@ -0,0 +1,17 @@ +{% extends parent_template %} + +{% block title %} + {{ Lang.get('core.search_results') }} - '{{ Input.get('query') }}' +{% endblock %} + +{% block content %} + + {% include 'operator.' ~ template ~ '.core.search_tabs' %} + +
+ + {{ Lang.get('messages.not_authorised') }}
+ {{ Lang.get('messages.not_permitted') }} +
+ +{% endblock %} \ No newline at end of file diff --git a/templates/operator/default/core/search_tabs.twig b/templates/operator/default/core/search_tabs.twig new file mode 100644 index 0000000..00946b9 --- /dev/null +++ b/templates/operator/default/core/search_tabs.twig @@ -0,0 +1,10 @@ + \ No newline at end of file diff --git a/templates/operator/default/datatables.twig b/templates/operator/default/datatables.twig index d5e3264..3fb8519 100644 --- a/templates/operator/default/datatables.twig +++ b/templates/operator/default/datatables.twig @@ -7,7 +7,7 @@ {% for i, c in columns %} - {{ c }} + {{ c|raw }} {% endfor %} diff --git a/templates/operator/default/forms/customfields.twig b/templates/operator/default/forms/customfields.twig index 93a0942..d5036ce 100644 --- a/templates/operator/default/forms/customfields.twig +++ b/templates/operator/default/forms/customfields.twig @@ -1,123 +1,118 @@ {% for customfield in customfields %} - {% if (customfield['type'] == 2 or customfield['type'] == 4 or customfield['type'] == 5 - or customfield['type'] == 7 or customfield['type'] == 10) and customfield['options'] is empty %} - {# No options so hide custom field #} - {% else %} - {% if operator|default is not empty %} - {% set customfield = customfield|merge({'locked': false}) %} - {% endif %} -
- {{ form_label('customfield[' ~ customfield['id'] ~ ']', customfield['name']) }} -
- {% if customfield['type'] == 0 %} -
- -
- -
+ {% if operator|default is not empty %} + {% set customfield = customfield|merge({'locked': false}) %} + {% endif %} +
+ {{ form_label('customfield[' ~ customfield['id'] ~ ']', customfield['name']) }} +
+ {% if customfield['type'] == 0 %} +
+ +
+ +
- {% elseif customfield['type'] == 1 %} - {{ form_checkbox('customfield[' ~ customfield['id'] ~ ']', 1, customfield['value'], - (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled'} : {})) }} + {% elseif customfield['type'] == 1 %} + {{ form_checkbox('customfield[' ~ customfield['id'] ~ ']', 1, customfield['value'], + (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled'} : {})) }} - {% elseif customfield['type'] == 2 %} -
- {% for key, option in customfield['options'] %} - - {% if not loop.last %} -
- {% endif %} - {% endfor %} -
+ {% elseif customfield['type'] == 2 %} +
+ {% for key, option in customfield['options'] %} + + {% if not loop.last %} +
+ {% endif %} + {% endfor %} +
- {% elseif customfield['type'] == 3 %} - {{ form_text('customfield[' ~ customfield['id'] ~ ']', - customfield['value']|default is not empty ? timeWithOffset(customfield['value'], false)|date(Config.get('settings.date_format')) : null, - {'class': 'datepicker', 'placeholder': '', 'disabled': (customfield['value'] is not empty and customfield['locked'] ? 'disabled' : null)}) }} + {% elseif customfield['type'] == 3 %} + {{ form_text('customfield[' ~ customfield['id'] ~ ']', + customfield['value']|default is not empty ? timeWithOffset(customfield['value'], false)|date(Config.get('settings.date_format')) : null, + {'class': 'datepicker', 'placeholder': '', 'disabled': (customfield['value'] is not empty and customfield['locked'] ? 'disabled' : null)}) }} - {% elseif customfield['type'] == 4 %} - {{ form_select('customfield[' ~ customfield['id'] ~ '][]', customfield['options'], - customfield['value'], - {'multiple': 'multiple', 'disabled': (customfield['value'] is not empty and customfield['locked'] ? 'disabled' : null)}) }} + {% elseif customfield['type'] == 4 %} + {{ form_select('customfield[' ~ customfield['id'] ~ '][]', customfield['options'], + customfield['value'], + {'multiple': 'multiple', 'disabled': (customfield['value'] is not empty and customfield['locked'] ? 'disabled' : null)}) }} - {% elseif customfield['type'] == 5 %} - {{ form_select('customfield[' ~ customfield['id'] ~ ']', {'': Lang.get('customfield.please_select')} + customfield['options'], - customfield['value'], (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled'} : {})) }} + {% elseif customfield['type'] == 5 %} + {{ form_select('customfield[' ~ customfield['id'] ~ ']', {'': Lang.get('customfield.please_select')} + customfield['options'], + customfield['value'], (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled'} : {})) }} - {% elseif customfield['type'] == 6 %} - {% if operator|default is not empty %} - - {% else %} - {{ form_password('customfield[' ~ customfield['id'] ~ ']', - (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled', 'size': '50%'} : {'size': '50%'})) - }}   {% if operator|default is empty %}({{ Lang.get('user.only_enter_to_change') }}){% endif %} - {% endif %} + {% elseif customfield['type'] == 6 %} + {% if operator|default is not empty %} + + {% else %} + {{ form_password('customfield[' ~ customfield['id'] ~ ']', + (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled', 'size': '50%'} : {'size': '50%'})) + }}   {% if operator|default is empty %}({{ Lang.get('user.only_enter_to_change') }}){% endif %} + {% endif %} - {% elseif customfield['type'] == 7 %} -
- {% for key, option in customfield['options'] %} - - {% if not loop.last %} -
- {% endif %} - {% endfor %} -
+ {% elseif customfield['type'] == 7 %} +
+ {% for key, option in customfield['options'] %} + + {% if not loop.last %} +
+ {% endif %} + {% endfor %} +
- {% elseif customfield['type'] == 8 %} - {{ form_text('customfield[' ~ customfield['id'] ~ ']', customfield['value'], - (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled', 'size': '50%'} : {'size': '50%'})) }} + {% elseif customfield['type'] == 8 %} + {{ form_text('customfield[' ~ customfield['id'] ~ ']', customfield['value'], + (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled', 'size': '50%'} : {'size': '50%'})) }} - {% elseif customfield['type'] == 9 %} - {{ form_textarea('customfield[' ~ customfield['id'] ~ ']', customfield['value'], - (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled'} : {})) }} + {% elseif customfield['type'] == 9 %} + {{ form_textarea('customfield[' ~ customfield['id'] ~ ']', customfield['value'], + (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled'} : {})) }} - {% elseif customfield['type'] == 10 %} -
-
-
+ {% elseif customfield['type'] == 10 %} +
+
+
+ {% for i in 1..5 %} +
{{ i }}
+ {% endfor %} +
+ {% for key, option in customfield['options'] %} +
+
{{ option }}
{% for i in 1..5 %} -
{{ i }}
- {% endfor %} -
- {% for key, option in customfield['options'] %} -
-
{{ option }}
- {% for i in 1..5 %} -
- {{ form_radio('customfield[' ~ customfield['id'] ~ '][' ~ key ~ ']', i, - (customfield['value'] is not empty and array_key_exists(key, customfield['value']) and customfield['value'][key] == i) ? true : false, - (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled'} : {})) }} -
- {% endfor %} +
+ {{ form_radio('customfield[' ~ customfield['id'] ~ '][' ~ key ~ ']', i, + (customfield['value'] is not empty and array_key_exists(key, customfield['value']) and customfield['value'][key] == i) ? true : false, + (customfield['value'] is not empty and customfield['locked'] ? {'disabled': 'disabled'} : {})) }}
{% endfor %} +
+ {% endfor %} + {% endif %} + {% if customfield['description'] is not empty %} + {% if customfield['type'] != 0 and customfield['type'] != 2 and customfield['type'] != 7 %} +
{% endif %} - {% if customfield['description'] is not empty %} - {% if customfield['type'] != 0 and customfield['type'] != 2 and customfield['type'] != 7 %} -
- {% endif %} - {{ customfield['description'] }} - {% endif %} -
+ {{ customfield['description'] }} + {% endif %}
- {% endif %} +
{% endfor %} \ No newline at end of file diff --git a/templates/operator/default/index.twig b/templates/operator/default/index.twig index ebd6358..da8e12c 100644 --- a/templates/operator/default/index.twig +++ b/templates/operator/default/index.twig @@ -99,15 +99,13 @@
-
- +
{% for category, reports in list %}