BigW Consortium Gitlab

event.rb 6.68 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 20 21
  delegate :name, :email, to: :author, prefix: true, allow_nil: true
  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 33
  # Callbacks
  after_create :reset_project_activity

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

  scope :in_projects, ->(projects) do
Josh Frye committed
39
    where(project_id: projects.map(&:id)).recent
40 41
  end

42
  scope :with_associations, -> { includes(project: :namespace) }
43
  scope :for_milestone_id, ->(milestone_id) { where(target_type: "Milestone", target_id: milestone_id) }
44

Andrey Kumanyaev committed
45
  class << self
46 47 48 49 50
    def reset_event_cache_for(target)
      Event.where(target_id: target.id, target_type: target.class.to_s).
        order('id DESC').limit(100).
        update_all(updated_at: Time.now)
    end
51

52
    # Update Gitlab::ContributionsCalendar#activity_dates if this changes
53 54 55 56 57
    def contributions
      where("action = ? OR (target_type in (?) AND action in (?))",
            Event::PUSHED, ["MergeRequest", "Issue"],
            [Event::CREATED, Event::CLOSED, Event::MERGED])
    end
58

59 60 61
    def limit_recent(limit = 20, offset = nil)
      recent.limit(limit).offset(offset)
    end
Dmitriy Zaporozhets committed
62 63
  end

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

80 81
  def project_name
    if project
82
      project.name_with_namespace
83
    else
84
      "(deleted project)"
85 86 87
    end
  end

88
  def target_title
89
    target.try(:title)
90 91 92 93
  end

  def created?
    action == CREATED
94 95
  end

96
  def push?
97
    action == PUSHED && valid_push?
98 99
  end

100
  def merged?
101
    action == MERGED
102 103
  end

104
  def closed?
105
    action == CLOSED
106 107 108
  end

  def reopened?
109 110 111 112 113 114 115 116 117 118 119
    action == REOPENED
  end

  def joined?
    action == JOINED
  end

  def left?
    action == LEFT
  end

120 121 122 123
  def expired?
    action == EXPIRED
  end

124 125 126 127
  def destroyed?
    action == DESTROYED
  end

128 129 130 131 132
  def commented?
    action == COMMENTED
  end

  def membership_changed?
133
    joined? || left? || expired?
134 135
  end

136
  def created_project?
137
    created? && !target && target_type.nil?
138 139 140 141 142 143
  end

  def created_target?
    created? && target
  end

144 145 146 147 148
  def milestone?
    target_type == "Milestone"
  end

  def note?
149
    target.is_a?(Note)
150 151
  end

152
  def issue?
153
    target_type == "Issue"
154 155
  end

156
  def merge_request?
157
    target_type == "MergeRequest"
158 159
  end

160 161
  def milestone
    target if milestone?
162 163
  end

164
  def issue
165
    target if issue?
166 167 168
  end

  def merge_request
169
    target if merge_request?
170 171
  end

172
  def note
173
    target if note?
174 175
  end

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

  def valid_push?
211
    data[:ref] && ref_name.present?
212
  rescue
Dmitriy Zaporozhets committed
213 214 215 216
    false
  end

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

  def branch?
221
    Gitlab::Git.branch_ref?(data[:ref])
Dmitriy Zaporozhets committed
222 223 224
  end

  def new_ref?
225
    Gitlab::Git.blank_ref?(commit_from)
Dmitriy Zaporozhets committed
226 227 228
  end

  def rm_ref?
229
    Gitlab::Git.blank_ref?(commit_to)
Dmitriy Zaporozhets committed
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
  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
253
    @branch_name ||= Gitlab::Git.ref_name(data[:ref])
Dmitriy Zaporozhets committed
254 255 256
  end

  def tag_name
257
    @tag_name ||= Gitlab::Git.ref_name(data[:ref])
Dmitriy Zaporozhets committed
258 259 260 261
  end

  # Max 20 commits from push DESC
  def commits
262
    @commits ||= (data[:commits] || []).reverse
Dmitriy Zaporozhets committed
263 264 265 266 267 268 269 270 271 272 273
  end

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

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

  def push_with_commits?
274
    !commits.empty? && commit_from && commit_to
Dmitriy Zaporozhets committed
275 276 277 278 279 280
  end

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

281 282 283 284
  def target_iid
    target.respond_to?(:iid) ? target.iid : target_id
  end

285
  def commit_note?
286
    target.for_commit?
Dmitriy Zaporozhets committed
287 288
  end

289
  def issue_note?
290
    note? && target && target.for_issue?
291 292
  end

293 294 295 296
  def merge_request_note?
    note? && target && target.for_merge_request?
  end

297
  def project_snippet_note?
298
    target.for_snippet?
Andrew8xx8 committed
299 300
  end

Dmitriy Zaporozhets committed
301 302 303 304 305
  def note_target
    target.noteable
  end

  def note_target_id
306
    if commit_note?
Dmitriy Zaporozhets committed
307 308 309 310 311 312
      target.commit_id
    else
      target.noteable_id.to_s
    end
  end

313 314 315 316 317 318
  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
319
    else
320 321
      note_target.to_reference
    end
Dmitriy Zaporozhets committed
322 323
  end

Dmitriy Zaporozhets committed
324 325 326 327 328 329 330
  def note_target_type
    if target.noteable_type.present?
      target.noteable_type.titleize
    else
      "Wall"
    end.downcase
  end
331 332 333

  def body?
    if push?
334
      push_with_commits? || rm_ref?
335 336 337 338 339 340
    elsif note?
      true
    else
      target.respond_to? :title
    end
  end
341 342

  def reset_project_activity
343 344
    return unless project

345
    # Don't bother updating if we know the project was updated recently.
346
    return if recent_update?
347

348 349 350 351
    # 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.
    Project.unscoped.where(id: project_id).
352
      where('last_activity_at <= ?', RESET_PROJECT_ACTIVITY_INTERVAL.ago).
353
      update_all(last_activity_at: created_at)
354 355 356 357 358 359 360
  end

  private

  def recent_update?
    project.last_activity_at > RESET_PROJECT_ACTIVITY_INTERVAL.ago
  end
361
end