Class: Snippet
- Inherits:
-
ApplicationRecord
show all
- Extended by:
- Gitlab::Utils::Override
- Includes:
- AfterCommitQueue, Awardable, CacheMarkdownField, CanMoveRepositoryStorage, CreatedAtFilterable, EachBatch, Editable, FromUnion, Gitlab::SQL::Pattern, Gitlab::VisibilityLevel, HasRepository, Import::HasImportSource, Mentionable, Noteable, Participable, Redactable, SafelyChangeColumnDefault, Sortable, Spammable
- Defined in:
- app/models/snippet.rb
Constant Summary
collapse
- MAX_FILE_COUNT =
10
- DESCRIPTION_LENGTH_MAX =
1.megabyte
Import::HasImportSource::IMPORT_SOURCES
CanMoveRepositoryStorage::RepositoryReadOnlyError
Gitlab::SQL::Pattern::MIN_CHARS_FOR_PARTIAL_MATCHING, Gitlab::SQL::Pattern::REGEX_QUOTED_TERM
Constants included
from Noteable
Noteable::MAX_NOTES_LIMIT
CacheMarkdownField::INVALIDATED_BY
Constants included
from Redactable
Redactable::UNSUBSCRIBE_PATTERN
Gitlab::VisibilityLevel::INTERNAL, Gitlab::VisibilityLevel::PRIVATE, Gitlab::VisibilityLevel::PUBLIC
ApplicationRecord::MAX_PLUCK
HasCheckConstraints::NOT_NULL_CHECK_PATTERN
ResetOnColumnErrors::MAX_RESET_PERIOD
Instance Attribute Summary
Attributes included from Noteable
#system_note_timestamp
#skip_markdown_cache_validation
Class Method Summary
collapse
Instance Method Summary
collapse
extended, extensions, included, method_added, override, prepended, queue_verification, verify!
#imported?
#run_after_commit, #run_after_commit_or_now
#git_transfer_in_progress?, #reference_counter, #set_repository_read_only!, #set_repository_writable!
#after_change_head_branch_does_not_exist, #after_repository_change_head, #branch_exists?, #commit, #commit_by, #commits_by, #default_branch_from_group_preferences, #default_branch_from_preferences, #empty_repo?, #http_url_to_repo, #lfs_enabled?, #lfs_http_url_to_repo, #reload_default_branch, #repo_exists?, #repository_exists?, #root_ref?, #ssh_url_to_repo, #url_to_repo, #valid_repo?, #web_url
#gitlab_shell
Methods included from Referable
#referable_inspect, #reference_link_text, #to_reference_base
split_query_to_search_terms
Methods included from Editable
#edited?, #last_edited_by
Methods included from Spammable
#allow_possible_spam?, #check_for_spam, #clear_spam_flags!, #invalidate_if_spam, #needs_recaptcha!, #recaptcha_error!, #render_recaptcha?, #spam, #spam!, #spam_description, #spam_title, #spammable_attribute_changed?, #spammable_entity_type, #spammable_text, #submittable_as_spam?, #submittable_as_spam_by?, #unrecoverable_spam_error!
#all_references, #create_cross_references!, #create_new_cross_references!, #directly_addressed_users, #extractors, #gfm_reference, #local_reference, #matches_cross_reference_regex?, #mentioned_users, #referenced_group_users, #referenced_groups, #referenced_mentionables, #referenced_project_users, #referenced_projects, #referenced_users, #user_mention_class, #user_mention_identifier
Methods included from Awardable
#awarded_emoji?, #downvotes, #emoji_awardable?, #grouped_awards, #upvotes, #user_authored?, #user_can_award?
#participant?, #participants, #visible_participants
Methods included from Noteable
#after_note_created, #after_note_destroyed, #base_class_name, #broadcast_notes_changed, #capped_notes_count, #commenters, #creatable_note_email_address, #discussion_ids_relation, #discussion_notes, #discussion_root_note_ids, #discussions, #discussions_can_be_resolved_by?, #discussions_rendered_on_frontend?, #discussions_resolvable?, #discussions_resolved?, #discussions_to_be_resolved, #grouped_diff_discussions, #has_any_diff_note_positions?, #human_class_name, #lockable?, #noteable_target_type_name, #preloads_discussion_diff_highlighting?, #real_time_notes_enabled?, #resolvable_discussions, #supports_creating_notes_by_email?, #supports_discussions?, #supports_replying_to_individual_notes?, #supports_resolvable_notes?, #supports_suggestion?
#attribute_invalidated?, #banzai_render_context, #cached_html_for, #cached_html_up_to_date?, #invalidated_markdown_cache?, #latest_cached_markdown_version, #mentionable_attributes_changed?, #mentioned_filtered_user_ids_for, #parent_user, #refresh_markdown_cache, #refresh_markdown_cache!, #rendered_field_content, #skip_project_check?, #store_mentions!, #store_mentions_after_commit?, #updated_cached_html_for
allowed_for?, allowed_level?, allowed_levels, allowed_levels_for_user, closest_allowed_level, #internal?, level_name, level_value, levels_for_user, non_restricted_level?, options, #private?, #public?, public_visibility_restricted?, restricted_level?, string_level, string_options, string_values, valid_level?, #visibility, #visibility=, #visibility_attribute_present?, #visibility_attribute_value, #visibility_level_attributes, #visibility_level_previous_changes, #visibility_level_value
===, cached_column_list, #create_or_load_association, declarative_enum, default_select_columns, id_in, id_not_in, iid_in, nullable_column?, pluck_primary_key, primary_key_in, #readable_by?, safe_ensure_unique, safe_find_or_create_by, safe_find_or_create_by!, underscore, where_exists, where_not_exists, with_fast_read_statement_timeout, without_order
#reset_on_union_error, #reset_on_unknown_attribute_error
#serializable_hash
Constructor Details
#initialize(attributes = {}) ⇒ Snippet
Returns a new instance of Snippet.
215
216
217
218
219
220
221
222
223
224
|
# File 'app/models/snippet.rb', line 215
def initialize(attributes = {})
attributes ||= {}
unless visibility_attribute_present?(attributes)
attributes[:visibility_level] = Gitlab::CurrentSettings.default_snippet_visibility
end
super
end
|
Class Method Details
.find_by_id_and_project(id:, project:) ⇒ Object
199
200
201
|
# File 'app/models/snippet.rb', line 199
def find_by_id_and_project(id:, project:)
Snippet.find_by(id: id, project: project)
end
|
.find_by_project_title_trunc_created_at(project, title, created_at) ⇒ Object
203
204
205
206
207
208
|
# File 'app/models/snippet.rb', line 203
def find_by_project_title_trunc_created_at(project, title, created_at)
where(project: project, title: title)
.find_by(
"date_trunc('second', created_at at time zone :tz) at time zone :tz = :created_at",
tz: created_at.zone, created_at: created_at)
end
|
.for_project_with_user(project, user = nil) ⇒ Object
166
167
168
169
170
171
172
173
174
|
# File 'app/models/snippet.rb', line 166
def for_project_with_user(project, user = nil)
return none unless project.snippets_visible?(user)
if project.member?(user)
project.snippets
else
project.snippets.public_to_user(user)
end
end
|
.link_reference_pattern ⇒ Object
195
196
197
|
# File 'app/models/snippet.rb', line 195
def link_reference_pattern
@link_reference_pattern ||= compose_link_reference_pattern('snippets', /(?<snippet>\d+)/)
end
|
.max_file_limit ⇒ Object
210
211
212
|
# File 'app/models/snippet.rb', line 210
def max_file_limit
MAX_FILE_COUNT
end
|
.only_include_authorized_projects(current_user) ⇒ Object
156
157
158
159
160
161
162
163
164
|
# File 'app/models/snippet.rb', line 156
def only_include_authorized_projects(current_user)
where(
'EXISTS (?)',
ProjectAuthorization
.select(1)
.where('project_id = snippets.project_id')
.where(user_id: current_user.id)
)
end
|
.only_include_projects_visible_to(current_user = nil) ⇒ Object
140
141
142
143
144
|
# File 'app/models/snippet.rb', line 140
def only_include_projects_visible_to(current_user = nil)
levels = Gitlab::VisibilityLevel.levels_for_user(current_user)
joins(:project).where(projects: { visibility_level: levels })
end
|
.only_include_projects_with_snippets_enabled(include_private: false) ⇒ Object
146
147
148
149
150
151
152
153
154
|
# File 'app/models/snippet.rb', line 146
def only_include_projects_with_snippets_enabled(include_private: false)
column = ProjectFeature.access_level_attribute(:snippets)
levels = [ProjectFeature::ENABLED, ProjectFeature::PUBLIC]
levels << ProjectFeature::PRIVATE if include_private
joins(project: :project_feature)
.where(project_features: { column => levels })
end
|
.only_personal_snippets ⇒ Object
132
133
134
|
# File 'app/models/snippet.rb', line 132
def only_personal_snippets
where(project_id: nil)
end
|
.only_project_snippets ⇒ Object
136
137
138
|
# File 'app/models/snippet.rb', line 136
def only_project_snippets
where.not(project_id: nil)
end
|
.parent_class ⇒ Object
116
117
118
|
# File 'app/models/snippet.rb', line 116
def parent_class
::Project
end
|
.reference_pattern ⇒ Object
Pattern used to extract ‘$123` snippet references from text
This pattern supports cross-project references.
188
189
190
191
192
193
|
# File 'app/models/snippet.rb', line 188
def reference_pattern
@reference_pattern ||= %r{
(#{Project.reference_pattern})?
#{Regexp.escape(reference_prefix)}(?<snippet>\d+)
}x
end
|
.reference_prefix ⇒ Object
181
182
183
|
# File 'app/models/snippet.rb', line 181
def reference_prefix
'$'
end
|
.sanitized_file_name(file_name) ⇒ Object
120
121
122
|
# File 'app/models/snippet.rb', line 120
def sanitized_file_name(file_name)
file_name.gsub(/[^a-zA-Z0-9_\-\.]+/, '')
end
|
.search(query) ⇒ Object
Searches for snippets with a matching title, description or file name.
This method uses ILIKE on PostgreSQL.
query - The search query as a String.
Returns an ActiveRecord::Relation.
112
113
114
|
# File 'app/models/snippet.rb', line 112
def search(query)
fuzzy_search(query, [:title, :description, :file_name])
end
|
.visible_to_or_authored_by(user) ⇒ Object
176
177
178
179
|
# File 'app/models/snippet.rb', line 176
def visible_to_or_authored_by(user)
query = where(visibility_level: Gitlab::VisibilityLevel.levels_for_user(user))
query.or(where(author_id: user.id))
end
|
.with_optional_visibility(value = nil) ⇒ Object
124
125
126
127
128
129
130
|
# File 'app/models/snippet.rb', line 124
def with_optional_visibility(value = nil)
if value
where(visibility_level: value)
else
all
end
end
|
Instance Method Details
#all_files ⇒ Object
236
237
238
|
# File 'app/models/snippet.rb', line 236
def all_files
list_files(default_branch)
end
|
#as_json(options = {}) ⇒ Object
304
305
306
307
308
309
|
# File 'app/models/snippet.rb', line 304
def as_json(options = {})
options[:except] = Array.wrap(options[:except])
options[:except] << :secret_token
super
end
|
#blobs(paths = []) ⇒ Object
244
245
246
247
248
249
250
251
|
# File 'app/models/snippet.rb', line 244
def blobs(paths = [])
return [] unless repository_exists?
paths = all_files if paths.empty?
items = paths.map { |path| [default_branch, path] }
repository.blobs_at(items).compact
end
|
#can_cache_field?(field) ⇒ Boolean
#check_for_spam? ⇒ Boolean
286
287
288
|
# File 'app/models/snippet.rb', line 286
def check_for_spam?(*)
visibility_level_changed?(to: Snippet::PUBLIC) || (public? && spammable_attribute_changed?)
end
|
#content_html_invalidated? ⇒ Boolean
44
45
46
|
# File 'app/models/snippet.rb', line 44
def content_html_invalidated?
default_content_html_invalidator || file_name_changed?
end
|
#create_repository ⇒ Object
355
356
357
358
359
360
|
# File 'app/models/snippet.rb', line 355
def create_repository
return if repository_exists? && snippet_repository
repository.create_if_not_exists(default_branch)
track_snippet_repository(repository.storage)
end
|
#default_branch ⇒ Object
347
348
349
|
# File 'app/models/snippet.rb', line 347
def default_branch
super || Gitlab::DefaultBranch.value(object: project)
end
|
#default_content_html_invalidator ⇒ Object
If file_name changes, it invalidates content
43
|
# File 'app/models/snippet.rb', line 43
alias_method :default_content_html_invalidator, :content_html_invalidated?
|
#embeddable? ⇒ Boolean
278
279
280
|
# File 'app/models/snippet.rb', line 278
def embeddable?
Ability.allowed?(nil, :read_snippet, self)
end
|
#file_name ⇒ Object
270
271
272
|
# File 'app/models/snippet.rb', line 270
def file_name
super.to_s
end
|
#file_name_on_repo ⇒ Object
375
376
377
378
379
|
# File 'app/models/snippet.rb', line 375
def file_name_on_repo
return if repository.empty?
list_files(default_branch).first
end
|
#full_path ⇒ Object
334
335
336
337
338
339
340
341
342
343
344
|
# File 'app/models/snippet.rb', line 334
def full_path
return unless persisted?
@full_path ||= begin
components = []
components << project.full_path if project_id?
components << 'snippets'
components << self.id
components.join('/')
end
end
|
#hexdigest ⇒ Object
371
372
373
|
# File 'app/models/snippet.rb', line 371
def hexdigest
Digest::SHA256.hexdigest("#{title}#{description}#{created_at}#{updated_at}")
end
|
#hidden_due_to_author_ban? ⇒ Boolean
391
392
393
|
# File 'app/models/snippet.rb', line 391
def hidden_due_to_author_ban?
Feature.enabled?(:hide_snippets_of_banned_users) && author.banned?
end
|
#hook_attrs ⇒ Object
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
|
# File 'app/models/snippet.rb', line 253
def hook_attrs
{
id: id,
title: title,
description: description,
content: content,
author_id: author_id,
project_id: project_id,
created_at: created_at,
updated_at: updated_at,
file_name: file_name,
type: type,
visibility_level: visibility_level,
url: Gitlab::UrlBuilder.build(self)
}
end
|
#list_files(ref = nil) ⇒ Object
381
382
383
384
385
|
# File 'app/models/snippet.rb', line 381
def list_files(ref = nil)
return [] if repository.empty?
repository.ls_files(ref || default_branch)
end
|
#multiple_files? ⇒ Boolean
387
388
389
|
# File 'app/models/snippet.rb', line 387
def multiple_files?
list_files.size > 1
end
|
#notes_with_associations ⇒ Object
282
283
284
|
# File 'app/models/snippet.rb', line 282
def notes_with_associations
notes.includes(:author)
end
|
#repository ⇒ Object
312
313
314
|
# File 'app/models/snippet.rb', line 312
def repository
@repository ||= Gitlab::GlRepository::SNIPPET.repository_for(self)
end
|
#repository_size_checker ⇒ Object
317
318
319
320
321
322
323
324
325
|
# File 'app/models/snippet.rb', line 317
def repository_size_checker
strong_memoize(:repository_size_checker) do
::Gitlab::RepositorySizeChecker.new(
current_size_proc: -> { repository.size.megabytes },
limit: Gitlab::CurrentSettings.snippet_size_limit,
namespace: nil
)
end
end
|
#repository_storage ⇒ Object
351
352
353
|
# File 'app/models/snippet.rb', line 351
def repository_storage
snippet_repository&.shard_name || Repository.pick_storage_shard
end
|
#supports_recaptcha? ⇒ Boolean
290
291
292
|
# File 'app/models/snippet.rb', line 290
def supports_recaptcha?
true
end
|
#to_ability_name ⇒ Object
294
295
296
|
# File 'app/models/snippet.rb', line 294
def to_ability_name
'snippet'
end
|
#to_reference(from = nil, full: false) ⇒ Object
226
227
228
229
230
231
232
233
234
|
# File 'app/models/snippet.rb', line 226
def to_reference(from = nil, full: false)
reference = "#{self.class.reference_prefix}#{id}"
if project.present?
"#{project.to_reference_base(from, full: full)}#{reference}"
else
reference
end
end
|
#track_snippet_repository(shard) ⇒ Object
362
363
364
365
|
# File 'app/models/snippet.rb', line 362
def track_snippet_repository(shard)
snippet_repo = snippet_repository || build_snippet_repository
snippet_repo.update!(shard_name: shard, disk_path: disk_path)
end
|
#valid_secret_token?(token) ⇒ Boolean
298
299
300
301
302
|
# File 'app/models/snippet.rb', line 298
def valid_secret_token?(token)
return false unless token && secret_token
ActiveSupport::SecurityUtils.secure_compare(token.to_s, secret_token.to_s)
end
|
#visibility_level_field ⇒ Object
274
275
276
|
# File 'app/models/snippet.rb', line 274
def visibility_level_field
:visibility_level
end
|