PageRenderTime 29ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/app/models/todo.rb

https://gitlab.com/artofhuman/gitlab-ce
Ruby | 198 lines | 137 code | 35 blank | 26 comment | 10 complexity | f167b3d23056096a924fc5211a9a6042 MD5 | raw file
  1. # frozen_string_literal: true
  2. class Todo < ActiveRecord::Base
  3. include Sortable
  4. include FromUnion
  5. # Time to wait for todos being removed when not visible for user anymore.
  6. # Prevents TODOs being removed by mistake, for example, removing access from a user
  7. # and giving it back again.
  8. WAIT_FOR_DELETE = 1.hour
  9. ASSIGNED = 1
  10. MENTIONED = 2
  11. BUILD_FAILED = 3
  12. MARKED = 4
  13. APPROVAL_REQUIRED = 5 # This is an EE-only feature
  14. UNMERGEABLE = 6
  15. DIRECTLY_ADDRESSED = 7
  16. ACTION_NAMES = {
  17. ASSIGNED => :assigned,
  18. MENTIONED => :mentioned,
  19. BUILD_FAILED => :build_failed,
  20. MARKED => :marked,
  21. APPROVAL_REQUIRED => :approval_required,
  22. UNMERGEABLE => :unmergeable,
  23. DIRECTLY_ADDRESSED => :directly_addressed
  24. }.freeze
  25. belongs_to :author, class_name: "User"
  26. belongs_to :note
  27. belongs_to :project
  28. belongs_to :group
  29. belongs_to :target, polymorphic: true, touch: true # rubocop:disable Cop/PolymorphicAssociations
  30. belongs_to :user
  31. delegate :name, :email, to: :author, prefix: true, allow_nil: true
  32. validates :action, :target_type, :user, presence: true
  33. validates :author, presence: true
  34. validates :target_id, presence: true, unless: :for_commit?
  35. validates :commit_id, presence: true, if: :for_commit?
  36. validates :project, presence: true, unless: :group_id
  37. validates :group, presence: true, unless: :project_id
  38. scope :pending, -> { with_state(:pending) }
  39. scope :done, -> { with_state(:done) }
  40. scope :for_action, -> (action) { where(action: action) }
  41. scope :for_author, -> (author) { where(author: author) }
  42. scope :for_project, -> (project) { where(project: project) }
  43. scope :for_group, -> (group) { where(group: group) }
  44. scope :for_type, -> (type) { where(target_type: type) }
  45. scope :for_target, -> (id) { where(target_id: id) }
  46. scope :for_commit, -> (id) { where(commit_id: id) }
  47. state_machine :state, initial: :pending do
  48. event :done do
  49. transition [:pending] => :done
  50. end
  51. state :pending
  52. state :done
  53. end
  54. after_save :keep_around_commit, if: :commit_id
  55. class << self
  56. # Returns all todos for the given group and its descendants.
  57. #
  58. # group - A `Group` to retrieve todos for.
  59. #
  60. # Returns an `ActiveRecord::Relation`.
  61. def for_group_and_descendants(group)
  62. groups = group.self_and_descendants
  63. from_union([
  64. for_project(Project.for_group(groups)),
  65. for_group(groups)
  66. ])
  67. end
  68. # Returns `true` if the current user has any todos for the given target.
  69. #
  70. # target - The value of the `target_type` column, such as `Issue`.
  71. def any_for_target?(target)
  72. exists?(target: target)
  73. end
  74. # Updates the state of a relation of todos to the new state.
  75. #
  76. # new_state - The new state of the todos.
  77. #
  78. # Returns an `Array` containing the IDs of the updated todos.
  79. def update_state(new_state)
  80. # Only update those that are not really on that state
  81. base = where.not(state: new_state).except(:order)
  82. ids = base.pluck(:id)
  83. base.update_all(state: new_state)
  84. ids
  85. end
  86. # Priority sorting isn't displayed in the dropdown, because we don't show
  87. # milestones, but still show something if the user has a URL with that
  88. # selected.
  89. def sort_by_attribute(method)
  90. sorted =
  91. case method.to_s
  92. when 'priority', 'label_priority' then order_by_labels_priority
  93. else order_by(method)
  94. end
  95. # Break ties with the ID column for pagination
  96. sorted.order(id: :desc)
  97. end
  98. # Order by priority depending on which issue/merge request the Todo belongs to
  99. # Todos with highest priority first then oldest todos
  100. # Need to order by created_at last because of differences on Mysql and Postgres when joining by type "Merge_request/Issue"
  101. def order_by_labels_priority
  102. params = {
  103. target_type_column: "todos.target_type",
  104. target_column: "todos.target_id",
  105. project_column: "todos.project_id"
  106. }
  107. highest_priority = highest_label_priority(params).to_sql
  108. select("#{table_name}.*, (#{highest_priority}) AS highest_priority")
  109. .order(Gitlab::Database.nulls_last_order('highest_priority', 'ASC'))
  110. .order('todos.created_at')
  111. end
  112. end
  113. def parent
  114. project
  115. end
  116. def unmergeable?
  117. action == UNMERGEABLE
  118. end
  119. def build_failed?
  120. action == BUILD_FAILED
  121. end
  122. def assigned?
  123. action == ASSIGNED
  124. end
  125. def action_name
  126. ACTION_NAMES[action]
  127. end
  128. def body
  129. if note.present?
  130. note.note
  131. else
  132. target.title
  133. end
  134. end
  135. def for_commit?
  136. target_type == "Commit"
  137. end
  138. # override to return commits, which are not active record
  139. def target
  140. if for_commit?
  141. project.commit(commit_id) rescue nil
  142. else
  143. super
  144. end
  145. end
  146. def target_reference
  147. if for_commit?
  148. target.reference_link_text(full: true)
  149. else
  150. target.to_reference(full: true)
  151. end
  152. end
  153. def self_added?
  154. author == user
  155. end
  156. def self_assigned?
  157. assigned? && self_added?
  158. end
  159. private
  160. def keep_around_commit
  161. project.repository.keep_around(self.commit_id)
  162. end
  163. end