tickets_controller.rb 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615
  1. # Copyright (C) 2012-2014 Zammad Foundation, http://zammad-foundation.org/
  2. class TicketsController < ApplicationController
  3. before_action :authentication_check
  4. # GET /api/v1/tickets
  5. def index
  6. offset = 0
  7. per_page = 100
  8. if params[:page] && params[:per_page]
  9. offset = (params[:page].to_i - 1) * params[:per_page].to_i
  10. per_page = params[:per_page].to_i
  11. end
  12. access_condition = Ticket.access_condition(current_user)
  13. tickets = Ticket.where(access_condition).offset(offset).limit(per_page)
  14. if params[:full]
  15. assets = {}
  16. item_ids = []
  17. tickets.each {|item|
  18. item_ids.push item.id
  19. assets = item.assets(assets)
  20. }
  21. render json: {
  22. record_ids: item_ids,
  23. assets: assets,
  24. }, status: :ok
  25. return
  26. end
  27. render json: tickets
  28. end
  29. # GET /api/v1/tickets/1
  30. def show
  31. # permission check
  32. ticket = Ticket.find(params[:id])
  33. return if !ticket_permission(ticket)
  34. if params[:full]
  35. render json: ticket_full(ticket)
  36. return
  37. end
  38. render json: ticket
  39. end
  40. # POST /api/v1/tickets
  41. def create
  42. clean_params = Ticket.param_association_lookup(params)
  43. clean_params = Ticket.param_cleanup(clean_params, true)
  44. ticket = Ticket.new(clean_params)
  45. # check if article is given
  46. if !params[:article]
  47. render json: { error: 'article hash is missing' }, status: :unprocessable_entity
  48. return
  49. end
  50. # create ticket
  51. if !ticket.save
  52. render json: ticket.errors, status: :unprocessable_entity
  53. return
  54. end
  55. # create tags if given
  56. if params[:tags] && !params[:tags].empty?
  57. tags = params[:tags].split(/,/)
  58. tags.each {|tag|
  59. Tag.tag_add(
  60. object: 'Ticket',
  61. o_id: ticket.id,
  62. item: tag,
  63. created_by_id: current_user.id,
  64. )
  65. }
  66. end
  67. # create article if given
  68. if params[:article]
  69. article_create(ticket, params[:article])
  70. end
  71. render json: ticket, status: :created
  72. end
  73. # PUT /api/v1/tickets/1
  74. def update
  75. # permission check
  76. ticket = Ticket.find(params[:id])
  77. return if !ticket_permission(ticket)
  78. clean_params = Ticket.param_association_lookup(params)
  79. clean_params = Ticket.param_cleanup(clean_params, true)
  80. if ticket.update_attributes(clean_params)
  81. if params[:article]
  82. article_create(ticket, params[:article])
  83. end
  84. render json: ticket, status: :ok
  85. else
  86. render json: ticket.errors, status: :unprocessable_entity
  87. end
  88. end
  89. # DELETE /api/v1/tickets/1
  90. def destroy
  91. # permission check
  92. ticket = Ticket.find(params[:id])
  93. return if !ticket_permission(ticket)
  94. ticket.destroy
  95. head :ok
  96. end
  97. # GET /api/v1/ticket_customer
  98. # GET /api/v1/tickets_customer
  99. def ticket_customer
  100. # return result
  101. result = Ticket::ScreenOptions.list_by_customer(
  102. customer_id: params[:customer_id],
  103. limit: 15,
  104. )
  105. render json: result
  106. end
  107. # GET /api/v1/ticket_history/1
  108. def ticket_history
  109. # get ticket data
  110. ticket = Ticket.find(params[:id])
  111. # permission check
  112. return if !ticket_permission(ticket)
  113. # get history of ticket
  114. history = ticket.history_get(true)
  115. # return result
  116. render json: history
  117. end
  118. # GET /api/v1/ticket_related/1
  119. def ticket_related
  120. ticket = Ticket.find(params[:ticket_id])
  121. assets = ticket.assets({})
  122. # open tickets by customer
  123. access_condition = Ticket.access_condition(current_user)
  124. ticket_lists = Ticket
  125. .where(
  126. customer_id: ticket.customer_id,
  127. state_id: Ticket::State.by_category('open')
  128. )
  129. .where(access_condition)
  130. .where('id != ?', [ ticket.id ])
  131. .order('created_at DESC')
  132. .limit(6)
  133. # get related assets
  134. ticket_ids_by_customer = []
  135. ticket_lists.each {|ticket_list|
  136. ticket_ids_by_customer.push ticket_list.id
  137. assets = ticket_list.assets(assets)
  138. }
  139. ticket_ids_recent_viewed = []
  140. recent_views = RecentView.list(current_user, 8, 'Ticket')
  141. recent_views.each {|recent_view|
  142. next if recent_view['object'] != 'Ticket'
  143. ticket_ids_recent_viewed.push recent_view['o_id']
  144. recent_view_ticket = Ticket.find(recent_view['o_id'])
  145. assets = recent_view_ticket.assets(assets)
  146. }
  147. # return result
  148. render json: {
  149. assets: assets,
  150. ticket_ids_by_customer: ticket_ids_by_customer,
  151. ticket_ids_recent_viewed: ticket_ids_recent_viewed,
  152. }
  153. end
  154. # GET /api/v1/ticket_merge/1/1
  155. def ticket_merge
  156. # check master ticket
  157. ticket_master = Ticket.find_by(number: params[:master_ticket_number])
  158. if !ticket_master
  159. render json: {
  160. result: 'faild',
  161. message: 'No such master ticket number!',
  162. }
  163. return
  164. end
  165. # permission check
  166. return if !ticket_permission(ticket_master)
  167. # check slave ticket
  168. ticket_slave = Ticket.find_by(id: params[:slave_ticket_id])
  169. if !ticket_slave
  170. render json: {
  171. result: 'faild',
  172. message: 'No such slave ticket!',
  173. }
  174. return
  175. end
  176. # permission check
  177. return if !ticket_permission(ticket_slave)
  178. # check diffetent ticket ids
  179. if ticket_slave.id == ticket_master.id
  180. render json: {
  181. result: 'faild',
  182. message: 'Can\'t merge ticket with it self!',
  183. }
  184. return
  185. end
  186. # merge ticket
  187. ticket_slave.merge_to(
  188. ticket_id: ticket_master.id,
  189. created_by_id: current_user.id,
  190. )
  191. # return result
  192. render json: {
  193. result: 'success',
  194. master_ticket: ticket_master.attributes,
  195. slave_ticket: ticket_slave.attributes,
  196. }
  197. end
  198. # GET /api/v1/ticket_split
  199. def ticket_split
  200. # permission check
  201. ticket = Ticket.find(params[:ticket_id])
  202. return if !ticket_permission(ticket)
  203. assets = ticket.assets({})
  204. # get related articles
  205. article = Ticket::Article.find(params[:article_id])
  206. assets = article.assets(assets)
  207. render json: {
  208. assets: assets
  209. }
  210. end
  211. # GET /api/v1/ticket_create
  212. def ticket_create
  213. # get attributes to update
  214. attributes_to_change = Ticket::ScreenOptions.attributes_to_change(
  215. user: current_user,
  216. )
  217. render json: attributes_to_change
  218. end
  219. # GET /api/v1/tickets/search
  220. def search
  221. # permit nested conditions
  222. params.require(:condition).permit!
  223. # build result list
  224. tickets = Ticket.search(
  225. limit: params[:limit],
  226. query: params[:term],
  227. condition: params[:condition],
  228. current_user: current_user,
  229. )
  230. assets = {}
  231. ticket_result = []
  232. tickets.each do |ticket|
  233. ticket_result.push ticket.id
  234. assets = ticket.assets(assets)
  235. end
  236. # return result
  237. render json: {
  238. tickets: ticket_result,
  239. tickets_count: tickets.count,
  240. assets: assets,
  241. }
  242. end
  243. # GET /api/v1/tickets/selector
  244. def selector
  245. return if deny_if_not_role(Z_ROLENAME_ADMIN)
  246. ticket_count, tickets = Ticket.selectors(params[:condition], 6)
  247. assets = {}
  248. ticket_ids = []
  249. if tickets
  250. tickets.each do |ticket|
  251. ticket_ids.push ticket.id
  252. assets = ticket.assets(assets)
  253. end
  254. end
  255. # return result
  256. render json: {
  257. ticket_ids: ticket_ids,
  258. ticket_count: ticket_count || 0,
  259. assets: assets,
  260. }
  261. end
  262. # GET /api/v1/ticket_stats
  263. def stats
  264. if !params[:user_id] && !params[:organization_id]
  265. raise 'Need user_id or organization_id as param'
  266. end
  267. # permission check
  268. #return if !ticket_permission(ticket)
  269. # lookup open user tickets
  270. limit = 100
  271. assets = {}
  272. access_condition = Ticket.access_condition(current_user)
  273. now = Time.zone.now
  274. user_tickets_open_ids = []
  275. user_tickets_closed_ids = []
  276. user_ticket_volume_by_year = []
  277. if params[:user_id]
  278. user = User.lookup(id: params[:user_id])
  279. condition = {
  280. 'ticket.state_id' => {
  281. operator: 'is',
  282. value: Ticket::State.by_category('open').map(&:id),
  283. },
  284. 'ticket.customer_id' => {
  285. operator: 'is',
  286. value: user.id,
  287. },
  288. }
  289. user_tickets_open = Ticket.search(
  290. limit: limit,
  291. condition: condition,
  292. current_user: current_user,
  293. )
  294. user_tickets_open_ids = assets_of_tickets(user_tickets_open, assets)
  295. # lookup closed user tickets
  296. condition = {
  297. 'ticket.state_id' => {
  298. operator: 'is',
  299. value: Ticket::State.by_category('closed').map(&:id),
  300. },
  301. 'ticket.customer_id' => {
  302. operator: 'is',
  303. value: user.id,
  304. },
  305. }
  306. user_tickets_closed = Ticket.search(
  307. limit: limit,
  308. condition: condition,
  309. current_user: current_user,
  310. )
  311. user_tickets_closed_ids = assets_of_tickets(user_tickets_closed, assets)
  312. # generate stats by user
  313. (0..11).each {|month_back|
  314. date_to_check = now - month_back.month
  315. date_start = "#{date_to_check.year}-#{date_to_check.month}-01 00:00:00"
  316. date_end = "#{date_to_check.year}-#{date_to_check.month}-#{date_to_check.end_of_month.day} 00:00:00"
  317. condition = {
  318. 'tickets.customer_id' => user.id,
  319. }
  320. # created
  321. created = Ticket.where('created_at > ? AND created_at < ?', date_start, date_end )
  322. .where(access_condition)
  323. .where(condition)
  324. .count
  325. # closed
  326. closed = Ticket.where('close_time > ? AND close_time < ?', date_start, date_end )
  327. .where(access_condition)
  328. .where(condition)
  329. .count
  330. data = {
  331. month: date_to_check.month,
  332. year: date_to_check.year,
  333. text: Date::MONTHNAMES[date_to_check.month],
  334. created: created,
  335. closed: closed,
  336. }
  337. user_ticket_volume_by_year.push data
  338. }
  339. end
  340. # lookup open org tickets
  341. org_tickets_open_ids = []
  342. org_tickets_closed_ids = []
  343. org_ticket_volume_by_year = []
  344. if params[:organization_id] && !params[:organization_id].empty?
  345. condition = {
  346. 'ticket.state_id' => {
  347. operator: 'is',
  348. value: Ticket::State.by_category('open').map(&:id),
  349. },
  350. 'ticket.organization_id' => {
  351. operator: 'is',
  352. value: params[:organization_id],
  353. },
  354. }
  355. org_tickets_open = Ticket.search(
  356. limit: limit,
  357. condition: condition,
  358. current_user: current_user,
  359. )
  360. org_tickets_open_ids = assets_of_tickets(org_tickets_open, assets)
  361. # lookup closed org tickets
  362. condition = {
  363. 'ticket.state_id' => {
  364. operator: 'is',
  365. value: Ticket::State.by_category('closed').map(&:id),
  366. },
  367. 'ticket.organization_id' => {
  368. operator: 'is',
  369. value: params[:organization_id],
  370. },
  371. }
  372. org_tickets_closed = Ticket.search(
  373. limit: limit,
  374. condition: condition,
  375. current_user: current_user,
  376. )
  377. org_tickets_closed_ids = assets_of_tickets(org_tickets_closed, assets)
  378. # generate stats by org
  379. (0..11).each {|month_back|
  380. date_to_check = now - month_back.month
  381. date_start = "#{date_to_check.year}-#{date_to_check.month}-01 00:00:00"
  382. date_end = "#{date_to_check.year}-#{date_to_check.month}-#{date_to_check.end_of_month.day} 00:00:00"
  383. condition = {
  384. 'tickets.organization_id' => params[:organization_id],
  385. }
  386. # created
  387. created = Ticket.where('created_at > ? AND created_at < ?', date_start, date_end ).where(condition).count
  388. # closed
  389. closed = Ticket.where('close_time > ? AND close_time < ?', date_start, date_end ).where(condition).count
  390. data = {
  391. month: date_to_check.month,
  392. year: date_to_check.year,
  393. text: Date::MONTHNAMES[date_to_check.month],
  394. created: created,
  395. closed: closed,
  396. }
  397. org_ticket_volume_by_year.push data
  398. }
  399. end
  400. # return result
  401. render json: {
  402. user_tickets_open_ids: user_tickets_open_ids,
  403. user_tickets_closed_ids: user_tickets_closed_ids,
  404. org_tickets_open_ids: org_tickets_open_ids,
  405. org_tickets_closed_ids: org_tickets_closed_ids,
  406. user_ticket_volume_by_year: user_ticket_volume_by_year,
  407. org_ticket_volume_by_year: org_ticket_volume_by_year,
  408. assets: assets,
  409. }
  410. end
  411. private
  412. def assets_of_tickets(tickets, assets)
  413. ticket_ids = []
  414. tickets.each do |ticket|
  415. ticket_ids.push ticket.id
  416. assets = ticket.assets(assets)
  417. end
  418. ticket_ids
  419. end
  420. def article_create(ticket, params)
  421. # create article if given
  422. form_id = params[:form_id]
  423. params.delete(:form_id)
  424. clean_params = Ticket::Article.param_association_lookup(params)
  425. clean_params = Ticket::Article.param_cleanup(clean_params, true)
  426. article = Ticket::Article.new(clean_params)
  427. article.ticket_id = ticket.id
  428. # store dataurl images to store
  429. if form_id && article.body && article.content_type =~ %r{text/html}i
  430. article.body.gsub!( %r{(<img\s.+?src=")(data:image/(jpeg|png);base64,.+?)">}i ) { |_item|
  431. file_attributes = StaticAssets.data_url_attributes($2)
  432. cid = "#{ticket.id}.#{form_id}.#{rand(999_999)}@#{Setting.get('fqdn')}"
  433. headers_store = {
  434. 'Content-Type' => file_attributes[:mime_type],
  435. 'Mime-Type' => file_attributes[:mime_type],
  436. 'Content-ID' => cid,
  437. 'Content-Disposition' => 'inline',
  438. }
  439. store = Store.add(
  440. object: 'UploadCache',
  441. o_id: form_id,
  442. data: file_attributes[:content],
  443. filename: cid,
  444. preferences: headers_store
  445. )
  446. "#{$1}cid:#{cid}\">"
  447. }
  448. end
  449. # find attachments in upload cache
  450. if form_id
  451. article.attachments = Store.list(
  452. object: 'UploadCache',
  453. o_id: form_id,
  454. )
  455. end
  456. if !article.save
  457. render json: article.errors, status: :unprocessable_entity
  458. return
  459. end
  460. # remove attachments from upload cache
  461. return if !form_id
  462. Store.remove(
  463. object: 'UploadCache',
  464. o_id: form_id,
  465. )
  466. end
  467. def ticket_full(ticket)
  468. # get attributes to update
  469. attributes_to_change = Ticket::ScreenOptions.attributes_to_change(user: current_user, ticket: ticket)
  470. # get related users
  471. assets = attributes_to_change[:assets]
  472. assets = ticket.assets(assets)
  473. # get related articles
  474. articles = Ticket::Article.where(ticket_id: ticket.id).order('created_at ASC, id ASC')
  475. # get related users
  476. article_ids = []
  477. articles.each {|article|
  478. # ignore internal article if customer is requesting
  479. next if article.internal == true && role?(Z_ROLENAME_CUSTOMER)
  480. # load article ids
  481. article_ids.push article.id
  482. # load assets
  483. assets = article.assets(assets)
  484. }
  485. # get links
  486. links = Link.list(
  487. link_object: 'Ticket',
  488. link_object_value: ticket.id,
  489. )
  490. link_list = []
  491. links.each { |item|
  492. link_list.push item
  493. if item['link_object'] == 'Ticket'
  494. linked_ticket = Ticket.lookup(id: item['link_object_value'])
  495. assets = linked_ticket.assets(assets)
  496. end
  497. }
  498. # get tags
  499. tags = Tag.tag_list(
  500. object: 'Ticket',
  501. o_id: ticket.id,
  502. )
  503. # return result
  504. {
  505. ticket_id: ticket.id,
  506. ticket_article_ids: article_ids,
  507. assets: assets,
  508. links: link_list,
  509. tags: tags,
  510. form_meta: attributes_to_change[:form_meta],
  511. }
  512. end
  513. end