Class: WikiPage::Meta

Inherits:
ApplicationRecord show all
Includes:
Gitlab::Utils::StrongMemoize, Mentionable, Noteable, Participable, Subscribable, Todoable
Defined in:
app/models/wiki_page/meta.rb

Constant Summary collapse

WikiPageInvalid =
Class.new(ArgumentError)

Constants included from Noteable

Noteable::MAX_NOTES_LIMIT

Constants inherited from ApplicationRecord

ApplicationRecord::MAX_PLUCK

Constants included from HasCheckConstraints

HasCheckConstraints::NOT_NULL_CHECK_PATTERN

Constants included from ResetOnColumnErrors

ResetOnColumnErrors::MAX_RESET_PERIOD

Instance Attribute Summary

Attributes included from Noteable

#system_note_timestamp

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Subscribable

#lazy_subscription, #set_subscription, #subscribe, #subscribed?, #subscribers, #toggle_subscription, #unsubscribe

Methods included from Participable

#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?

Methods included from Mentionable

#all_references, #create_cross_references!, #create_new_cross_references!, #directly_addressed_users, #extractors, #local_reference, #matches_cross_reference_regex?, #mentioned_users, #referenced_group_users, #referenced_groups, #referenced_mentionables, #referenced_projects, #referenced_users, #user_mention_class, #user_mention_identifier

Methods inherited from ApplicationRecord

===, cached_column_list, #create_or_load_association, current_transaction, declarative_enum, default_select_columns, delete_all_returning, #deleted_from_database?, id_in, id_not_in, iid_in, nullable_column?, primary_key_in, 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

Methods included from Organizations::Sharding

#sharding_organization

Methods included from ResetOnColumnErrors

#reset_on_union_error, #reset_on_unknown_attribute_error

Methods included from Gitlab::SensitiveSerializableHash

#serializable_hash

Class Method Details

.declarative_policy_classObject



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

def declarative_policy_class
  'WikiPagePolicy'
end

.find_by_canonical_slug(canonical_slug, container) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'app/models/wiki_page/meta.rb', line 77

def find_by_canonical_slug(canonical_slug, container)
  return unless canonical_slug.present? && container.present?

  meta, conflict = with_canonical_slug(canonical_slug)
    .where(container_attrs(container))
    .limit(2)

  if conflict.present?
    # Ensure the conflict record will be the orphaned record when doing a page update
    if canonical_slug.size > 1
      old_slug, _new_slug = canonical_slug

      meta, conflict = conflict, meta if conflict.canonical_slug == old_slug
    end

    transaction(requires_new: false) do
      conflict.todos.each_batch do |batch|
        batch.update_all(target_id: meta.id)
      end

      conflict.destroy
    end
  end

  meta
end

.find_or_create(last_known_slug, wiki_page) ⇒ Object

Return the (updated) WikiPage::Meta record for a given wiki page

If none is found, then a new record is created, and its fields are set to reflect the wiki_page passed.

This method raises errors on validation issues.

Parameters:

  • last_known_slug (String)
  • wiki_page (WikiPage)

Raises:



55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
# File 'app/models/wiki_page/meta.rb', line 55

def find_or_create(last_known_slug, wiki_page)
  raise WikiPageInvalid unless wiki_page.valid?

  container = wiki_page.wiki.container
  known_slugs = [last_known_slug, wiki_page.slug].compact.uniq
  raise 'No slugs found! This should not be possible.' if known_slugs.empty?

  transaction do
    updates = wiki_page_updates(wiki_page)
    found = find_by_canonical_slug(known_slugs, container)
    meta = found || create!(updates.merge(container_attrs(container)))

    meta.update_state(found.nil?, known_slugs, wiki_page, updates)

    # We don't need to run validations here, since find_by_canonical_slug
    # guarantees that there is no conflict in canonical_slug, and DB
    # constraints on title and project_id/group_id enforce our other invariants
    # This saves us a query.
    meta
  end
end

Instance Method Details

#canonical_slugObject



152
153
154
# File 'app/models/wiki_page/meta.rb', line 152

def canonical_slug
  slugs.canonical.take&.slug
end

#canonical_slug=(slug) ⇒ Object



157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
# File 'app/models/wiki_page/meta.rb', line 157

def canonical_slug=(slug)
  return if @canonical_slug == slug

  if persisted?
    transaction do
      slugs.canonical.update_all(canonical: false)
      page_slug = slugs.create_with(canonical: true).find_or_create_by(slug: slug)
      page_slug.update_columns(canonical: true) unless page_slug.canonical?
    end
  else
    slugs.new(slug: slug, canonical: true)
  end

  @canonical_slug = slug
end

#containerObject



131
132
133
# File 'app/models/wiki_page/meta.rb', line 131

def container
  project || namespace
end

#container=(value) ⇒ Object



135
136
137
138
# File 'app/models/wiki_page/meta.rb', line 135

def container=(value)
  self.project = value if value.is_a?(Project)
  self.namespace = value if value.is_a?(Namespace)
end

#container_keyObject



148
149
150
# File 'app/models/wiki_page/meta.rb', line 148

def container_key
  for_group_wiki? ? :namespace_id : :project_id
end

#for_group_wiki?Boolean

Returns:

  • (Boolean)


144
145
146
# File 'app/models/wiki_page/meta.rb', line 144

def for_group_wiki?
  namespace_id.present?
end

#gfm_reference(from = nil) ⇒ Object



179
180
181
# File 'app/models/wiki_page/meta.rb', line 179

def gfm_reference(from = nil)
  "#{container.class.name.downcase} wiki page #{to_reference(from)}"
end

#notes_with_associationsObject



196
197
198
# File 'app/models/wiki_page/meta.rb', line 196

def notes_with_associations
  notes.includes(:author)
end

#readable_by?(user) ⇒ Boolean

Used by app/policies/todo_policy.rb

Returns:

  • (Boolean)


188
189
190
# File 'app/models/wiki_page/meta.rb', line 188

def readable_by?(user)
  Ability.allowed?(user, :read_wiki, self)
end


183
184
185
# File 'app/models/wiki_page/meta.rb', line 183

def reference_link_text
  canonical_slug
end

#resource_parentObject



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

def resource_parent
  container
end

#subscribed_without_subscriptions?(user, _project) ⇒ Boolean

Returns:

  • (Boolean)


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

def subscribed_without_subscriptions?(user, _project)
  participant?(user)
end

#to_ability_nameObject



192
193
194
# File 'app/models/wiki_page/meta.rb', line 192

def to_ability_name
  'wiki_page'
end

#update_state(created, known_slugs, wiki_page, updates) ⇒ Object



173
174
175
176
177
# File 'app/models/wiki_page/meta.rb', line 173

def update_state(created, known_slugs, wiki_page, updates)
  update_wiki_page_attributes(updates)
  insert_slugs(known_slugs, created, wiki_page.slug)
  self.canonical_slug = wiki_page.slug
end

#wiki_pageObject



127
128
129
# File 'app/models/wiki_page/meta.rb', line 127

def wiki_page
  wiki.find_page(canonical_slug, load_content: true)
end