diff --git a/README.md b/README.md index e2f8578ce..030acd785 100644 --- a/README.md +++ b/README.md @@ -3,9 +3,9 @@ Zammad is a web based open source helpdesk/customer support system with many features to manage customer communication via several channels like telephone, facebook, twitter, chat and e-mails. It is distributed under the GNU AFFERO -General Public License (AGPL) and tested on Linux, Solaris, AIX, FreeBSD, -OpenBSD and Mac OS 10.x. Do you receive many e-mails and want to answer them -with a team of agents? +General Public License (AGPL). + +Do you receive many e-mails and want to answer them with a team of agents? You're going to love Zammad! diff --git a/app/assets/javascripts/app/controllers/ticket_zoom.coffee b/app/assets/javascripts/app/controllers/ticket_zoom.coffee index d05db0448..430a8b7d0 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom.coffee @@ -848,7 +848,7 @@ class App.TicketZoom extends App.Controller error: (settings, details) => App.Event.trigger 'notify', { type: 'error' - msg: App.i18n.translateContent(details.error_human || details.error || 'Unable to update!') + msg: App.i18n.translateContent(details.error_human || details.error || settings.responseJSON.error || 'Unable to update!') timeout: 2000 } @autosaveStart() diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/sidebar_ticket.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/sidebar_ticket.coffee index 53253d729..76bfeff9e 100644 --- a/app/assets/javascripts/app/controllers/ticket_zoom/sidebar_ticket.coffee +++ b/app/assets/javascripts/app/controllers/ticket_zoom/sidebar_ticket.coffee @@ -8,23 +8,42 @@ class Edit extends App.ObserverController render: (ticket, diff) => defaults = ticket.attributes() delete defaults.article # ignore article infos + followUpPossible = App.Group.find(defaults.group_id).follow_up_possible + ticketState = App.TicketState.find(defaults.state_id).name + taskState = @taskGet('ticket') if !_.isEmpty(taskState) defaults = _.extend(defaults, taskState) - new App.ControllerForm( - elReplace: @el - model: App.Ticket - screen: 'edit' - handlers: [ - @ticketFormChanges - ] - filter: @formMeta.filter - params: defaults - isDisabled: !ticket.editable() - #bookmarkable: true - ) + if followUpPossible == 'new_ticket' && ticketState != 'closed' || + followUpPossible != 'new_ticket' || + @permissionCheck('admin') || @permissionCheck('ticket.agent') + new App.ControllerForm( + elReplace: @el + model: App.Ticket + screen: 'edit' + handlers: [ + @ticketFormChanges + ] + filter: @formMeta.filter + params: defaults + isDisabled: !ticket.editable() + #bookmarkable: true + ) + else + new App.ControllerForm( + elReplace: @el + model: App.Ticket + screen: 'edit' + handlers: [ + @ticketFormChanges + ] + filter: @formMeta.filter + params: defaults + isDisabled: ticket.editable() + #bookmarkable: true + ) @markForm(true) diff --git a/app/assets/javascripts/app/lib/app_post/utils.coffee b/app/assets/javascripts/app/lib/app_post/utils.coffee index 0bc5a7f97..134deb96e 100644 --- a/app/assets/javascripts/app/lib/app_post/utils.coffee +++ b/app/assets/javascripts/app/lib/app_post/utils.coffee @@ -964,8 +964,8 @@ class App.Utils senders = App.Utils.parseAddressListLocal(article.from) if senders for sender in senders - if sender && sender.address && sender.address.match('@') - senderIsLocal = isLocalAddress(sender.address) + if sender && sender.match('@') + senderIsLocal = isLocalAddress(sender) # check if article recipient is local recipientIsLocal = false diff --git a/app/controllers/tickets_controller.rb b/app/controllers/tickets_controller.rb index 9b2414be3..dbdf36ef6 100644 --- a/app/controllers/tickets_controller.rb +++ b/app/controllers/tickets_controller.rb @@ -7,6 +7,7 @@ class TicketsController < ApplicationController include TicketStats prepend_before_action :authentication_check + before_action :follow_up_possible_check, only: :update # GET /api/v1/tickets def index @@ -124,7 +125,7 @@ class TicketsController < ApplicationController if !local_customer && clean_customer[:id].present? local_customer = User.find_by(id: clean_customer[:id]) end - if clean_customer[:email].present? + if !local_customer && clean_customer[:email].present? local_customer = User.find_by(email: clean_customer[:email].downcase) end if !local_customer && clean_customer[:login].present? @@ -599,6 +600,14 @@ class TicketsController < ApplicationController private + def follow_up_possible_check + ticket = Ticket.find(params[:id]) + + return true if ticket.group.follow_up_possible != 'new_ticket' # check if the setting for follow_up_possible is disabled + return true if ticket.state.name != 'closed' # check if the ticket state is already closed + raise Exceptions::UnprocessableEntity, 'Cannot follow up on a closed ticket. Please create a new ticket.' + end + def ticket_all(ticket) # get attributes to update diff --git a/test/controllers/tickets_controller_test.rb b/test/controllers/tickets_controller_test.rb index 785ab7001..bc86dfc57 100644 --- a/test/controllers/tickets_controller_test.rb +++ b/test/controllers/tickets_controller_test.rb @@ -48,6 +48,7 @@ class TicketsControllerTest < ActionDispatch::IntegrationTest roles: roles, ) UserInfo.current_user_id = nil + end test '01.01 ticket create with agent - missing group' do @@ -1733,4 +1734,76 @@ AAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO end + test '06.01 - ticket with follow up possible set to new_ticket' do + group = Group.create_or_update( + name: "GroupWithNoFollowUp-#{rand(9_999_999_999)}", + active: true, + updated_by_id: 1, + created_by_id: 1, + follow_up_possible: 'new_ticket' # disable follow up possible + ) + + ticket = Ticket.create!( + title: 'ticket with wrong ticket id', + group_id: group.id, + customer_id: @customer_without_org.id, + state: Ticket::State.lookup(name: 'closed'), # set the ticket to closed + priority: Ticket::Priority.lookup(name: '2 normal'), + updated_by_id: 1, + created_by_id: 1, + ) + + state = Ticket::State.find_by(name: 'open') # try to open a ticket from a closed state + + # customer + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-customer1@example.com', 'customer1pw') + params = { + state_id: state.id, # set the state id + } + + put "/api/v1/tickets/#{ticket.id}", params: params.to_json, headers: @headers.merge('Authorization' => credentials) + assert_response(422) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('Cannot follow up on a closed ticket. Please create a new ticket.', result['error']) + + ticket = Ticket.create!( + title: 'ticket with wrong ticket id', + group_id: group.id, + customer_id: @customer_without_org.id, + state: Ticket::State.lookup(name: 'closed'), # set the ticket to closed + priority: Ticket::Priority.lookup(name: '2 normal'), + updated_by_id: 1, + created_by_id: 1, + ) + + # admin + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-admin@example.com', 'adminpw') + + put "/api/v1/tickets/#{ticket.id}", params: params.to_json, headers: @headers.merge('Authorization' => credentials) + assert_response(422) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('Cannot follow up on a closed ticket. Please create a new ticket.', result['error']) + + ticket = Ticket.create!( + title: 'ticket with wrong ticket id', + group_id: group.id, + customer_id: @customer_without_org.id, + state: Ticket::State.lookup(name: 'closed'), # set the ticket to closed + priority: Ticket::Priority.lookup(name: '2 normal'), + updated_by_id: 1, + created_by_id: 1, + ) + + # agent + credentials = ActionController::HttpAuthentication::Basic.encode_credentials('tickets-agent@example.com', 'agentpw') + + put "/api/v1/tickets/#{ticket.id}", params: params.to_json, headers: @headers.merge('Authorization' => credentials) + assert_response(422) + result = JSON.parse(@response.body) + assert_equal(Hash, result.class) + assert_equal('Cannot follow up on a closed ticket. Please create a new ticket.', result['error']) + end + end