otrs.rb 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447
  1. require 'base64'
  2. module Import
  3. end
  4. module Import::OTRS
  5. =begin
  6. result = request_json( :Subaction => 'List', 1)
  7. return
  8. { some json structure }
  9. result = request_json( :Subaction => 'List' )
  10. return
  11. "some data string"
  12. =end
  13. def self.request_json(data, data_only = false)
  14. response = post(data)
  15. if !response
  16. fail "Can't connect to Zammad Migrator"
  17. end
  18. if !response.success?
  19. fail "Can't connect to Zammad Migrator"
  20. end
  21. result = json(response)
  22. if !result
  23. fail 'Invalid response'
  24. end
  25. if data_only
  26. result['Result']
  27. else
  28. result
  29. end
  30. end
  31. =begin
  32. start get request to backend, add auth data automatically
  33. result = request('Subaction=List')
  34. return
  35. "some data string"
  36. =end
  37. def self.request(part)
  38. url = Setting.get('import_otrs_endpoint') + part + ';Key=' + Setting.get('import_otrs_endpoint_key')
  39. log 'GET: ' + url
  40. response = UserAgent.get(
  41. url,
  42. {},
  43. {
  44. open_timeout: 10,
  45. read_timeout: 60,
  46. user: Setting.get('import_otrs_user'),
  47. password: Setting.get('import_otrs_password'),
  48. },
  49. )
  50. if !response.success?
  51. log "ERROR: #{response.error}"
  52. return
  53. end
  54. response
  55. end
  56. =begin
  57. start post request to backend, add auth data automatically
  58. result = request('Subaction=List')
  59. return
  60. "some data string"
  61. =end
  62. def self.post(data, url = nil)
  63. if !url
  64. url = Setting.get('import_otrs_endpoint')
  65. data['Action'] = 'ZammadMigrator'
  66. end
  67. data['Key'] = Setting.get('import_otrs_endpoint_key')
  68. log 'POST: ' + url
  69. log 'PARAMS: ' + data.inspect
  70. open_timeout = 10
  71. read_timeout = 120
  72. if data.empty?
  73. open_timeout = 6
  74. read_timeout = 20
  75. end
  76. response = UserAgent.post(
  77. url,
  78. data,
  79. {
  80. open_timeout: open_timeout,
  81. read_timeout: read_timeout,
  82. user: Setting.get('import_otrs_user'),
  83. password: Setting.get('import_otrs_password'),
  84. },
  85. )
  86. if !response.success?
  87. log "ERROR: #{response.error}"
  88. return
  89. end
  90. response
  91. end
  92. =begin
  93. start post request to backend, add auth data automatically
  94. result = json('some response string')
  95. return
  96. {}
  97. =end
  98. def self.json(response)
  99. data = Encode.conv( 'utf8', response.body.to_s )
  100. JSON.parse( data )
  101. end
  102. =begin
  103. start auth on OTRS - just for experimental reasons
  104. result = auth(username, password)
  105. return
  106. { ..user structure.. }
  107. =end
  108. def self.auth(username, password)
  109. url = Setting.get('import_otrs_endpoint')
  110. url.gsub!('ZammadMigrator', 'ZammadSSO')
  111. response = post( { Action: 'ZammadSSO', Subaction: 'Auth', User: username, Pw: password }, url )
  112. return if !response
  113. return if !response.success?
  114. result = json(response)
  115. result
  116. end
  117. =begin
  118. request session data - just for experimental reasons
  119. result = session(session_id)
  120. return
  121. { ..session structure.. }
  122. =end
  123. def self.session(session_id)
  124. url = Setting.get('import_otrs_endpoint')
  125. url.gsub!('ZammadMigrator', 'ZammadSSO')
  126. response = post( { Action: 'ZammadSSO', Subaction: 'SessionCheck', SessionID: session_id }, url )
  127. return if !response
  128. return if !response.success?
  129. result = json(response)
  130. result
  131. end
  132. =begin
  133. load objects from otrs
  134. result = load('SysConfig')
  135. return
  136. [
  137. { ..object1.. },
  138. { ..object2.. },
  139. { ..object3.. },
  140. ]
  141. =end
  142. def self.load( object, limit = '', offset = '', diff = 0 )
  143. request_json( { Subaction: 'Export', Object: object, Limit: limit, Offset: offset, Diff: diff }, 1 )
  144. end
  145. =begin
  146. start get request to backend to check connection
  147. result = connection_test
  148. return
  149. true | false
  150. =end
  151. def self.connection_test
  152. request_json({})
  153. end
  154. =begin
  155. get object statistic from remote server ans save it in cache
  156. result = statistic('Subaction=List')
  157. return
  158. {
  159. 'Ticket' => 1234,
  160. 'User' => 123,
  161. 'SomeObject' => 999,
  162. }
  163. =end
  164. def self.statistic
  165. # check cache
  166. cache = Cache.get('import_otrs_stats')
  167. if cache
  168. return cache
  169. end
  170. # retrive statistic
  171. statistic = request_json( { Subaction: 'List' }, 1)
  172. if statistic
  173. Cache.write('import_otrs_stats', statistic)
  174. end
  175. statistic
  176. end
  177. =begin
  178. return current import state
  179. result = current_state
  180. return
  181. {
  182. :Ticket => {
  183. :total => 1234,
  184. :done => 13,
  185. },
  186. :Base => {
  187. :total => 1234,
  188. :done => 13,
  189. },
  190. }
  191. =end
  192. def self.current_state
  193. data = statistic
  194. base = Group.count + Ticket::State.count + Ticket::Priority.count
  195. base_total = data['Queue'] + data['State'] + data['Priority']
  196. user = User.count
  197. user_total = data['User'] + data['CustomerUser']
  198. data = {
  199. Base: {
  200. done: base,
  201. total: base_total || 0,
  202. },
  203. User: {
  204. done: user,
  205. total: user_total || 0,
  206. },
  207. Ticket: {
  208. done: Ticket.count,
  209. total: data['Ticket'] || 0,
  210. },
  211. }
  212. data
  213. end
  214. #
  215. # start import
  216. #
  217. # Import::OTRS.start
  218. #
  219. def self.start
  220. log 'Start import...'
  221. # check if system is in import mode
  222. if !Setting.get('import_mode')
  223. fail 'System is not in import mode!'
  224. end
  225. result = request_json({})
  226. if !result['Success']
  227. fail 'API key not valid!'
  228. end
  229. # set settings
  230. settings = load('SysConfig')
  231. setting(settings)
  232. # dynamic fields
  233. dynamic_fields = load('DynamicField')
  234. #settings(dynamic_fields, settings)
  235. # email accounts
  236. #accounts = load('PostMasterAccount')
  237. #account(accounts)
  238. # email filter
  239. #filters = load('PostMasterFilter')
  240. #filter(filters)
  241. # create states
  242. states = load('State')
  243. ActiveRecord::Base.transaction do
  244. state(states)
  245. end
  246. # create priorities
  247. priorities = load('Priority')
  248. ActiveRecord::Base.transaction do
  249. priority(priorities)
  250. end
  251. # create groups
  252. queues = load('Queue')
  253. ActiveRecord::Base.transaction do
  254. ticket_group(queues)
  255. end
  256. # get agents groups
  257. groups = load('Group')
  258. # get agents roles
  259. roles = load('Role')
  260. # create agents
  261. users = load('User')
  262. ActiveRecord::Base.transaction do
  263. user(users, groups, roles, queues)
  264. end
  265. # create organizations
  266. organizations = load('Customer')
  267. ActiveRecord::Base.transaction do
  268. organization(organizations)
  269. end
  270. # create customers
  271. count = 0
  272. steps = 50
  273. run = true
  274. while run
  275. count += steps
  276. records = load('CustomerUser', steps, count - steps)
  277. if !records || !records[0]
  278. log 'all customers imported.'
  279. run = false
  280. next
  281. end
  282. customer(records, organizations)
  283. end
  284. Thread.abort_on_exception = true
  285. thread_count = 8
  286. threads = {}
  287. steps = 20
  288. (1..thread_count).each { |thread|
  289. threads[thread] = Thread.new {
  290. log "Started import thread# #{thread} ..."
  291. Thread.current[:thread_no] = thread
  292. Thread.current[:loop_count] = 0
  293. loop do
  294. # get the offset for the current thread and loop count
  295. thread_offset_base = (Thread.current[:thread_no] - 1) * steps
  296. thread_step = thread_count * steps
  297. offset = Thread.current[:loop_count] * thread_step + thread_offset_base
  298. log "loading... thread# #{thread} ..."
  299. records = load( 'Ticket', steps, offset)
  300. if !records || !records[0]
  301. log "... thread# #{thread}, no more work."
  302. break
  303. end
  304. _ticket_result(records, thread)
  305. Thread.current[:loop_count] += 1
  306. end
  307. ActiveRecord::Base.connection.close
  308. }
  309. }
  310. (1..thread_count).each {|thread|
  311. threads[thread].join
  312. }
  313. true
  314. end
  315. =begin
  316. start import in background
  317. Import::OTRS.start_bg
  318. =end
  319. def self.start_bg
  320. Setting.reload
  321. Import::OTRS.connection_test
  322. # start thread to observe current state
  323. status_update_thread = Thread.new {
  324. loop do
  325. result = {
  326. data: current_state,
  327. result: 'in_progress',
  328. }
  329. Cache.write('import:state', result, expires_in: 10.minutes)
  330. sleep 8
  331. end
  332. }
  333. sleep 2
  334. # start thread to import data
  335. begin
  336. import_thread = Thread.new {
  337. Import::OTRS.start
  338. }
  339. rescue => e
  340. status_update_thread.exit
  341. status_update_thread.join
  342. Rails.logger.error e.message
  343. Rails.logger.error e.backtrace.inspect
  344. result = {
  345. message: e.message,
  346. result: 'error',
  347. }
  348. Cache.write('import:state', result, expires_in: 10.hours)
  349. return false
  350. end
  351. import_thread.join
  352. status_update_thread.exit
  353. status_update_thread.join
  354. result = {
  355. result: 'import_done',
  356. }
  357. Cache.write('import:state', result, expires_in: 10.hours)
  358. Setting.set('system_init_done', true)
  359. Setting.set('import_mode', false)
  360. end
  361. =begin
  362. get import state from background process
  363. result = Import::OTRS.status_bg
  364. =end
  365. def self.status_bg
  366. state = Cache.get('import:state')
  367. return state if state
  368. {
  369. message: 'not running',
  370. }
  371. end
  372. def self.diff_worker
  373. return if !Setting.get('import_mode')
  374. return if Setting.get('import_otrs_endpoint') == 'http://otrs_host/otrs'
  375. diff
  376. end
  377. def self.diff
  378. log 'Start diff...'
  379. # check if system is in import mode
  380. if !Setting.get('import_mode')
  381. fail 'System is not in import mode!'
  382. end
  383. # create states
  384. states = load('State')
  385. state(states)
  386. # create priorities
  387. priorities = load('Priority')
  388. priority(priorities)
  389. # create groups
  390. queues = load('Queue')
  391. ticket_group(queues)
  392. # get agents groups
  393. groups = load('Group')
  394. # get agents roles
  395. roles = load('Role')
  396. # create agents
  397. users = load('User')
  398. user(users, groups, roles, queues)
  399. # create organizations
  400. organizations = load('Customer')
  401. organization(organizations)
  402. # get changed tickets
  403. ticket_diff
  404. end
  405. def self.ticket_diff
  406. count = 0
  407. run = true
  408. steps = 20
  409. while run
  410. count += steps
  411. log 'loading... diff ...'
  412. records = load( 'Ticket', steps, count - steps, 1 )
  413. if !records || !records[0]
  414. log '... no more work.'
  415. run = false
  416. next
  417. end
  418. _ticket_result(records)
  419. end
  420. end
  421. def self._ticket_result(result, _thread = '-')
  422. map = {
  423. Ticket: {
  424. Changed: :updated_at,
  425. Created: :created_at,
  426. CreateBy: :created_by_id,
  427. TicketNumber: :number,
  428. QueueID: :group_id,
  429. StateID: :state_id,
  430. PriorityID: :priority_id,
  431. Owner: :owner,
  432. CustomerUserID: :customer,
  433. Title: :title,
  434. TicketID: :id,
  435. FirstResponse: :first_response,
  436. #FirstResponseTimeDestinationDate: :first_response_escal_date,
  437. #FirstResponseInMin: :first_response_in_min,
  438. #FirstResponseDiffInMin: :first_response_diff_in_min,
  439. Closed: :close_time,
  440. #SoltutionTimeDestinationDate: :close_time_escal_date,
  441. #CloseTimeInMin: :close_time_in_min,
  442. #CloseTimeDiffInMin: :close_time_diff_in_min,
  443. },
  444. Article: {
  445. SenderType: :sender,
  446. ArticleType: :type,
  447. TicketID: :ticket_id,
  448. ArticleID: :id,
  449. Body: :body,
  450. From: :from,
  451. To: :to,
  452. Cc: :cc,
  453. Subject: :subject,
  454. InReplyTo: :in_reply_to,
  455. MessageID: :message_id,
  456. #ReplyTo: :reply_to,
  457. References: :references,
  458. Changed: :updated_at,
  459. Created: :created_at,
  460. ChangedBy: :updated_by_id,
  461. CreatedBy: :created_by_id,
  462. },
  463. }
  464. result.each {|record|
  465. # cleanup values
  466. _cleanup(record)
  467. _utf8_encode(record)
  468. ticket_new = {
  469. title: '',
  470. created_by_id: 1,
  471. updated_by_id: 1,
  472. }
  473. map[:Ticket].each { |key, value|
  474. next if !record.key?(key.to_s)
  475. ticket_new[value] = record[key.to_s]
  476. }
  477. # find owner
  478. if ticket_new[:owner]
  479. user = User.lookup( login: ticket_new[:owner].downcase )
  480. if user
  481. ticket_new[:owner_id] = user.id
  482. else
  483. ticket_new[:owner_id] = 1
  484. end
  485. ticket_new.delete(:owner)
  486. end
  487. # find customer
  488. if ticket_new[:customer]
  489. user = User.lookup( login: ticket_new[:customer].downcase )
  490. if user
  491. ticket_new[:customer_id] = user.id
  492. else
  493. ticket_new[:customer_id] = 1
  494. end
  495. ticket_new.delete(:customer)
  496. else
  497. ticket_new[:customer_id] = 1
  498. end
  499. # update or create ticket
  500. ticket_old = Ticket.find_by(id: ticket_new[:id])
  501. if ticket_old
  502. log "update Ticket.find(#{ticket_new[:id]})"
  503. ticket_old.update_attributes(ticket_new)
  504. else
  505. log "add Ticket.find(#{ticket_new[:id]})"
  506. begin
  507. ticket = Ticket.new(ticket_new)
  508. ticket.id = ticket_new[:id]
  509. ticket.save
  510. rescue ActiveRecord::RecordNotUnique
  511. log "Ticket #{ticket_new[:id]} is handled by another thead, skipping."
  512. next
  513. end
  514. end
  515. # utf8 encode
  516. record['Articles'].each { |article|
  517. _utf8_encode(article)
  518. }
  519. # lookup customers to create first
  520. record['Articles'].each { |article|
  521. _article_based_customers(article)
  522. }
  523. record['Articles'].each do |article|
  524. retries = 3
  525. begin
  526. ActiveRecord::Base.transaction do
  527. # get article values
  528. article_new = {
  529. created_by_id: 1,
  530. updated_by_id: 1,
  531. }
  532. map[:Article].each { |key, value|
  533. next if !article.key?(key.to_s)
  534. article_new[value] = article[key.to_s]
  535. }
  536. if article_new[:sender] == 'customer'
  537. article_new[:sender_id] = Ticket::Article::Sender.lookup( name: 'Customer' ).id
  538. article_new.delete( :sender )
  539. end
  540. if article_new[:sender] == 'agent'
  541. article_new[:sender_id] = Ticket::Article::Sender.lookup( name: 'Agent' ).id
  542. article_new.delete( :sender )
  543. end
  544. if article_new[:sender] == 'system'
  545. article_new[:sender_id] = Ticket::Article::Sender.lookup( name: 'System' ).id
  546. article_new.delete( :sender )
  547. end
  548. if article_new[:type] == 'email-external'
  549. article_new[:type_id] = Ticket::Article::Type.lookup( name: 'email' ).id
  550. article_new[:internal] = false
  551. elsif article_new[:type] == 'email-internal'
  552. article_new[:type_id] = Ticket::Article::Type.lookup( name: 'email' ).id
  553. article_new[:internal] = true
  554. elsif article_new[:type] == 'note-external'
  555. article_new[:type_id] = Ticket::Article::Type.lookup( name: 'note' ).id
  556. article_new[:internal] = false
  557. elsif article_new[:type] == 'note-internal'
  558. article_new[:type_id] = Ticket::Article::Type.lookup( name: 'note' ).id
  559. article_new[:internal] = true
  560. elsif article_new[:type] == 'phone'
  561. article_new[:type_id] = Ticket::Article::Type.lookup( name: 'phone' ).id
  562. article_new[:internal] = false
  563. elsif article_new[:type] == 'webrequest'
  564. article_new[:type_id] = Ticket::Article::Type.lookup( name: 'web' ).id
  565. article_new[:internal] = false
  566. else
  567. article_new[:type_id] = 9
  568. end
  569. article_new.delete( :type )
  570. article_object = Ticket::Article.find_by( id: article_new[:id] )
  571. # set state types
  572. if article_object
  573. log "update Ticket::Article.find(#{article_new[:id]})"
  574. article_object.update_attributes(article_new)
  575. else
  576. log "add Ticket::Article.find(#{article_new[:id]})"
  577. begin
  578. article_object = Ticket::Article.new(article_new)
  579. article_object.id = article_new[:id]
  580. article_object.save
  581. rescue ActiveRecord::RecordNotUnique
  582. log "Ticket #{ticket_new[:id]} (article #{article_new[:id]}) is handled by another thead, skipping."
  583. next
  584. end
  585. end
  586. next if !article['Attachments']
  587. next if article['Attachments'].empty?
  588. # TODO: refactor
  589. # check if there are attachments present
  590. if !article_object.attachments.empty?
  591. # skip attachments if count is equal
  592. next if article_object.attachments.count == article['Attachments'].count
  593. # if the count differs delete all so we
  594. # can have a fresh start
  595. article_object.attachments.each(&:delete)
  596. end
  597. # import article attachments
  598. article['Attachments'].each { |attachment|
  599. filename = Base64.decode64(attachment['Filename'])
  600. Store.add(
  601. object: 'Ticket::Article',
  602. o_id: article_object.id,
  603. filename: filename,
  604. data: Base64.decode64(attachment['Content']),
  605. preferences: {
  606. 'Mime-Type' => attachment['ContentType'],
  607. 'Content-ID' => attachment['ContentID'],
  608. 'content-alternative' => attachment['ContentAlternative'],
  609. },
  610. created_by_id: 1,
  611. )
  612. }
  613. end
  614. rescue ActiveRecord::RecordNotUnique => e
  615. log "Ticket #{ticket_new[:id]} - RecordNotUnique: #{e}"
  616. sleep rand 3
  617. retry if !(retries -= 1).zero?
  618. raise
  619. end
  620. end
  621. #puts "HS: #{record['History'].inspect}"
  622. record['History'].each { |history|
  623. begin
  624. if history['HistoryType'] == 'NewTicket'
  625. History.add(
  626. id: history['HistoryID'],
  627. o_id: history['TicketID'],
  628. history_type: 'created',
  629. history_object: 'Ticket',
  630. created_at: history['CreateTime'],
  631. created_by_id: history['CreateBy']
  632. )
  633. elsif history['HistoryType'] == 'StateUpdate'
  634. data = history['Name']
  635. # "%%new%%open%%"
  636. from = nil
  637. to = nil
  638. if data =~ /%%(.+?)%%(.+?)%%/
  639. from = $1
  640. to = $2
  641. state_from = Ticket::State.lookup( name: from )
  642. state_to = Ticket::State.lookup( name: to )
  643. if state_from
  644. from_id = state_from.id
  645. end
  646. if state_to
  647. to_id = state_to.id
  648. end
  649. end
  650. History.add(
  651. id: history['HistoryID'],
  652. o_id: history['TicketID'],
  653. history_type: 'updated',
  654. history_object: 'Ticket',
  655. history_attribute: 'state',
  656. value_from: from,
  657. id_from: from_id,
  658. value_to: to,
  659. id_to: to_id,
  660. created_at: history['CreateTime'],
  661. created_by_id: history['CreateBy']
  662. )
  663. elsif history['HistoryType'] == 'Move'
  664. data = history['Name']
  665. # "%%Queue1%%5%%Postmaster%%1"
  666. from = nil
  667. to = nil
  668. if data =~ /%%(.+?)%%(.+?)%%(.+?)%%(.+?)$/
  669. from = $1
  670. from_id = $2
  671. to = $3
  672. to_id = $4
  673. end
  674. History.add(
  675. id: history['HistoryID'],
  676. o_id: history['TicketID'],
  677. history_type: 'updated',
  678. history_object: 'Ticket',
  679. history_attribute: 'group',
  680. value_from: from,
  681. value_to: to,
  682. id_from: from_id,
  683. id_to: to_id,
  684. created_at: history['CreateTime'],
  685. created_by_id: history['CreateBy']
  686. )
  687. elsif history['HistoryType'] == 'PriorityUpdate'
  688. data = history['Name']
  689. # "%%3 normal%%3%%5 very high%%5"
  690. from = nil
  691. to = nil
  692. if data =~ /%%(.+?)%%(.+?)%%(.+?)%%(.+?)$/
  693. from = $1
  694. from_id = $2
  695. to = $3
  696. to_id = $4
  697. end
  698. History.add(
  699. id: history['HistoryID'],
  700. o_id: history['TicketID'],
  701. history_type: 'updated',
  702. history_object: 'Ticket',
  703. history_attribute: 'priority',
  704. value_from: from,
  705. value_to: to,
  706. id_from: from_id,
  707. id_to: to_id,
  708. created_at: history['CreateTime'],
  709. created_by_id: history['CreateBy']
  710. )
  711. elsif history['ArticleID'] && !history['ArticleID'].to_i.zero?
  712. History.add(
  713. id: history['HistoryID'],
  714. o_id: history['ArticleID'],
  715. history_type: 'created',
  716. history_object: 'Ticket::Article',
  717. related_o_id: history['TicketID'],
  718. related_history_object: 'Ticket',
  719. created_at: history['CreateTime'],
  720. created_by_id: history['CreateBy']
  721. )
  722. end
  723. rescue ActiveRecord::RecordNotUnique
  724. log "Ticket #{ticket_new[:id]} (history #{history['HistoryID']}) is handled by another thead, skipping."
  725. next
  726. end
  727. }
  728. }
  729. end
  730. # sync ticket states
  731. def self.state(records)
  732. map = {
  733. ChangeTime: :updated_at,
  734. CreateTime: :created_at,
  735. CreateBy: :created_by_id,
  736. ChangeBy: :updated_by_id,
  737. Name: :name,
  738. ID: :id,
  739. ValidID: :active,
  740. Comment: :note,
  741. }
  742. # rename states to handle not uniq issues
  743. Ticket::State.all.each {|state|
  744. state.name = state.name + '_tmp'
  745. state.save
  746. }
  747. records.each { |state|
  748. _set_valid(state)
  749. # get new attributes
  750. state_new = {
  751. created_by_id: 1,
  752. updated_by_id: 1,
  753. }
  754. map.each { |key, value|
  755. next if !state.key?(key.to_s)
  756. state_new[value] = state[key.to_s]
  757. }
  758. # check if state already exists
  759. state_old = Ticket::State.where( id: state_new[:id] ).first
  760. # set state types
  761. if state['TypeName'] == 'pending auto'
  762. state['TypeName'] = 'pending action'
  763. end
  764. state_type = Ticket::StateType.where( name: state['TypeName'] ).first
  765. state_new[:state_type_id] = state_type.id
  766. if state_old
  767. state_old.update_attributes(state_new)
  768. else
  769. state = Ticket::State.new(state_new)
  770. state.id = state_new[:id]
  771. state.save
  772. end
  773. }
  774. end
  775. # sync ticket priorities
  776. def self.priority(records)
  777. map = {
  778. ChangeTime: :updated_at,
  779. CreateTime: :created_at,
  780. CreateBy: :created_by_id,
  781. ChangeBy: :updated_by_id,
  782. Name: :name,
  783. ID: :id,
  784. ValidID: :active,
  785. Comment: :note,
  786. }
  787. records.each { |priority|
  788. _set_valid(priority)
  789. # get new attributes
  790. priority_new = {
  791. created_by_id: 1,
  792. updated_by_id: 1,
  793. }
  794. map.each { |key, value|
  795. next if !priority.key?(key.to_s)
  796. priority_new[value] = priority[key.to_s]
  797. }
  798. # check if state already exists
  799. priority_old = Ticket::Priority.where( id: priority_new[:id] ).first
  800. # set state types
  801. if priority_old
  802. priority_old.update_attributes(priority_new)
  803. else
  804. priority = Ticket::Priority.new(priority_new)
  805. priority.id = priority_new[:id]
  806. priority.save
  807. end
  808. }
  809. end
  810. # sync ticket groups / queues
  811. def self.ticket_group(records)
  812. map = {
  813. ChangeTime: :updated_at,
  814. CreateTime: :created_at,
  815. CreateBy: :created_by_id,
  816. ChangeBy: :updated_by_id,
  817. Name: :name,
  818. QueueID: :id,
  819. ValidID: :active,
  820. Comment: :note,
  821. }
  822. records.each { |group|
  823. _set_valid(group)
  824. # get new attributes
  825. group_new = {
  826. created_by_id: 1,
  827. updated_by_id: 1,
  828. }
  829. map.each { |key, value|
  830. next if !group.key?(key.to_s)
  831. group_new[value] = group[key.to_s]
  832. }
  833. # check if state already exists
  834. group_old = Group.where( id: group_new[:id] ).first
  835. # set state types
  836. if group_old
  837. group_old.update_attributes(group_new)
  838. else
  839. group = Group.new(group_new)
  840. group.id = group_new[:id]
  841. group.save
  842. end
  843. }
  844. end
  845. # sync agents
  846. def self.user(records, groups, roles, queues)
  847. map = {
  848. ChangeTime: :updated_at,
  849. CreateTime: :created_at,
  850. CreateBy: :created_by_id,
  851. ChangeBy: :updated_by_id,
  852. UserID: :id,
  853. ValidID: :active,
  854. Comment: :note,
  855. UserEmail: :email,
  856. UserFirstname: :firstname,
  857. UserLastname: :lastname,
  858. UserLogin: :login,
  859. UserPw: :password,
  860. }
  861. records.each { |user|
  862. _set_valid(user)
  863. # get roles
  864. role_ids = get_roles_ids(user, groups, roles, queues)
  865. # get groups
  866. group_ids = get_queue_ids(user, groups, roles, queues)
  867. # get new attributes
  868. user_new = {
  869. created_by_id: 1,
  870. updated_by_id: 1,
  871. source: 'OTRS Import',
  872. role_ids: role_ids,
  873. group_ids: group_ids,
  874. }
  875. map.each { |key, value|
  876. next if !user.key?(key.to_s)
  877. user_new[value] = user[key.to_s]
  878. }
  879. # set pw
  880. if user_new[:password]
  881. user_new[:password] = "{sha2}#{user_new[:password]}"
  882. end
  883. # check if agent already exists
  884. user_old = User.where( id: user_new[:id] ).first
  885. # check if login is already used
  886. login_in_use = User.where( "login = ? AND id != #{user_new[:id]}", user_new[:login].downcase ).count
  887. if login_in_use > 0
  888. user_new[:login] = "#{user_new[:login]}_#{user_new[:id]}"
  889. end
  890. # create / update agent
  891. if user_old
  892. log "update User.find(#{user_old[:id]})"
  893. # only update roles if different (reduce sql statements)
  894. if user_old.role_ids == user_new[:role_ids]
  895. user_new.delete( :role_ids )
  896. end
  897. user_old.update_attributes(user_new)
  898. else
  899. log "add User.find(#{user_new[:id]})"
  900. user = User.new(user_new)
  901. user.id = user_new[:id]
  902. user.save
  903. end
  904. }
  905. end
  906. def self.get_queue_ids(user, _groups, _roles, queues)
  907. queue_ids = []
  908. # lookup by groups
  909. user['GroupIDs'].each {|group_id, permissions|
  910. queues.each {|queue_lookup|
  911. next if queue_lookup['GroupID'] != group_id
  912. next if !permissions
  913. next if !permissions.include?('rw')
  914. queue_ids.push queue_lookup['QueueID']
  915. }
  916. }
  917. # lookup by roles
  918. # roles of user
  919. # groups of roles
  920. # queues of group
  921. queue_ids
  922. end
  923. def self.get_roles_ids(user, groups, roles, _queues)
  924. local_roles = ['Agent']
  925. local_role_ids = []
  926. # apply group permissions
  927. user['GroupIDs'].each {|group_id, permissions|
  928. groups.each {|group_lookup|
  929. next if group_id != group_lookup['ID']
  930. next if !permissions
  931. if group_lookup['Name'] == 'admin' && permissions.include?('rw')
  932. local_roles.push 'Admin'
  933. end
  934. next if group_lookup['Name'] !~ /^(stats|report)/
  935. next if !( permissions.include?('ro') || permissions.include?('rw') )
  936. local_roles.push 'Report'
  937. }
  938. }
  939. # apply role permissions
  940. user['RoleIDs'].each {|role_id|
  941. # get groups of role
  942. roles.each {|role|
  943. next if role['ID'] != role_id
  944. # verify group names
  945. role['GroupIDs'].each {|group_id, permissions|
  946. groups.each {|group_lookup|
  947. next if group_id != group_lookup['ID']
  948. next if !permissions
  949. if group_lookup['Name'] == 'admin' && permissions.include?('rw')
  950. local_roles.push 'Admin'
  951. end
  952. next if group_lookup['Name'] !~ /^(stats|report)/
  953. next if !( permissions.include?('ro') || permissions.include?('rw') )
  954. local_roles.push 'Report'
  955. }
  956. }
  957. }
  958. }
  959. local_roles.each {|role|
  960. role_lookup = Role.lookup( name: role )
  961. next if !role_lookup
  962. local_role_ids.push role_lookup.id
  963. }
  964. local_role_ids
  965. end
  966. # sync customers
  967. def self.customer(records, organizations)
  968. map = {
  969. ChangeTime: :updated_at,
  970. CreateTime: :created_at,
  971. CreateBy: :created_by_id,
  972. ChangeBy: :updated_by_id,
  973. ValidID: :active,
  974. UserComment: :note,
  975. UserEmail: :email,
  976. UserFirstname: :firstname,
  977. UserLastname: :lastname,
  978. UserLogin: :login,
  979. UserPassword: :password,
  980. UserPhone: :phone,
  981. UserFax: :fax,
  982. UserMobile: :mobile,
  983. UserStreet: :street,
  984. UserZip: :zip,
  985. UserCity: :city,
  986. UserCountry: :country,
  987. }
  988. role_agent = Role.lookup( name: 'Agent' )
  989. role_customer = Role.lookup( name: 'Customer' )
  990. records.each { |user|
  991. _set_valid(user)
  992. # get new attributes
  993. user_new = {
  994. created_by_id: 1,
  995. updated_by_id: 1,
  996. source: 'OTRS Import',
  997. organization_id: get_organization_id(user, organizations),
  998. role_ids: [ role_customer.id ],
  999. }
  1000. map.each { |key, value|
  1001. next if !user.key?(key.to_s)
  1002. user_new[value] = user[key.to_s]
  1003. }
  1004. # check if customer already exists
  1005. user_old = User.where( login: user_new[:login].downcase ).first
  1006. # create / update agent
  1007. if user_old
  1008. # do not update user if it is already agent
  1009. if !user_old.role_ids.include?( role_agent.id )
  1010. # only update roles if different (reduce sql statements)
  1011. if user_old.role_ids == user_new[:role_ids]
  1012. user_new.delete( :role_ids )
  1013. end
  1014. log "update User.find(#{user_old[:id]})"
  1015. user_old.update_attributes(user_new)
  1016. end
  1017. else
  1018. log "add User.find(#{user_new[:id]})"
  1019. user = User.new(user_new)
  1020. user.save
  1021. end
  1022. }
  1023. end
  1024. def self.get_organization_id(user, organizations)
  1025. organization_id = nil
  1026. if user['UserCustomerID']
  1027. organizations.each {|organization|
  1028. next if user['UserCustomerID'] != organization['CustomerID']
  1029. organization = Organization.where(name: organization['CustomerCompanyName'] ).first
  1030. organization_id = organization.id
  1031. }
  1032. end
  1033. organization_id
  1034. end
  1035. # sync organizations
  1036. def self.organization(records)
  1037. map = {
  1038. ChangeTime: :updated_at,
  1039. CreateTime: :created_at,
  1040. CreateBy: :created_by_id,
  1041. ChangeBy: :updated_by_id,
  1042. CustomerCompanyName: :name,
  1043. ValidID: :active,
  1044. CustomerCompanyComment: :note,
  1045. }
  1046. records.each { |organization|
  1047. _set_valid(organization)
  1048. # get new attributes
  1049. organization_new = {
  1050. created_by_id: 1,
  1051. updated_by_id: 1,
  1052. }
  1053. map.each { |key, value|
  1054. next if !organization.key?(key.to_s)
  1055. organization_new[value] = organization[key.to_s]
  1056. }
  1057. # check if state already exists
  1058. organization_old = Organization.where( name: organization_new[:name] ).first
  1059. # set state types
  1060. if organization_old
  1061. organization_old.update_attributes(organization_new)
  1062. else
  1063. organization = Organization.new(organization_new)
  1064. organization.id = organization_new[:id]
  1065. organization.save
  1066. end
  1067. }
  1068. end
  1069. # sync settings
  1070. def self.setting(records)
  1071. records.each { |setting|
  1072. # fqdn
  1073. if setting['Key'] == 'FQDN'
  1074. Setting.set( 'fqdn', setting['Value'] )
  1075. end
  1076. # http type
  1077. if setting['Key'] == 'HttpType'
  1078. Setting.set( 'http_type', setting['Value'] )
  1079. end
  1080. # system id
  1081. if setting['Key'] == 'SystemID'
  1082. Setting.set( 'system_id', setting['Value'] )
  1083. end
  1084. # organization
  1085. if setting['Key'] == 'Organization'
  1086. Setting.set( 'organization', setting['Value'] )
  1087. end
  1088. # sending emails
  1089. if setting['Key'] == 'SendmailModule'
  1090. # TODO
  1091. end
  1092. # number generater
  1093. if setting['Key'] == 'Ticket::NumberGenerator'
  1094. if setting['Value'] == 'Kernel::System::Ticket::Number::DateChecksum'
  1095. Setting.set( 'ticket_number', 'Ticket::Number::Date' )
  1096. Setting.set( 'ticket_number_date', { checksum: true } )
  1097. elsif setting['Value'] == 'Kernel::System::Ticket::Number::Date'
  1098. Setting.set( 'ticket_number', 'Ticket::Number::Date' )
  1099. Setting.set( 'ticket_number_date', { checksum: false } )
  1100. end
  1101. end
  1102. # ticket hook
  1103. if setting['Key'] == 'Ticket::Hook'
  1104. Setting.set( 'ticket_hook', setting['Value'] )
  1105. end
  1106. }
  1107. end
  1108. # log
  1109. def self.log(message)
  1110. thread_no = Thread.current[:thread_no] || '-'
  1111. Rails.logger.info "thread##{thread_no}: #{message}"
  1112. end
  1113. # set translate valid ids to active = true|false
  1114. def self._set_valid(record)
  1115. # map
  1116. if record['ValidID'].to_s == '3'
  1117. record['ValidID'] = false
  1118. elsif record['ValidID'].to_s == '2'
  1119. record['ValidID'] = false
  1120. elsif record['ValidID'].to_s == '1'
  1121. record['ValidID'] = true
  1122. elsif record['ValidID'].to_s == '0'
  1123. record['ValidID'] = false
  1124. # fallback
  1125. else
  1126. record['ValidID'] = true
  1127. end
  1128. end
  1129. # cleanup invalid values
  1130. def self._cleanup(record)
  1131. record.each {|key, value|
  1132. if value == '0000-00-00 00:00:00'
  1133. record[key] = nil
  1134. end
  1135. }
  1136. # fix OTRS 3.1 bug, no close time if ticket is created
  1137. if record['StateType'] == 'closed' && ( !record['Closed'] || record['Closed'].empty? )
  1138. record['Closed'] = record['Created']
  1139. end
  1140. end
  1141. # utf8 convert
  1142. def self._utf8_encode(data)
  1143. data.each { |key, value|
  1144. next if !value
  1145. next if value.class != String
  1146. data[key] = Encode.conv( 'utf8', value )
  1147. }
  1148. end
  1149. # create customers for article
  1150. def self._article_based_customers(article)
  1151. # create customer/sender if needed
  1152. return if article['sender'] != 'customer'
  1153. return if article['created_by_id'].to_i != 1
  1154. return if article['from'].empty?
  1155. email = nil
  1156. begin
  1157. email = Mail::Address.new( article['from'] ).address
  1158. rescue
  1159. email = article['from']
  1160. if article['from'] =~ /<(.+?)>/
  1161. email = $1
  1162. end
  1163. end
  1164. user = User.where( email: email.downcase ).first
  1165. if !user
  1166. user = User.where( login: email.downcase ).first
  1167. end
  1168. if !user
  1169. begin
  1170. display_name = Mail::Address.new( article['from'] ).display_name ||
  1171. ( Mail::Address.new( article['from'] ).comments && Mail::Address.new( article['from'] ).comments[0] )
  1172. rescue
  1173. display_name = article['from']
  1174. end
  1175. # do extra decoding because we needed to use field.value
  1176. display_name = Mail::Field.new( 'X-From', display_name ).to_s
  1177. roles = Role.lookup( name: 'Customer' )
  1178. begin
  1179. user = User.create(
  1180. login: email,
  1181. firstname: display_name,
  1182. lastname: '',
  1183. email: email,
  1184. password: '',
  1185. active: true,
  1186. role_ids: [roles.id],
  1187. updated_by_id: 1,
  1188. created_by_id: 1,
  1189. )
  1190. rescue ActiveRecord::RecordNotUnique
  1191. log "User #{email} was handled by another thread, taking this."
  1192. user = User.find_by( login: email.downcase )
  1193. if !user
  1194. log "User #{email} wasn't created sleep and retry."
  1195. sleep rand 3
  1196. retry
  1197. end
  1198. end
  1199. end
  1200. article['created_by_id'] = user.id
  1201. true
  1202. end
  1203. end