tickets_controller.rb 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626
  1. # Copyright (C) 2012-2016 Zammad Foundation, http://zammad-foundation.org/
  2. class TicketsController < ApplicationController
  3. include AccessesTickets
  4. include CreatesTicketArticles
  5. include TicketStats
  6. prepend_before_action :authentication_check
  7. # GET /api/v1/tickets
  8. def index
  9. offset = 0
  10. per_page = 100
  11. if params[:page] && params[:per_page]
  12. offset = (params[:page].to_i - 1) * params[:per_page].to_i
  13. per_page = params[:per_page].to_i
  14. end
  15. if per_page > 100
  16. per_page = 100
  17. end
  18. access_condition = Ticket.access_condition(current_user)
  19. tickets = Ticket.where(access_condition).order(id: 'ASC').offset(offset).limit(per_page)
  20. if params[:expand]
  21. list = []
  22. tickets.each { |ticket|
  23. list.push ticket.attributes_with_association_names
  24. }
  25. render json: list, status: :ok
  26. return
  27. end
  28. if params[:full]
  29. assets = {}
  30. item_ids = []
  31. tickets.each { |item|
  32. item_ids.push item.id
  33. assets = item.assets(assets)
  34. }
  35. render json: {
  36. record_ids: item_ids,
  37. assets: assets,
  38. }, status: :ok
  39. return
  40. end
  41. render json: tickets
  42. end
  43. # GET /api/v1/tickets/1
  44. def show
  45. # permission check
  46. ticket = Ticket.find(params[:id])
  47. ticket_permission(ticket)
  48. if params[:expand]
  49. result = ticket.attributes_with_association_names
  50. render json: result, status: :ok
  51. return
  52. end
  53. if params[:full]
  54. full = Ticket.full(params[:id])
  55. render json: full
  56. return
  57. end
  58. if params[:all]
  59. render json: ticket_all(ticket)
  60. return
  61. end
  62. render json: ticket
  63. end
  64. # POST /api/v1/tickets
  65. def create
  66. clean_params = Ticket.association_name_to_id_convert(params)
  67. # overwrite params
  68. if !current_user.permissions?('ticket.agent')
  69. [:owner, :owner_id, :customer, :customer_id, :organization, :organization_id, :preferences].each { |key|
  70. clean_params.delete(key)
  71. }
  72. clean_params[:customer_id] = current_user.id
  73. end
  74. # try to create customer if needed
  75. if clean_params[:customer_id] && clean_params[:customer_id] =~ /^guess:(.+?)$/
  76. email = $1
  77. if email !~ /@/ || email =~ /(>|<|\||\!|"|§|'|\$|%|&|\(|\)|\?|\s)/
  78. render json: { error: 'Invalid email of customer' }, status: :unprocessable_entity
  79. return
  80. end
  81. customer = User.find_by(email: email)
  82. if !customer
  83. role_ids = Role.signup_role_ids
  84. customer = User.create(
  85. firstname: '',
  86. lastname: '',
  87. email: email,
  88. password: '',
  89. active: true,
  90. role_ids: role_ids,
  91. )
  92. end
  93. clean_params[:customer_id] = customer.id
  94. end
  95. clean_params = Ticket.param_cleanup(clean_params, true)
  96. ticket = Ticket.new(clean_params)
  97. # check if article is given
  98. if !params[:article]
  99. render json: { error: 'article hash is missing' }, status: :unprocessable_entity
  100. return
  101. end
  102. # create ticket
  103. ticket.save!
  104. ticket.with_lock do
  105. # create tags if given
  106. if params[:tags] && !params[:tags].empty?
  107. tags = params[:tags].split(/,/)
  108. tags.each { |tag|
  109. ticket.tag_add(tag)
  110. }
  111. end
  112. # create article if given
  113. if params[:article]
  114. article_create(ticket, params[:article])
  115. end
  116. end
  117. # create links (e. g. in case of ticket split)
  118. # links: {
  119. # Ticket: {
  120. # parent: [ticket_id1, ticket_id2, ...]
  121. # normal: [ticket_id1, ticket_id2, ...]
  122. # child: [ticket_id1, ticket_id2, ...]
  123. # },
  124. # }
  125. if params[:links]
  126. raise 'Invalid link structure' if params[:links].to_h.class != Hash
  127. params[:links].each { |target_object, link_types_with_object_ids|
  128. raise 'Invalid link structure (Object)' if link_types_with_object_ids.to_h.class != Hash
  129. link_types_with_object_ids.each { |link_type, object_ids|
  130. raise 'Invalid link structure (Object->LinkType)' if object_ids.class != Array
  131. object_ids.each { |local_object_id|
  132. link = Link.add(
  133. link_type: link_type,
  134. link_object_target: target_object,
  135. link_object_target_value: local_object_id,
  136. link_object_source: 'Ticket',
  137. link_object_source_value: ticket.id,
  138. )
  139. }
  140. }
  141. }
  142. end
  143. if params[:expand]
  144. result = ticket.reload.attributes_with_association_names
  145. render json: result, status: :created
  146. return
  147. end
  148. if params[:all]
  149. render json: ticket_all(ticket.reload)
  150. return
  151. end
  152. render json: ticket.reload, status: :created
  153. end
  154. # PUT /api/v1/tickets/1
  155. def update
  156. # permission check
  157. ticket = Ticket.find(params[:id])
  158. ticket_permission(ticket)
  159. clean_params = Ticket.association_name_to_id_convert(params)
  160. clean_params = Ticket.param_cleanup(clean_params, true)
  161. # overwrite params
  162. if !current_user.permissions?('ticket.agent')
  163. [:owner, :owner_id, :customer, :customer_id, :organization, :organization_id, :preferences].each { |key|
  164. clean_params.delete(key)
  165. }
  166. end
  167. ticket.with_lock do
  168. ticket.update_attributes!(clean_params)
  169. if params[:article]
  170. article_create(ticket, params[:article])
  171. end
  172. end
  173. if params[:expand]
  174. result = ticket.reload.attributes_with_association_names
  175. render json: result, status: :ok
  176. return
  177. end
  178. if params[:all]
  179. render json: ticket_all(ticket.reload)
  180. return
  181. end
  182. render json: ticket.reload, status: :ok
  183. end
  184. # DELETE /api/v1/tickets/1
  185. def destroy
  186. # permission check
  187. ticket = Ticket.find(params[:id])
  188. ticket_permission(ticket)
  189. raise Exceptions::NotAuthorized, 'Not authorized (admin permission required)!' if !current_user.permissions?('admin')
  190. ticket.destroy!
  191. head :ok
  192. end
  193. # GET /api/v1/ticket_customer
  194. # GET /api/v1/tickets_customer
  195. def ticket_customer
  196. # return result
  197. result = Ticket::ScreenOptions.list_by_customer(
  198. customer_id: params[:customer_id],
  199. limit: 15,
  200. )
  201. render json: result
  202. end
  203. # GET /api/v1/ticket_history/1
  204. def ticket_history
  205. # get ticket data
  206. ticket = Ticket.find(params[:id])
  207. # permission check
  208. ticket_permission(ticket)
  209. # get history of ticket
  210. history = ticket.history_get(true)
  211. # return result
  212. render json: history
  213. end
  214. # GET /api/v1/ticket_related/1
  215. def ticket_related
  216. ticket = Ticket.find(params[:ticket_id])
  217. assets = ticket.assets({})
  218. # open tickets by customer
  219. access_condition = Ticket.access_condition(current_user)
  220. ticket_lists = Ticket
  221. .where(
  222. customer_id: ticket.customer_id,
  223. state_id: Ticket::State.by_category(:open)
  224. )
  225. .where(access_condition)
  226. .where('id != ?', [ ticket.id ])
  227. .order('created_at DESC')
  228. .limit(6)
  229. # if we do not have open related tickets, search for any tickets
  230. if ticket_lists.empty?
  231. ticket_lists = Ticket
  232. .where(
  233. customer_id: ticket.customer_id,
  234. ).where.not(
  235. state_id: Ticket::State.by_category(:merged)
  236. )
  237. .where(access_condition)
  238. .where('id != ?', [ ticket.id ])
  239. .order('created_at DESC')
  240. .limit(6)
  241. end
  242. # get related assets
  243. ticket_ids_by_customer = []
  244. ticket_lists.each { |ticket_list|
  245. ticket_ids_by_customer.push ticket_list.id
  246. assets = ticket_list.assets(assets)
  247. }
  248. ticket_ids_recent_viewed = []
  249. recent_views = RecentView.list(current_user, 8, 'Ticket').delete_if { |object| object['o_id'] == ticket.id }
  250. recent_views.each { |recent_view|
  251. next if recent_view['object'] != 'Ticket'
  252. ticket_ids_recent_viewed.push recent_view['o_id']
  253. recent_view_ticket = Ticket.find(recent_view['o_id'])
  254. next if recent_view_ticket.state.state_type.name == 'merged'
  255. assets = recent_view_ticket.assets(assets)
  256. }
  257. # return result
  258. render json: {
  259. assets: assets,
  260. ticket_ids_by_customer: ticket_ids_by_customer,
  261. ticket_ids_recent_viewed: ticket_ids_recent_viewed,
  262. }
  263. end
  264. # GET /api/v1/ticket_merge/1/1
  265. def ticket_merge
  266. # check master ticket
  267. ticket_master = Ticket.find_by(number: params[:master_ticket_number])
  268. if !ticket_master
  269. render json: {
  270. result: 'failed',
  271. message: 'No such master ticket number!',
  272. }
  273. return
  274. end
  275. # permission check
  276. ticket_permission(ticket_master)
  277. # check slave ticket
  278. ticket_slave = Ticket.find_by(id: params[:slave_ticket_id])
  279. if !ticket_slave
  280. render json: {
  281. result: 'failed',
  282. message: 'No such slave ticket!',
  283. }
  284. return
  285. end
  286. # permission check
  287. ticket_permission(ticket_slave)
  288. # check diffetent ticket ids
  289. if ticket_slave.id == ticket_master.id
  290. render json: {
  291. result: 'failed',
  292. message: 'Can\'t merge ticket with it self!',
  293. }
  294. return
  295. end
  296. # merge ticket
  297. ticket_slave.merge_to(
  298. ticket_id: ticket_master.id,
  299. created_by_id: current_user.id,
  300. )
  301. # return result
  302. render json: {
  303. result: 'success',
  304. master_ticket: ticket_master.attributes,
  305. slave_ticket: ticket_slave.attributes,
  306. }
  307. end
  308. # GET /api/v1/ticket_split
  309. def ticket_split
  310. # permission check
  311. ticket = Ticket.find(params[:ticket_id])
  312. ticket_permission(ticket)
  313. assets = ticket.assets({})
  314. # get related articles
  315. article = Ticket::Article.find(params[:article_id])
  316. assets = article.assets(assets)
  317. render json: {
  318. assets: assets
  319. }
  320. end
  321. # GET /api/v1/ticket_create
  322. def ticket_create
  323. # get attributes to update
  324. attributes_to_change = Ticket::ScreenOptions.attributes_to_change(
  325. user: current_user,
  326. )
  327. render json: attributes_to_change
  328. end
  329. # GET /api/v1/tickets/search
  330. def search
  331. # permit nested conditions
  332. if params[:condition]
  333. params.require(:condition).permit!
  334. end
  335. # set limit for pagination if needed
  336. if params[:page] && params[:per_page]
  337. params[:limit] = params[:page].to_i * params[:per_page].to_i
  338. end
  339. if params[:limit] && params[:limit].to_i > 100
  340. params[:limit].to_i = 100
  341. end
  342. # build result list
  343. tickets = Ticket.search(
  344. limit: params[:limit],
  345. query: params[:query],
  346. condition: params[:condition],
  347. current_user: current_user,
  348. )
  349. # do pagination if needed
  350. if params[:page] && params[:per_page]
  351. offset = (params[:page].to_i - 1) * params[:per_page].to_i
  352. tickets = tickets.slice(offset, params[:per_page].to_i) || []
  353. end
  354. if params[:expand]
  355. list = []
  356. tickets.each { |ticket|
  357. list.push ticket.attributes_with_association_names
  358. }
  359. render json: list, status: :ok
  360. return
  361. end
  362. assets = {}
  363. ticket_result = []
  364. tickets.each do |ticket|
  365. ticket_result.push ticket.id
  366. assets = ticket.assets(assets)
  367. end
  368. # return result
  369. render json: {
  370. tickets: ticket_result,
  371. tickets_count: tickets.count,
  372. assets: assets,
  373. }
  374. end
  375. # GET /api/v1/tickets/selector
  376. def selector
  377. permission_check('admin.*')
  378. ticket_count, tickets = Ticket.selectors(params[:condition], 6)
  379. assets = {}
  380. ticket_ids = []
  381. if tickets
  382. tickets.each do |ticket|
  383. ticket_ids.push ticket.id
  384. assets = ticket.assets(assets)
  385. end
  386. end
  387. # return result
  388. render json: {
  389. ticket_ids: ticket_ids,
  390. ticket_count: ticket_count || 0,
  391. assets: assets,
  392. }
  393. end
  394. # GET /api/v1/ticket_stats
  395. def stats
  396. if !params[:user_id] && !params[:organization_id]
  397. raise 'Need user_id or organization_id as param'
  398. end
  399. # lookup open user tickets
  400. limit = 100
  401. assets = {}
  402. access_condition = Ticket.access_condition(current_user)
  403. user_tickets = {}
  404. if params[:user_id]
  405. user = User.lookup(id: params[:user_id])
  406. if !user
  407. raise "No such user with id #{params[:user_id]}"
  408. end
  409. conditions = {
  410. closed_ids: {
  411. 'ticket.state_id' => {
  412. operator: 'is',
  413. value: Ticket::State.by_category(:closed).pluck(:id),
  414. },
  415. 'ticket.customer_id' => {
  416. operator: 'is',
  417. value: user.id,
  418. },
  419. },
  420. open_ids: {
  421. 'ticket.state_id' => {
  422. operator: 'is',
  423. value: Ticket::State.by_category(:open).pluck(:id),
  424. },
  425. 'ticket.customer_id' => {
  426. operator: 'is',
  427. value: user.id,
  428. },
  429. },
  430. }
  431. conditions.each { |key, local_condition|
  432. user_tickets[key] = ticket_ids_and_assets(local_condition, current_user, limit, assets)
  433. }
  434. # generate stats by user
  435. condition = {
  436. 'tickets.customer_id' => user.id,
  437. }
  438. user_tickets[:volume_by_year] = ticket_stats_last_year(condition, access_condition)
  439. end
  440. # lookup open org tickets
  441. org_tickets = {}
  442. if params[:organization_id] && !params[:organization_id].empty?
  443. organization = Organization.lookup(id: params[:organization_id])
  444. if !organization
  445. raise "No such organization with id #{params[:organization_id]}"
  446. end
  447. conditions = {
  448. closed_ids: {
  449. 'ticket.state_id' => {
  450. operator: 'is',
  451. value: Ticket::State.by_category(:closed).pluck(:id),
  452. },
  453. 'ticket.organization_id' => {
  454. operator: 'is',
  455. value: organization.id,
  456. },
  457. },
  458. open_ids: {
  459. 'ticket.state_id' => {
  460. operator: 'is',
  461. value: Ticket::State.by_category(:open).pluck(:id),
  462. },
  463. 'ticket.organization_id' => {
  464. operator: 'is',
  465. value: organization.id,
  466. },
  467. },
  468. }
  469. conditions.each { |key, local_condition|
  470. org_tickets[key] = ticket_ids_and_assets(local_condition, current_user, limit, assets)
  471. }
  472. # generate stats by org
  473. condition = {
  474. 'tickets.organization_id' => organization.id,
  475. }
  476. org_tickets[:volume_by_year] = ticket_stats_last_year(condition, access_condition)
  477. end
  478. # return result
  479. render json: {
  480. user: user_tickets,
  481. organization: org_tickets,
  482. assets: assets,
  483. }
  484. end
  485. private
  486. def ticket_all(ticket)
  487. # get attributes to update
  488. attributes_to_change = Ticket::ScreenOptions.attributes_to_change(user: current_user, ticket: ticket)
  489. # get related users
  490. assets = attributes_to_change[:assets]
  491. assets = ticket.assets(assets)
  492. # get related users
  493. article_ids = []
  494. ticket.articles.each { |article|
  495. # ignore internal article if customer is requesting
  496. next if article.internal == true && current_user.permissions?('ticket.customer')
  497. article_ids.push article.id
  498. assets = article.assets(assets)
  499. }
  500. # get links
  501. links = Link.list(
  502. link_object: 'Ticket',
  503. link_object_value: ticket.id,
  504. )
  505. link_list = []
  506. links.each { |item|
  507. link_list.push item
  508. if item['link_object'] == 'Ticket'
  509. linked_ticket = Ticket.lookup(id: item['link_object_value'])
  510. assets = linked_ticket.assets(assets)
  511. end
  512. }
  513. # get tags
  514. tags = ticket.tag_list
  515. # return result
  516. {
  517. ticket_id: ticket.id,
  518. ticket_article_ids: article_ids,
  519. assets: assets,
  520. links: link_list,
  521. tags: tags,
  522. form_meta: attributes_to_change[:form_meta],
  523. }
  524. end
  525. end