Class: Note

Inherits:
ActiveRecord::Base
  • Object
show all
Includes:
Gitlab::CurrentSettings, Mentionable, Participable
Defined in:
app/models/note.rb

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Mentionable

#all_references, #create_cross_references!, #create_new_cross_references!, #gfm_reference, #local_reference, #mentioned_users, #referenced_mentionables

Methods included from Participable

#participants

Methods included from Gitlab::CurrentSettings

#current_application_settings, #fake_application_settings

Class Method Details

.build_discussion_id(type, id, line_code) ⇒ Object


104
105
106
# File 'app/models/note.rb', line 104

def build_discussion_id(type, id, line_code)
  [:discussion, type.try(:underscore), id, line_code].join("-").to_sym
end

.discussions_from_notes(notes) ⇒ Object


83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'app/models/note.rb', line 83

def discussions_from_notes(notes)
  discussion_ids = []
  discussions = []

  notes.each do |note|
    next if discussion_ids.include?(note.discussion_id)

    # don't group notes for the main target
    if !note.for_diff_line? && note.for_merge_request?
      discussions << [note]
    else
      discussions << notes.select do |other_note|
        note.discussion_id == other_note.discussion_id
      end
      discussion_ids << note.discussion_id
    end
  end

  discussions
end

.grouped_awardsObject


122
123
124
125
126
127
128
129
130
131
132
133
# File 'app/models/note.rb', line 122

def grouped_awards
  notes = {}

  awards.select(:note).distinct.map do |note|
    notes[note.note] = where(note: note.note)
  end

  notes["thumbsup"] ||= Note.none
  notes["thumbsdown"] ||= Note.none

  notes
end

.search(query) ⇒ Object

Searches for notes matching the given query.

This method uses ILIKE on PostgreSQL and LIKE on MySQL.

query - The search query as a String.

Returns an ActiveRecord::Relation.


115
116
117
118
119
120
# File 'app/models/note.rb', line 115

def search(query)
  table   = arel_table
  pattern = "%#{query}%"

  where(table[:note].matches(pattern))
end

Instance Method Details

#active?Boolean

Check if this note is part of an “active” discussion

This will always return true for anything except MergeRequest noteables, which have special logic.

If the note's current diff cannot be matched in the MergeRequest's current diff, it's considered inactive.

Returns:

  • (Boolean)

182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
# File 'app/models/note.rb', line 182

def active?
  return true unless self.diff
  return false unless noteable
  return @active if defined?(@active)

  noteable_diff = find_noteable_diff

  if noteable_diff
    parsed_lines = Gitlab::Diff::Parser.new.parse(noteable_diff.diff.each_line)

    @active = parsed_lines.any? { |line_obj| line_obj.text == diff_line }
  else
    @active = false
  end

  @active
end

#cross_reference?Boolean

Returns:

  • (Boolean)

136
137
138
# File 'app/models/note.rb', line 136

def cross_reference?
  system && SystemNoteService.cross_reference?(note)
end

#cross_reference_not_visible_for?(user) ⇒ Boolean

Returns:

  • (Boolean)

361
362
363
# File 'app/models/note.rb', line 361

def cross_reference_not_visible_for?(user)
  cross_reference? && referenced_mentionables(user).empty?
end

#diffObject


167
168
169
# File 'app/models/note.rb', line 167

def diff
  @diff ||= Gitlab::Git::Diff.new(st_diff) if st_diff.respond_to?(:map)
end

#diff_file_indexObject


200
201
202
# File 'app/models/note.rb', line 200

def diff_file_index
  line_code.split('_')[0] if line_code
end

#diff_file_nameObject


204
205
206
# File 'app/models/note.rb', line 204

def diff_file_name
  diff.new_path if diff
end

#diff_for_line_codeObject


171
172
173
# File 'app/models/note.rb', line 171

def diff_for_line_code
  Note.where(noteable_id: noteable_id, noteable_type: noteable_type, line_code: line_code).last.try(:diff)
end

#diff_lineObject


228
229
230
231
232
233
234
235
236
237
238
239
240
# File 'app/models/note.rb', line 228

def diff_line
  return @diff_line if @diff_line

  if diff
    diff_lines.each do |line|
      if generate_line_code(line) == self.line_code
        @diff_line = line.text
      end
    end
  end

  @diff_line
end

#diff_line_typeObject


242
243
244
245
246
247
248
249
250
251
252
253
254
# File 'app/models/note.rb', line 242

def diff_line_type
  return @diff_line_type if @diff_line_type

  if diff
    diff_lines.each do |line|
      if generate_line_code(line) == self.line_code
        @diff_line_type = line.type
      end
    end
  end

  @diff_line_type
end

#diff_linesObject


277
278
279
# File 'app/models/note.rb', line 277

def diff_lines
  @diff_lines ||= Gitlab::Diff::Parser.new.parse(diff.diff.each_line)
end

#diff_new_lineObject


220
221
222
# File 'app/models/note.rb', line 220

def diff_new_line
  line_code.split('_')[2].to_i if line_code
end

#diff_old_lineObject


216
217
218
# File 'app/models/note.rb', line 216

def diff_old_line
  line_code.split('_')[1].to_i if line_code
end

#discussion_idObject


285
286
287
# File 'app/models/note.rb', line 285

def discussion_id
  @discussion_id ||= Note.build_discussion_id(noteable_type, noteable_id || commit_id, line_code)
end

#downvote?Boolean

Returns:

  • (Boolean)

349
350
351
# File 'app/models/note.rb', line 349

def downvote?
  is_award && note == "thumbsdown"
end

#editable?Boolean

Returns:

  • (Boolean)

357
358
359
# File 'app/models/note.rb', line 357

def editable?
  !system? && !is_award
end

#file_pathObject


208
209
210
211
212
213
214
# File 'app/models/note.rb', line 208

def file_path
  if diff.new_path.present?
    diff.new_path
  elsif diff.old_path.present?
    diff.old_path
  end
end

#find_diffObject


144
145
146
147
148
149
150
151
152
# File 'app/models/note.rb', line 144

def find_diff
  return nil unless noteable
  return @diff if defined?(@diff)

  # Don't use ||= because nil is a valid value for @diff
  @diff = noteable.diffs(Commit.max_diff_options).find do |d|
    Digest::SHA1.hexdigest(d.new_path) == diff_file_index if d.new_path
  end
end

#for_commit?Boolean

Returns:

  • (Boolean)

289
290
291
# File 'app/models/note.rb', line 289

def for_commit?
  noteable_type == "Commit"
end

#for_commit_diff_line?Boolean

Returns:

  • (Boolean)

293
294
295
# File 'app/models/note.rb', line 293

def for_commit_diff_line?
  for_commit? && for_diff_line?
end

#for_diff_line?Boolean

Returns:

  • (Boolean)

297
298
299
# File 'app/models/note.rb', line 297

def for_diff_line?
  line_code.present?
end

#for_issue?Boolean

Returns:

  • (Boolean)

301
302
303
# File 'app/models/note.rb', line 301

def for_issue?
  noteable_type == "Issue"
end

#for_merge_request?Boolean

Returns:

  • (Boolean)

305
306
307
# File 'app/models/note.rb', line 305

def for_merge_request?
  noteable_type == "MergeRequest"
end

#for_merge_request_diff_line?Boolean

Returns:

  • (Boolean)

309
310
311
# File 'app/models/note.rb', line 309

def for_merge_request_diff_line?
  for_merge_request? && for_diff_line?
end

#for_snippet?Boolean

Returns:

  • (Boolean)

313
314
315
# File 'app/models/note.rb', line 313

def for_snippet?
  noteable_type == "Snippet"
end

#generate_line_code(line) ⇒ Object


224
225
226
# File 'app/models/note.rb', line 224

def generate_line_code(line)
  Gitlab::Diff::LineCode.generate(file_path, line.new_pos, line.old_pos)
end

#highlighted_diff_linesObject


281
282
283
# File 'app/models/note.rb', line 281

def highlighted_diff_lines
  Gitlab::Diff::Highlight.new(diff_lines).highlight
end

#hook_attrsObject


154
155
156
# File 'app/models/note.rb', line 154

def hook_attrs
  attributes
end

#max_attachment_sizeObject


140
141
142
# File 'app/models/note.rb', line 140

def max_attachment_size
  current_application_settings.max_attachment_size.megabytes.to_i
end

#noteableObject

override to return commits, which are not active record


318
319
320
321
322
323
324
325
326
327
328
# File 'app/models/note.rb', line 318

def noteable
  if for_commit?
    project.commit(commit_id)
  else
    super
  end
# Temp fix to prevent app crash
# if note commit id doesn't exist
rescue
  nil
end

#noteable_type=(noteable_type) ⇒ Object

FIXME: Hack for polymorphic associations with STI

For more information visit http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#label-Polymorphic+Associations

332
333
334
# File 'app/models/note.rb', line 332

def noteable_type=(noteable_type)
  super(noteable_type.to_s.classify.constantize.base_class.to_s)
end

#reset_events_cacheObject

Reset notes events cache

Since we do cache @event we need to reset cache in special cases:

  • when a note is updated

  • when a note is removed

Events cache stored like events/23-20130109142513. The cache key includes updated_at timestamp. Thus it will automatically generate a new fragment when the event is updated because the key changes.


345
346
347
# File 'app/models/note.rb', line 345

def reset_events_cache
  Event.reset_event_cache_for(self)
end

#set_award!Object

Checks if note is an award added as a comment

If note is an award, this method sets is_award to true

and changes content of the note to award name.

Method is executed as a before_validation callback.


372
373
374
375
376
377
# File 'app/models/note.rb', line 372

def set_award!
  return unless awards_supported? && contains_emoji_only?

  self.is_award = true
  self.note = award_emoji_name
end

#set_diffObject


158
159
160
161
162
163
164
165
# File 'app/models/note.rb', line 158

def set_diff
  # First lets find notes with same diff
  # before iterating over all mr diffs
  diff = diff_for_line_code unless for_merge_request?
  diff ||= find_diff

  self.st_diff = diff.to_hash if diff
end

#truncated_diff_linesObject


256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
# File 'app/models/note.rb', line 256

def truncated_diff_lines
  max_number_of_lines = 16
  prev_match_line = nil
  prev_lines = []

  highlighted_diff_lines.each do |line|
    if line.type == "match"
      prev_lines.clear
      prev_match_line = line
    else
      prev_lines << line

      break if generate_line_code(line) == self.line_code

      prev_lines.shift if prev_lines.length >= max_number_of_lines
    end
  end

  prev_lines
end

#upvote?Boolean

Returns:

  • (Boolean)

353
354
355
# File 'app/models/note.rb', line 353

def upvote?
  is_award && note == "thumbsup"
end