BigW Consortium Gitlab

event.rb 6.94 KB
Newer Older
1
class Event < ActiveRecord::Base
2
  include Sortable
3
  default_scope { reorder(nil).where.not(author_id: nil) }
4

5 6 7 8 9 10 11 12 13
  CREATED   = 1
  UPDATED   = 2
  CLOSED    = 3
  REOPENED  = 4
  PUSHED    = 5
  COMMENTED = 6
  MERGED    = 7
  JOINED    = 8 # User joined project
  LEFT      = 9 # User left project
14
  DESTROYED = 10
15
  EXPIRED   = 11 # User left project due to expiry
16

17 18
  RESET_PROJECT_ACTIVITY_INTERVAL = 1.hour

19
  delegate :name, :email, :public_email, :username, to: :author, prefix: true, allow_nil: true
20 21
  delegate :title, to: :issue, prefix: true, allow_nil: true
  delegate :title, to: :merge_request, prefix: true, allow_nil: true
22
  delegate :title, to: :note, prefix: true, allow_nil: true
23

randx committed
24
  belongs_to :author, class_name: "User"
25
  belongs_to :project
26
  belongs_to :target, polymorphic: true
27

28 29
  # For Hash only
  serialize :data
30

31 32
  # Callbacks
  after_create :reset_project_activity
33
  after_create :set_last_repository_updated_at, if: :push?
34

Andrey Kumanyaev committed
35
  # Scopes
36
  scope :recent, -> { reorder(id: :desc) }
37
  scope :code_push, -> { where(action: PUSHED) }
38 39

  scope :in_projects, ->(projects) do
40
    where(project_id: projects.pluck(:id)).recent
41 42
  end

43
  scope :with_associations, -> { includes(:author, :project, project: :namespace).preload(:target) }
44
  scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) }
45

Andrey Kumanyaev committed
46
  class << self
47
    # Update Gitlab::ContributionsCalendar#activity_dates if this changes
48
    def contributions
49 50
      where("action = ? OR (target_type IN (?) AND action IN (?)) OR (target_type = ? AND action = ?)",
            Event::PUSHED,
Douwe Maan committed
51
            %w(MergeRequest Issue), [Event::CREATED, Event::CLOSED, Event::MERGED],
52
            "Note", Event::COMMENTED)
53
    end
54

55 56 57
    def limit_recent(limit = 20, offset = nil)
      recent.limit(limit).offset(offset)
    end
Dmitriy Zaporozhets committed
58 59
  end

60
  def visible_to_user?(user = nil)
61
    if push? || commit_note?
62
      Ability.allowed?(user, :download_code, project)
63 64
    elsif membership_changed?
      true
65 66
    elsif created_project?
      true
67
    elsif issue? || issue_note?
68
      Ability.allowed?(user, :read_issue, note? ? note_target : target)
69 70
    elsif merge_request? || merge_request_note?
      Ability.allowed?(user, :read_merge_request, note? ? note_target : target)
71
    else
72
      milestone?
73
    end
74 75
  end

76 77
  def project_name
    if project
78
      project.name_with_namespace
79
    else
80
      "(deleted project)"
81 82 83
    end
  end

84
  def target_title
85
    target.try(:title)
86 87 88 89
  end

  def created?
    action == CREATED
90 91
  end

92
  def push?
93
    action == PUSHED && valid_push?
94 95
  end

96
  def merged?
97
    action == MERGED
98 99
  end

100
  def closed?
101
    action == CLOSED
102 103 104
  end

  def reopened?
105 106 107 108 109 110 111 112 113 114 115
    action == REOPENED
  end

  def joined?
    action == JOINED
  end

  def left?
    action == LEFT
  end

116 117 118 119
  def expired?
    action == EXPIRED
  end

120 121 122 123
  def destroyed?
    action == DESTROYED
  end

124 125 126 127 128
  def commented?
    action == COMMENTED
  end

  def membership_changed?
129
    joined? || left? || expired?
130 131
  end

132
  def created_project?
133
    created? && !target && target_type.nil?
134 135 136 137 138 139
  end

  def created_target?
    created? && target
  end

140 141 142 143 144
  def milestone?
    target_type == "Milestone"
  end

  def note?
145
    target.is_a?(Note)
146 147
  end

148
  def issue?
149
    target_type == "Issue"
150 151
  end

152
  def merge_request?
153
    target_type == "MergeRequest"
154 155
  end

156 157
  def milestone
    target if milestone?
158 159
  end

160
  def issue
161
    target if issue?
162 163 164
  end

  def merge_request
165
    target if merge_request?
166 167
  end

168
  def note
169
    target if note?
170 171
  end

172
  def action_name
173 174 175 176 177 178 179 180 181
    if push?
      if new_ref?
        "pushed new"
      elsif rm_ref?
        "deleted"
      else
        "pushed to"
      end
    elsif closed?
182 183
      "closed"
    elsif merged?
184
      "accepted"
185 186
    elsif joined?
      'joined'
187 188
    elsif left?
      'left'
189 190
    elsif expired?
      'removed due to membership expiration from'
191 192
    elsif destroyed?
      'destroyed'
193 194
    elsif commented?
      "commented on"
195
    elsif created_project?
196
      if project.external_import?
197 198 199 200
        "imported"
      else
        "created"
      end
201
    else
202
      "opened"
203 204
    end
  end
Dmitriy Zaporozhets committed
205 206

  def valid_push?
207
    data[:ref] && ref_name.present?
208
  rescue
Dmitriy Zaporozhets committed
209 210 211 212
    false
  end

  def tag?
213
    Gitlab::Git.tag_ref?(data[:ref])
Dmitriy Zaporozhets committed
214 215 216
  end

  def branch?
217
    Gitlab::Git.branch_ref?(data[:ref])
Dmitriy Zaporozhets committed
218 219 220
  end

  def new_ref?
221
    Gitlab::Git.blank_ref?(commit_from)
Dmitriy Zaporozhets committed
222 223 224
  end

  def rm_ref?
225
    Gitlab::Git.blank_ref?(commit_to)
Dmitriy Zaporozhets committed
226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
  end

  def md_ref?
    !(rm_ref? || new_ref?)
  end

  def commit_from
    data[:before]
  end

  def commit_to
    data[:after]
  end

  def ref_name
    if tag?
      tag_name
    else
      branch_name
    end
  end

  def branch_name
249
    @branch_name ||= Gitlab::Git.ref_name(data[:ref])
Dmitriy Zaporozhets committed
250 251 252
  end

  def tag_name
253
    @tag_name ||= Gitlab::Git.ref_name(data[:ref])
Dmitriy Zaporozhets committed
254 255 256 257
  end

  # Max 20 commits from push DESC
  def commits
258
    @commits ||= (data[:commits] || []).reverse
Dmitriy Zaporozhets committed
259 260 261 262 263 264 265 266 267 268 269
  end

  def commits_count
    data[:total_commits_count] || commits.count || 0
  end

  def ref_type
    tag? ? "tag" : "branch"
  end

  def push_with_commits?
270
    !commits.empty? && commit_from && commit_to
Dmitriy Zaporozhets committed
271 272 273 274 275 276
  end

  def last_push_to_non_root?
    branch? && project.default_branch != branch_name
  end

277 278 279 280
  def target_iid
    target.respond_to?(:iid) ? target.iid : target_id
  end

281
  def commit_note?
282
    note? && target && target.for_commit?
Dmitriy Zaporozhets committed
283 284
  end

285
  def issue_note?
286
    note? && target && target.for_issue?
287 288
  end

289 290 291 292
  def merge_request_note?
    note? && target && target.for_merge_request?
  end

293
  def project_snippet_note?
294
    note? && target && target.for_snippet?
Andrew8xx8 committed
295 296
  end

Dmitriy Zaporozhets committed
297 298 299 300 301
  def note_target
    target.noteable
  end

  def note_target_id
302
    if commit_note?
Dmitriy Zaporozhets committed
303 304 305 306 307 308
      target.commit_id
    else
      target.noteable_id.to_s
    end
  end

309 310 311 312 313 314
  def note_target_reference
    return unless note_target

    # Commit#to_reference returns the full SHA, but we want the short one here
    if commit_note?
      note_target.short_id
Dmitriy Zaporozhets committed
315
    else
316 317
      note_target.to_reference
    end
Dmitriy Zaporozhets committed
318 319
  end

Dmitriy Zaporozhets committed
320 321 322 323 324 325 326
  def note_target_type
    if target.noteable_type.present?
      target.noteable_type.titleize
    else
      "Wall"
    end.downcase
  end
327 328 329

  def body?
    if push?
330
      push_with_commits? || rm_ref?
331 332 333 334 335 336
    elsif note?
      true
    else
      target.respond_to? :title
    end
  end
337 338

  def reset_project_activity
339 340
    return unless project

341
    # Don't bother updating if we know the project was updated recently.
342
    return if recent_update?
343

344 345 346
    # At this point it's possible for multiple threads/processes to try to
    # update the project. Only one query should actually perform the update,
    # hence we add the extra WHERE clause for last_activity_at.
347 348 349
    Project.unscoped.where(id: project_id).
      where('last_activity_at <= ?', RESET_PROJECT_ACTIVITY_INTERVAL.ago).
      update_all(last_activity_at: created_at)
350 351
  end

352 353 354 355
  def authored_by?(user)
    user ? author_id == user.id : false
  end

356 357 358 359 360
  private

  def recent_update?
    project.last_activity_at > RESET_PROJECT_ACTIVITY_INTERVAL.ago
  end
361 362 363 364 365

  def set_last_repository_updated_at
    Project.unscoped.where(id: project_id).
      update_all(last_repository_updated_at: created_at)
  end
366
end