can_selector.rb 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. # Copyright (C) 2012-2025 Zammad Foundation, https://zammad-foundation.org/
  2. module Ticket::CanSelector
  3. extend ActiveSupport::Concern
  4. include ::CanSelector
  5. class_methods do
  6. =begin
  7. get count of tickets and tickets which match on selector
  8. @param [Hash] selectors hash with conditions
  9. @oparam [Hash] options
  10. @option options [String] :access can be 'full', 'read', 'create' or 'ignore' (ignore means a selector over all tickets), defaults to 'full'
  11. @option options [Integer] :limit of tickets to return
  12. @option options [User] :user is a current user
  13. @option options [Integer] :execution_time is a current user
  14. @return [Integer, [<Ticket>]]
  15. @example
  16. ticket_count, tickets = Ticket.selectors(params[:condition], limit: limit, current_user: current_user, access: 'full')
  17. ticket_count # count of found tickets
  18. tickets # tickets
  19. =end
  20. def selectors(selectors, options = {})
  21. limit = options[:limit] || 10
  22. raise 'no selectors given' if !selectors
  23. ActiveRecord::Base.transaction(requires_new: true) do
  24. scope = raw_selectors(selectors, options)
  25. next [] if scope.nil?
  26. [
  27. scope.count(:all).count, # grouped queries count is a hash, not a single digit :(
  28. scope.limit(limit)
  29. ]
  30. rescue ActiveRecord::StatementInvalid => e
  31. Rails.logger.error e
  32. raise ActiveRecord::Rollback
  33. end
  34. end
  35. # @example
  36. # Ticket.raw_selectors({}, { order_by: 'tickets.state_id ASC' })
  37. # Ticket.raw_selectors({}, { order_by: [{ column: 'state', direction: 'ASC'}] })
  38. # Ticket.raw_selectors({}, { order_by: [{ column: 'state', direction: 'ASC'}], locale: 'de-de' })
  39. def raw_selectors(selectors, options)
  40. query, bind_params, tables = selector2sql(selectors, options)
  41. return if !query
  42. current_user = options[:current_user]
  43. access = options[:access] || 'full'
  44. relation = if !current_user || access == 'ignore'
  45. Ticket.all
  46. else
  47. "TicketPolicy::#{access.camelize}Scope".constantize.new(current_user).resolve
  48. end
  49. order_clause = CanSelector::AdvancedSorting.new(options[:order_by], options[:locale], Ticket).calculate_sorting
  50. relation = relation
  51. .group('tickets.id')
  52. .where(query, *bind_params)
  53. .joins(tables)
  54. .select('tickets.*')
  55. .reorder(nil)
  56. apply_order_onto_relation(relation, order_clause)
  57. end
  58. def apply_order_onto_relation(relation, order_clause)
  59. case order_clause
  60. when String, Symbol
  61. relation.order(Arel.sql(order_clause.to_s)) # rubocop:disable Zammad/ActiveRecordReorder
  62. when Array, Hash
  63. order_clause = [order_clause] if order_clause.is_a? Hash
  64. order_clause.reduce(relation) do |memo, elem|
  65. case elem
  66. when String, Symbol
  67. memo.reorder(Arel.sql(elem))
  68. when Hash
  69. memo = memo.select(Arel.sql(elem[:select])) if elem[:select]
  70. memo = memo.order(Arel.sql(elem[:order])) if elem[:order] # rubocop:disable Zammad/ActiveRecordReorder
  71. memo = memo.joins(elem[:joins]) if elem[:joins]
  72. memo = memo.group(elem[:group]) if elem[:group]
  73. memo
  74. else
  75. memo
  76. end
  77. end
  78. else
  79. relation
  80. end
  81. end
  82. end
  83. end