Implemented import mode (disabled sending emails, stop executing of not needed triggers).

This commit is contained in:
Martin Edenhofer 2012-12-24 14:55:43 +01:00
parent 1c9f2bbdc8
commit ef2f55cfee
22 changed files with 355 additions and 163 deletions

View file

@ -1,5 +1,4 @@
class App.TicketHistory extends App.ControllerModal class App.TicketHistory extends App.ControllerModal
events: events:
'click [data-type=sortorder]': 'sortorder', 'click [data-type=sortorder]': 'sortorder',
'click .close': 'close', 'click .close': 'close',

View file

@ -19,7 +19,7 @@ class Channel::EmailBuild
end end
attr['X-Powered-BY'] = 'OTRS - Open Ticket Request System (http://otrs.org/)' attr['X-Powered-BY'] = 'OTRS - Open Ticket Request System (http://otrs.org/)'
attr['X-Mailer'] = 'OTRS Mail Service (3.0.12)' attr['X-Mailer'] = 'OTRS Mail Service (3.x)'
# set headers # set headers
attr.each do |key, v| attr.each do |key, v|

View file

@ -1,6 +1,10 @@
class Channel::Sendmail < Channel::EmailBuild class Channel::Sendmail < Channel::EmailBuild
include UserInfo include UserInfo
def send(attr, channel, notification = false) def send(attr, channel, notification = false)
# return if we run import mode
return if Setting.get('import_mode')
mail = build(attr, notification) mail = build(attr, notification)
mail.delivery_method :sendmail mail.delivery_method :sendmail
mail.deliver mail.deliver

View file

@ -1,6 +1,10 @@
class Channel::SMTP < Channel::EmailBuild class Channel::SMTP < Channel::EmailBuild
include UserInfo include UserInfo
def send(attr, channel, notification = false) def send(attr, channel, notification = false)
# return if we run import mode
return if Setting.get('import_mode')
mail = build(attr, notification) mail = build(attr, notification)
mail.delivery_method :smtp, { mail.delivery_method :smtp, {
:openssl_verify_mode => 'none', :openssl_verify_mode => 'none',

View file

@ -31,7 +31,8 @@ class History < ActiveRecord::Base
end end
# create history # create history
History.create( record = {
:id => data[:id],
:o_id => data[:o_id], :o_id => data[:o_id],
:history_type_id => history_type.id, :history_type_id => history_type.id,
:history_object_id => history_object.id, :history_object_id => history_object.id,
@ -42,9 +43,20 @@ class History < ActiveRecord::Base
:value_to => data[:value_to], :value_to => data[:value_to],
:id_from => data[:id_from], :id_from => data[:id_from],
:id_to => data[:id_to], :id_to => data[:id_to],
:created_at => data[:created_at],
:created_by_id => data[:created_by_id] :created_by_id => data[:created_by_id]
) }
history_record = nil
if data[:id]
history_record = History.where( :id => data[:id] ).first
end
if history_record
history_record.update_attributes(record)
else
record_new = History.create(record)
record_new.id = record[:id]
record_new.save
end
end end
def self.history_destroy( requested_object, requested_object_id ) def self.history_destroy( requested_object, requested_object_id )

View file

@ -28,9 +28,7 @@ class Observer::History < ActiveRecord::Observer
current = record.class.find(record.id) current = record.class.find(record.id)
# do not send anything if nothing has changed # do not send anything if nothing has changed
if current.attributes == record.attributes return if current.attributes == record.attributes
return
end
puts "HISTORY OBSERVER, object will be updated #{ record.class.name.to_s}.find(#{ current.id.to_s })" puts "HISTORY OBSERVER, object will be updated #{ record.class.name.to_s}.find(#{ current.id.to_s })"
# puts 'current' # puts 'current'
@ -61,12 +59,19 @@ class Observer::History < ActiveRecord::Observer
:lookup_name => 'name', :lookup_name => 'name',
} }
} }
ignore_attributes = {
:created_at => true,
:updated_at => true,
:article_count => true,
:create_article_type_id => true,
:create_article_sender_id => true,
}
diff.each do |key, value_ids| diff.each do |key, value_ids|
# do not log created_at and updated_at attributes # do not log created_at and updated_at attributes
next if key.to_s == 'created_at' next if ignore_attributes[key.to_sym] == true
next if key.to_s == 'updated_at'
puts " CHANGED: #{key} is #{value_ids.inspect}" puts " CHANGED: #{key} is #{value_ids.inspect}"

View file

@ -0,0 +1,69 @@
class Observer::Ticket::Article::CommunicateEmail < ActiveRecord::Observer
observe 'ticket::_article'
def after_create(record)
# return if we run import mode
return if Setting.get('import_mode')
# if sender is customer, do not communication
sender = Ticket::Article::Sender.where( :id => record.ticket_article_sender_id ).first
return 1 if sender == nil
return 1 if sender['name'] == 'Customer'
# only apply on emails
type = Ticket::Article::Type.where( :id => record.ticket_article_type_id ).first
return if type['name'] != 'email'
# build subject
ticket = Ticket.find(record.ticket_id)
subject = ticket.subject_build( record.subject )
# send email
a = Channel::IMAP.new
message = a.send(
{
:message_id => record.message_id,
:in_reply_to => record.in_reply_to,
:from => record.from,
:to => record.to,
:cc => record.cc,
:subject => subject,
:body => record.body,
:attachments => record.attachments
}
)
# store mail plain
Store.add(
:object => 'Ticket::Article::Mail',
:o_id => record.id,
:data => message.to_s,
:filename => "ticket-#{ticket.number}-#{record.id}.eml",
:preferences => {}
)
# add history record
recipient_list = ''
[:to, :cc].each { |key|
if record[key] && record[key] != ''
if recipient_list != ''
recipient_list += ','
end
recipient_list += record[key]
end
}
if recipient_list != ''
History.history_create(
:o_id => record.id,
:history_type => 'email',
:history_object => 'Ticket::Article',
:related_o_id => ticket.id,
:related_history_object => 'Ticket',
:value_from => record.subject,
:value_to => recipient_list,
:created_by_id => record.created_by_id,
)
end
end
end

View file

@ -0,0 +1,27 @@
class Observer::Ticket::Article::CommunicateFacebook < ActiveRecord::Observer
observe 'ticket::_article'
def after_create(record)
# return if we run import mode
return if Setting.get('import_mode')
# if sender is customer, do not communication
sender = Ticket::Article::Sender.where( :id => record.ticket_article_sender_id ).first
return 1 if sender == nil
return 1 if sender['name'] == 'Customer'
# only apply on emails
type = Ticket::Article::Type.where( :id => record.ticket_article_type_id ).first
return if type['name'] != 'facebook'
a = Channel::Facebook.new
a.send(
{
:from => 'me@znuny.com',
:to => 'medenhofer',
:body => record.body
}
)
end
end

View file

@ -0,0 +1,31 @@
class Observer::Ticket::Article::CommunicateTwitter < ActiveRecord::Observer
observe 'ticket::_article'
def after_create(record)
# return if we run import mode
return if Setting.get('import_mode')
# if sender is customer, do not communication
sender = Ticket::Article::Sender.where( :id => record.ticket_article_sender_id ).first
return 1 if sender == nil
return 1 if sender['name'] == 'Customer'
# only apply on tweets
type = Ticket::Article::Type.where( :id => record.ticket_article_type_id ).first
return if type['name'] != 'twitter direct-message' && type['name'] != 'twitter status'
a = Channel::Twitter2.new
message = a.send(
{
:type => type['name'],
:to => record.to,
:body => record.body,
:in_reply_to => record.in_reply_to
},
Rails.application.config.channel_twitter
)
record.message_id = message.id
record.save
end
end

View file

@ -0,0 +1,42 @@
class Observer::Ticket::Article::FillupFromEmail < ActiveRecord::Observer
observe 'ticket::_article'
def before_create(record)
# return if we run import mode
return if Setting.get('import_mode')
# if sender is customer, do not change anything
sender = Ticket::Article::Sender.where( :id => record.ticket_article_sender_id ).first
return if sender == nil
return if sender['name'] == 'Customer'
# set email attributes
type = Ticket::Article::Type.where( :id => record.ticket_article_type_id ).first
return if type['name'] != 'email'
# set subject if empty
ticket = Ticket.find( record.ticket_id )
if !record.subject || record.subject == ''
record.subject = ticket.title
end
# clean subject
record.subject = ticket.subject_clean( record.subject )
# generate message id
fqdn = Setting.get('fqdn')
record.message_id = '<' + DateTime.current.to_s(:number) + '.' + record.ticket_id.to_s + '.' + rand(999999).to_s() + '@' + fqdn + '>'
# set sender
email_address = ticket.group.email_address
system_sender = "#{email_address.realname} <#{email_address.email}>"
if Setting.get('ticket_define_email_from') == 'AgentNameSystemAddressName'
seperator = Setting.get('ticket_define_email_from_seperator')
sender = User.find( record.created_by_id )
record.from = "#{sender.firstname} #{sender.lastname} #{seperator} #{system_sender}"
else
record.from = system_sender
end
end
end

View file

@ -0,0 +1,20 @@
class Observer::Ticket::Article::FillupFromGeneral < ActiveRecord::Observer
observe 'ticket::_article'
def before_create(record)
# return if we run import mode
return if Setting.get('import_mode')
# if sender is customer, do not change anything
sender = Ticket::Article::Sender.where( :id => record.ticket_article_sender_id ).first
return if sender == nil
return if sender['name'] == 'Customer'
# set from if not given
if !record.from
user = User.find( record.created_by_id )
record.from = "#{user.firstname} #{user.lastname}"
end
end
end

View file

@ -0,0 +1,12 @@
class Observer::Ticket::ArticleCounter < ActiveRecord::Observer
observe 'ticket::_article'
def after_create(record)
# get article count
record.ticket.article_count = record.ticket.articles.count
# save ticket
record.ticket.save
end
end

View file

@ -0,0 +1,16 @@
class Observer::Ticket::ArticleSenderType < ActiveRecord::Observer
observe 'ticket::_article'
def after_create(record)
# get article count
count = Ticket::Article.where( :ticket_id => record.ticket_id ).count
return if count > 1
record.ticket.create_article_type_id = record.ticket_article_type_id
record.ticket.create_article_sender_id = record.ticket_article_sender_id
# save ticket
record.ticket.save
end
end

View file

@ -4,6 +4,9 @@ class Observer::Ticket::CloseTime < ActiveRecord::Observer
def after_update(record) def after_update(record)
# puts 'check close time' # puts 'check close time'
# return if we run import mode
return if Setting.get('import_mode')
# check if close_time is already set # check if close_time is already set
return true if record.close_time return true if record.close_time

View file

@ -4,6 +4,9 @@ class Observer::Ticket::FirstResponse < ActiveRecord::Observer
def after_create(record) def after_create(record)
# puts 'check first response' # puts 'check first response'
# return if we run import mode
return if Setting.get('import_mode')
# if article in internal # if article in internal
return true if record.internal return true if record.internal
@ -22,4 +25,4 @@ class Observer::Ticket::FirstResponse < ActiveRecord::Observer
# save ticket # save ticket
record.ticket.save record.ticket.save
end end
end end

View file

@ -6,7 +6,10 @@ class Observer::Ticket::Notification < ActiveRecord::Observer
@@event_buffer = [] @@event_buffer = []
def self.transaction def self.transaction
# return if we run import mode
return if Setting.get('import_mode')
# puts '@@event_buffer' # puts '@@event_buffer'
# puts @@event_buffer.inspect # puts @@event_buffer.inspect
@@event_buffer.each { |event| @@event_buffer.each { |event|
@ -233,6 +236,10 @@ From: #{article.from}
end end
def after_create(record) def after_create(record)
# return if we run import mode
return if Setting.get('import_mode')
# puts 'CREATED!!!!' # puts 'CREATED!!!!'
# puts record.inspect # puts record.inspect
e = { e = {
@ -245,6 +252,10 @@ From: #{article.from}
end end
def before_update(record) def before_update(record)
# return if we run import mode
return if Setting.get('import_mode')
puts 'before_update' puts 'before_update'
current = record.class.find(record.id) current = record.class.find(record.id)
@ -267,6 +278,10 @@ From: #{article.from}
end end
def after_update(record) def after_update(record)
# return if we run import mode
return if Setting.get('import_mode')
# puts 'after_update' # puts 'after_update'
# puts record.inspect # puts record.inspect
# puts '-----' # puts '-----'

View file

@ -31,6 +31,15 @@ class Setting < ApplicationModel
return config return config
end end
def self.set(name, value)
setting = Setting.where( :name => name ).first
if !setting
raise "Can't find config setting '#{name}'"
end
setting.state = { :value => value }
setting.save
end
def self.get(name) def self.get(name)
self.load self.load
return Thread.current[:settings_config][name] return Thread.current[:settings_config][name]

View file

@ -4,13 +4,15 @@ class Ticket < ApplicationModel
before_destroy :destroy_dependencies before_destroy :destroy_dependencies
belongs_to :group belongs_to :group
has_many :articles, :class_name => 'Ticket::Article', :after_add => :cache_update, :after_remove => :cache_update has_many :articles, :class_name => 'Ticket::Article', :after_add => :cache_update, :after_remove => :cache_update
belongs_to :organization belongs_to :organization
belongs_to :ticket_state, :class_name => 'Ticket::State' belongs_to :ticket_state, :class_name => 'Ticket::State'
belongs_to :ticket_priority, :class_name => 'Ticket::Priority' belongs_to :ticket_priority, :class_name => 'Ticket::Priority'
belongs_to :owner, :class_name => 'User' belongs_to :owner, :class_name => 'User'
belongs_to :customer, :class_name => 'User' belongs_to :customer, :class_name => 'User'
belongs_to :created_by, :class_name => 'User' belongs_to :created_by, :class_name => 'User'
belongs_to :create_article_type, :class_name => 'Ticket::Article::Type'
belongs_to :create_article_sender, :class_name => 'Ticket::Article::Sender'
after_create :cache_delete after_create :cache_delete
after_update :cache_delete after_update :cache_delete

View file

@ -1,6 +1,5 @@
class Ticket::Article < ApplicationModel class Ticket::Article < ApplicationModel
before_create :fillup after_create :attachment_check
after_create :attachment_check, :communicate
belongs_to :ticket belongs_to :ticket
belongs_to :ticket_article_type, :class_name => 'Ticket::Article::Type' belongs_to :ticket_article_type, :class_name => 'Ticket::Article::Type'
belongs_to :ticket_article_sender, :class_name => 'Ticket::Article::Sender' belongs_to :ticket_article_sender, :class_name => 'Ticket::Article::Sender'
@ -11,49 +10,6 @@ class Ticket::Article < ApplicationModel
after_destroy :cache_delete after_destroy :cache_delete
private private
def fillup
# if sender is customer, do not change anything
sender = Ticket::Article::Sender.where( :id => self.ticket_article_sender_id ).first
return if sender == nil
return if sender['name'] == 'Customer'
type = Ticket::Article::Type.where( :id => self.ticket_article_type_id ).first
ticket = Ticket.find(self.ticket_id)
# set from if not given
if !self.from
user = User.find(self.created_by_id)
self.from = "#{user.firstname} #{user.lastname}"
end
# set email attributes
if type['name'] == 'email'
# set subject if empty
if !self.subject || self.subject == ''
self.subject = ticket.title
end
# clean subject
self.subject = ticket.subject_clean(self.subject)
# generate message id
fqdn = Setting.get('fqdn')
self.message_id = '<' + DateTime.current.to_s(:number) + '.' + self.ticket_id.to_s + '.' + rand(999999).to_s() + '@' + fqdn + '>'
# set sender
email_address = ticket.group.email_address
system_sender = "#{email_address.realname} <#{email_address.email}>"
if Setting.get('ticket_define_email_from') == 'AgentNameSystemAddressName'
seperator = Setting.get('ticket_define_email_from_seperator')
sender = User.find(self.created_by_id)
self.from = "#{sender.firstname} #{sender.lastname} #{seperator} #{system_sender}"
else
self.from = system_sender
end
end
end
def attachment_check def attachment_check
# do nothing if no attachment exists # do nothing if no attachment exists
@ -73,108 +29,11 @@ class Ticket::Article < ApplicationModel
self.attachments = article_store self.attachments = article_store
end end
def communicate
# if sender is customer, do not communication
sender = Ticket::Article::Sender.where( :id => self.ticket_article_sender_id ).first
return 1 if sender == nil
return 1 if sender['name'] == 'Customer'
type = Ticket::Article::Type.where( :id => self.ticket_article_type_id ).first
ticket = Ticket.find(self.ticket_id)
# if sender is agent or system
# create tweet
if type['name'] == 'twitter direct-message' || type['name'] == 'twitter status'
a = Channel::Twitter2.new
message = a.send(
{
:type => type['name'],
:to => self.to,
:body => self.body,
:in_reply_to => self.in_reply_to
},
Rails.application.config.channel_twitter
)
self.message_id = message.id
self.save
end
# post facebook comment
if type['name'] == 'facebook'
a = Channel::Facebook.new
a.send(
{
:from => 'me@znuny.com',
:to => 'medenhofer',
:body => self.body
}
)
end
# send email
if type['name'] == 'email'
# build subject
subject = ticket.subject_build(self.subject)
# send email
a = Channel::IMAP.new
message = a.send(
{
:message_id => self.message_id,
:in_reply_to => self.in_reply_to,
:from => self.from,
:to => self.to,
:cc => self.cc,
:subject => subject,
:body => self.body,
:attachments => self.attachments
}
)
# store mail plain
Store.add(
:object => 'Ticket::Article::Mail',
:o_id => self.id,
:data => message.to_s,
:filename => "ticket-#{ticket.number}-#{self.id}.eml",
:preferences => {}
)
# add history record
recipient_list = ''
[:to, :cc].each { |key|
if self[key] && self[key] != ''
if recipient_list != ''
recipient_list += ','
end
recipient_list += self[key]
end
}
if recipient_list != ''
History.history_create(
:o_id => self.id,
:history_type => 'email',
:history_object => 'Ticket::Article',
:related_o_id => ticket.id,
:related_history_object => 'Ticket',
:value_from => self.subject,
:value_to => recipient_list,
:created_by_id => self.created_by_id,
)
end
end
end
class Flag < ApplicationModel class Flag < ApplicationModel
end end
class Sender < ApplicationModel class Sender < ApplicationModel
validates :name, :presence => true validates :name, :presence => true
end end
class Type < ApplicationModel class Type < ApplicationModel

View file

@ -17,6 +17,7 @@ module Zammad
# Custom directories with classes and modules you want to be autoloadable. # Custom directories with classes and modules you want to be autoloadable.
config.autoload_paths += Dir["#{config.root}/lib/**/"] config.autoload_paths += Dir["#{config.root}/lib/**/"]
# config.autoload_paths += %W(#{config.root}/lib)
# Only load the plugins named here, in the order given (default is alphabetical). # Only load the plugins named here, in the order given (default is alphabetical).
# :all can be used as a placeholder for all plugins not explicitly named. # :all can be used as a placeholder for all plugins not explicitly named.
@ -30,6 +31,13 @@ module Zammad
'observer::_ticket::_last_contact', 'observer::_ticket::_last_contact',
'observer::_ticket::_close_time', 'observer::_ticket::_close_time',
'observer::_ticket::_user_ticket_counter', 'observer::_ticket::_user_ticket_counter',
'observer::_ticket::_article_counter',
'observer::_ticket::_article_sender_type',
'observer::_ticket::_article::_fillup_from_general',
'observer::_ticket::_article::_fillup_from_email',
'observer::_ticket::_article::_communicate_email',
'observer::_ticket::_article::_communicate_facebook',
'observer::_ticket::_article::_communicate_twitter',
'observer::_ticket::_notification', 'observer::_ticket::_notification',
'observer::_tag::_ticket_history' 'observer::_tag::_ticket_history'

View file

@ -0,0 +1,28 @@
class TicketExtend < ActiveRecord::Migration
def up
change_table :tickets do |t|
t.column :first_response_escal_date, :timestamp, :null => true
t.column :first_response_sla_time, :timestamp, :null => true
t.column :close_time_escal_date, :timestamp, :null => true
t.column :close_time_sla_time, :timestamp, :null => true
t.column :create_article_type_id, :integer, :null => true
t.column :create_article_sender_id, :integer, :null => true
t.column :article_count, :integer, :null => true
end
add_index :tickets, [:first_response_escal_date]
add_index :tickets, [:close_time_escal_date]
add_index :tickets, [:create_article_type_id]
add_index :tickets, [:create_article_sender_id]
tickets = Ticket.all
tickets.each {|t|
t.article_count = t.articles.count
t.create_ticket_article_type_id = t.articles.first.ticket_article_type.id
t.create_ticket_article_sender_id = t.articles.first.ticket_article_sender.id
t.save
}
end
def down
end
end

View file

@ -1018,6 +1018,31 @@ Setting.create(
:frontend => true :frontend => true
) )
Setting.create(
:title => 'Import Mode',
:name => 'import_mode',
:area => 'System::Import',
:description => 'Set system in import mode (disable some triggers).',
:options => {
:form => [
{
:display => '',
:null => true,
:name => 'import_mode',
:tag => 'boolean',
:options => {
true => 'yes',
false => 'no',
},
},
],
},
:state => {
:value => false,
},
:frontend => true
)
email_address = EmailAddress.create( email_address = EmailAddress.create(
:id => 1, :id => 1,
:realname => 'Zammad', :realname => 'Zammad',
@ -1034,8 +1059,7 @@ signature = Signature.create(
Super Support - Waterford Business Park Super Support - Waterford Business Park
5201 Blue Lagoon Drive - 8th Floor & 9th Floor - Miami, 33126 USA 5201 Blue Lagoon Drive - 8th Floor & 9th Floor - Miami, 33126 USA
Email: hot@example.com - Web: http://www.example.com/ Email: hot@example.com - Web: http://www.example.com/
-- --',
)',
:updated_by_id => 1, :updated_by_id => 1,
:created_by_id => 1 :created_by_id => 1
) )