Class: Wiki
Constant Summary
collapse
- MARKUPS =
rubocop:disable Style/MultilineIfModifier
{ markdown: {
name: 'Markdown',
default_extension: :md,
extension_regex: Regexp.new('md|mkdn?|mdown|markdown', 'i'),
created_by_user: true
},
rdoc: {
name: 'RDoc',
default_extension: :rdoc,
extension_regex: Regexp.new('rdoc', 'i'),
created_by_user: true
},
asciidoc: {
name: 'AsciiDoc',
default_extension: :asciidoc,
extension_regex: Regexp.new('adoc|asciidoc', 'i'),
created_by_user: true
},
org: {
name: 'Org',
default_extension: :org,
extension_regex: Regexp.new('org', 'i'),
created_by_user: true
},
textile: {
name: 'Textile',
default_extension: :textile,
extension_regex: Regexp.new('textile', 'i')
},
creole: {
name: 'Creole',
default_extension: :creole,
extension_regex: Regexp.new('creole', 'i')
},
rest: {
name: 'reStructuredText',
default_extension: :rst,
extension_regex: Regexp.new('re?st(\.txt)?', 'i')
},
mediawiki: {
name: 'MediaWiki',
default_extension: :mediawiki,
extension_regex: Regexp.new('(media)?wiki', 'i')
},
pod: {
name: 'Pod',
default_extension: :pod,
extension_regex: Regexp.new('pod', 'i')
},
plaintext: {
name: 'Plain Text',
default_extension: :txt,
extension_regex: Regexp.new('txt', 'i')
}
}.freeze
- VALID_USER_MARKUPS =
MARKUPS.select { |_, v| v[:created_by_user] }.freeze
- ALLOWED_EXTENSIONS_REGEX =
Regexp.union(MARKUPS.map { |key, value| value[:extension_regex] }).freeze
- CouldNotCreateWikiError =
Class.new(StandardError)
- HOMEPAGE =
'home'
'_sidebar'
- TITLE_ORDER =
'title'
- CREATED_AT_ORDER =
'created_at'
- DIRECTION_DESC =
'desc'
- DIRECTION_ASC =
'asc'
Class Attribute Summary collapse
Instance Attribute Summary collapse
Class Method Summary
collapse
Instance Method Summary
collapse
-
#==(other) ⇒ Object
-
#after_post_receive ⇒ Object
Callbacks for background processing after wiki changes.
-
#after_wiki_activity ⇒ Object
Callbacks for synchronous processing after wiki changes.
-
#capture_git_error(action, &block) ⇒ Object
-
#cleanup ⇒ Object
-
#create_page(title, content, format = :markdown, message = nil) ⇒ Object
-
#create_wiki_repository ⇒ Object
-
#default_branch ⇒ Object
-
#delete_page(page, message = nil) ⇒ Object
-
#empty? ⇒ Boolean
-
#exists? ⇒ Boolean
-
#find_file(name, version = default_branch, load_content: true) ⇒ Object
-
#find_page(title, version = nil, load_content: true) ⇒ Object
Finds a page within the repository based on a title or slug.
-
#find_sidebar(version = nil) ⇒ Object
-
#full_path ⇒ Object
(also: #path_with_namespace)
-
#git_garbage_collect_worker_klass ⇒ Object
-
#has_home_page? ⇒ Boolean
-
#hashed_storage? ⇒ Boolean
-
#hook_attrs ⇒ Object
-
#id ⇒ Object
This is needed in: - Storage::Hashed - Gitlab::GlRepository::RepoType#identifier_for_container.
-
#initialize(container, user = nil) ⇒ Wiki
constructor
-
#list_pages(direction: DIRECTION_ASC, load_content: false, limit: 0, offset: 0) ⇒ Object
Lists wiki pages of the repository.
-
#page_title_and_dir(title) ⇒ Object
-
#path ⇒ Object
-
#repository ⇒ Object
-
#repository_storage ⇒ Object
-
#sidebar_entries(limit: Gitlab::WikiPages::MAX_SIDEBAR_PAGES, **options) ⇒ Object
-
#update_page(page, content:, title: nil, format: :markdown, message: nil) ⇒ Object
-
#wiki_base_path ⇒ Object
extended, extensions, included, method_added, override, prepended, queue_verification, verify!
#wrapped_gitaly_errors
#increment_pushes_since_gc, #pushes_since_gc, #reset_pushes_since_gc
#after_change_head_branch_does_not_exist, #after_repository_change_head, #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?, #repository_size_checker, #root_ref?, #ssh_url_to_repo, #storage, #url_to_repo, #valid_repo?, #web_url
#gitlab_shell
Methods included from Referable
#referable_inspect, #reference_link_text, #to_reference, #to_reference_base
Constructor Details
#initialize(container, user = nil) ⇒ Wiki
Returns a new instance of Wiki.
143
144
145
146
147
148
|
# File 'app/models/wiki.rb', line 143
def initialize(container, user = nil)
raise ArgumentError, "user must be a User, got #{user.class}" if user && !user.is_a?(User)
@container = container
@user = user
end
|
Class Attribute Details
.container_class ⇒ Object
Returns the value of attribute container_class.
97
98
99
|
# File 'app/models/wiki.rb', line 97
def container_class
@container_class
end
|
Instance Attribute Details
#container ⇒ Object
Returns the value of attribute container.
86
87
88
|
# File 'app/models/wiki.rb', line 86
def container
@container
end
|
#error_message ⇒ Object
Returns a string describing what went wrong after an operation fails.
90
91
92
|
# File 'app/models/wiki.rb', line 90
def error_message
@error_message
end
|
#user ⇒ Object
Returns the value of attribute user.
86
87
88
|
# File 'app/models/wiki.rb', line 86
def user
@user
end
|
Class Method Details
.canonicalize_filename(filename) ⇒ Object
119
120
121
|
# File 'app/models/wiki.rb', line 119
def canonicalize_filename(filename)
::File.basename(filename, ::File.extname(filename)).tr('-', ' ')
end
|
.cname(name, char_white_sub = '-', char_other_sub = '-') ⇒ Object
123
124
125
|
# File 'app/models/wiki.rb', line 123
def cname(name, char_white_sub = '-', char_other_sub = '-')
name.to_s.gsub(/\s/, char_white_sub).gsub(/[<>+]/, char_other_sub)
end
|
.find_by_id(container_id) ⇒ Object
This is needed to support repository lookup through Gitlab::GlRepository::Identifier
104
105
106
|
# File 'app/models/wiki.rb', line 104
def find_by_id(container_id)
container_class.find_by_id(container_id)&.wiki
end
|
.for_container(container, user = nil) ⇒ Object
99
100
101
|
# File 'app/models/wiki.rb', line 99
def for_container(container, user = nil)
"#{container.class.name}Wiki".constantize.new(container, user)
end
|
.preview_slug(title, format) ⇒ Object
127
128
129
130
131
132
133
134
135
136
137
138
139
140
|
# File 'app/models/wiki.rb', line 127
def preview_slug(title, format)
ext = format == :markdown ? "md" : format.to_s
name = cname(title) + '.' + ext
canonical_name = canonicalize_filename(name)
path =
if name.include?('/')
name.sub(%r{/[^/]+$}, '/')
else
''
end
path + cname(canonical_name, '-', '-')
end
|
.sluggified_full_path(title, extension) ⇒ Object
108
109
110
|
# File 'app/models/wiki.rb', line 108
def sluggified_full_path(title, extension)
sluggified_title(title) + '.' + extension
end
|
.sluggified_title(title) ⇒ Object
112
113
114
115
116
117
|
# File 'app/models/wiki.rb', line 112
def sluggified_title(title)
title = Gitlab::EncodingHelper.encode_utf8_no_detect(title.to_s.strip)
title = File.absolute_path(title, '/')
title = Pathname.new(title).relative_path_from('/').to_s
title.tr(' ', '-')
end
|
Instance Method Details
#==(other) ⇒ Object
150
151
152
|
# File 'app/models/wiki.rb', line 150
def ==(other)
other.is_a?(self.class) && container == other.container
end
|
#after_post_receive ⇒ Object
Callbacks for background processing after wiki changes. These will be executed after any change to the wiki repository.
407
|
# File 'app/models/wiki.rb', line 407
def after_post_receive; end
|
#after_wiki_activity ⇒ Object
Callbacks for synchronous processing after wiki changes. These will be executed after any change made through GitLab itself (web UI and API), but not for Git pushes.
403
|
# File 'app/models/wiki.rb', line 403
def after_wiki_activity; end
|
#capture_git_error(action, &block) ⇒ Object
#cleanup ⇒ Object
414
415
416
|
# File 'app/models/wiki.rb', line 414
def cleanup
@repository = nil
end
|
#create_page(title, content, format = :markdown, message = nil) ⇒ Object
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
|
# File 'app/models/wiki.rb', line 289
def create_page(title, content, format = :markdown, message = nil)
with_valid_format(format) do |default_extension|
next duplicated_page_error if file_exists_by_regex?(title)
capture_git_error(:created) do
create_wiki_repository unless repository_exists?
sanitized_path = sluggified_full_path(title, default_extension)
repository.create_file(user, sanitized_path, content, **multi_commit_options(:created, message, title))
repository.expire_status_cache if repository.empty?
after_wiki_activity
true
rescue Gitlab::Git::Index::IndexError
duplicated_page_error
end
end
end
|
#create_wiki_repository ⇒ Object
172
173
174
175
176
177
178
179
180
181
182
183
184
185
|
# File 'app/models/wiki.rb', line 172
def create_wiki_repository
repository.create_if_not_exists(default_branch)
raise CouldNotCreateWikiError unless repository_exists?
rescue StandardError => e
Gitlab::ErrorTracking.track_exception(e, wiki: {
container_type: container.class.name,
container_id: container.id,
full_path: full_path,
disk_path: disk_path
})
raise CouldNotCreateWikiError
end
|
#default_branch ⇒ Object
392
393
394
|
# File 'app/models/wiki.rb', line 392
def default_branch
super || Gitlab::DefaultBranch.value(object: container)
end
|
#delete_page(page, message = nil) ⇒ Object
339
340
341
342
343
344
345
346
347
348
349
350
|
# File 'app/models/wiki.rb', line 339
def delete_page(page, message = nil)
return unless page
capture_git_error(:deleted) do
create_wiki_repository unless repository_exists?
repository.delete_file(user, page.path, **multi_commit_options(:deleted, message, page.title))
after_wiki_activity
true
end
end
|
#empty? ⇒ Boolean
193
194
195
|
# File 'app/models/wiki.rb', line 193
def empty?
!repository_exists? || list_page_paths(limit: 1).empty?
end
|
#exists? ⇒ Boolean
197
198
199
|
# File 'app/models/wiki.rb', line 197
def exists?
!empty?
end
|
#find_file(name, version = default_branch, load_content: true) ⇒ Object
280
281
282
283
284
285
286
287
|
# File 'app/models/wiki.rb', line 280
def find_file(name, version = default_branch, load_content: true)
data_limit = load_content ? -1 : 0
blobs = repository.blobs_at([[version, name]], blob_size_limit: data_limit)
return if blobs.empty?
Gitlab::Git::WikiFile.new(blobs.first)
end
|
#find_page(title, version = nil, load_content: true) ⇒ Object
Finds a page within the repository based on a title or slug.
title - The human readable or parameterized title of
the page.
Returns an initialized WikiPage instance or nil
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
|
# File 'app/models/wiki.rb', line 251
def find_page(title, version = nil, load_content: true)
create_wiki_repository unless repository_exists?
version = version.presence || default_branch
path = find_matched_file(title, version)
return if path.blank?
blob_options = load_content ? {} : { limit: 0 }
blob = repository.blob_at(version, path, **blob_options)
commit = repository.commit(blob.commit_id)
format = find_page_format(path)
page = Gitlab::Git::WikiPage.new(
url_path: sluggified_title(strip_extension(path)),
title: canonicalize_filename(path),
format: format,
path: sluggified_title(path),
raw_data: blob.data,
name: canonicalize_filename(path),
historical: version == default_branch ? false : check_page_historical(path, commit),
version: Gitlab::Git::WikiPageVersion.new(commit, format)
)
WikiPage.new(self, page)
end
|
276
277
278
|
# File 'app/models/wiki.rb', line 276
def (version = nil)
find_page(SIDEBAR, version)
end
|
#full_path ⇒ Object
Also known as:
path_with_namespace
384
385
386
|
# File 'app/models/wiki.rb', line 384
def full_path
container.full_path + '.wiki'
end
|
#git_garbage_collect_worker_klass ⇒ Object
#has_home_page? ⇒ Boolean
187
188
189
190
191
|
# File 'app/models/wiki.rb', line 187
def has_home_page?
!!find_page(HOMEPAGE)
rescue StandardError
false
end
|
#hashed_storage? ⇒ Boolean
379
380
381
|
# File 'app/models/wiki.rb', line 379
def hashed_storage?
raise NotImplementedError
end
|
#hook_attrs ⇒ Object
360
361
362
363
364
365
366
367
368
|
# File 'app/models/wiki.rb', line 360
def hook_attrs
{
web_url: web_url,
git_ssh_url: ssh_url_to_repo,
git_http_url: http_url_to_repo,
path_with_namespace: full_path,
default_branch: default_branch
}
end
|
#id ⇒ Object
This is needed in:
We also need an ‘#id` to support `build_stubbed` in tests, where the value doesn’t matter.
NOTE: Wikis don’t have a DB record, so this ID can be the same for two wikis in different containers and should not be expected to be unique. Use ‘to_global_id` instead if you need a unique ID.
164
165
166
|
# File 'app/models/wiki.rb', line 164
def id
container.id
end
|
#list_pages(direction: DIRECTION_ASC, load_content: false, limit: 0, offset: 0) ⇒ Object
Lists wiki pages of the repository.
limit - max number of pages returned by the method. sort - criterion by which the pages are sorted. direction - order of the sorted pages. load_content - option, which specifies whether the content inside the page
will be loaded.
Returns an Array of GitLab WikiPage instances or an empty Array if this Wiki has no pages.
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
|
# File 'app/models/wiki.rb', line 211
def list_pages(direction: DIRECTION_ASC, load_content: false, limit: 0, offset: 0)
create_wiki_repository unless repository_exists?
paths = list_page_paths(limit: limit, offset: offset)
return [] if paths.empty?
pages = paths.map do |path|
page = Gitlab::Git::WikiPage.new(
url_path: sluggified_title(strip_extension(path)),
title: canonicalize_filename(path),
format: find_page_format(path),
path: sluggified_title(path),
raw_data: '',
name: canonicalize_filename(path),
historical: false
)
WikiPage.new(self, page)
end
sort_pages!(pages, direction)
pages = pages.take(limit) if limit > 0
fetch_pages_content!(pages) if load_content
pages
end
|
#page_title_and_dir(title) ⇒ Object
352
353
354
355
356
357
358
|
# File 'app/models/wiki.rb', line 352
def page_title_and_dir(title)
return unless title
title_array = title.split("/")
title = title_array.pop
[title, title_array.join("/")]
end
|
#path ⇒ Object
168
169
170
|
# File 'app/models/wiki.rb', line 168
def path
container.path + '.wiki'
end
|
#repository ⇒ Object
371
372
373
|
# File 'app/models/wiki.rb', line 371
def repository
@repository ||= Gitlab::GlRepository::WIKI.repository_for(self)
end
|
#repository_storage ⇒ Object
375
376
377
|
# File 'app/models/wiki.rb', line 375
def repository_storage
raise NotImplementedError
end
|
236
237
238
239
240
241
242
|
# File 'app/models/wiki.rb', line 236
def (limit: Gitlab::WikiPages::MAX_SIDEBAR_PAGES, **options)
pages = list_pages(**options.merge(limit: limit + 1))
limited = pages.size > limit
pages = pages.first(limit) if limited
[WikiDirectory.group_pages(pages), limited]
end
|
#update_page(page, content:, title: nil, format: :markdown, message: nil) ⇒ Object
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
|
# File 'app/models/wiki.rb', line 307
def update_page(page, content:, title: nil, format: :markdown, message: nil)
with_valid_format(format) do |default_extension|
title = title.presence || Pathname(page.path).sub_ext('').to_s
extension = page.format != format.to_sym ? default_extension : File.extname(page.path).downcase[1..]
capture_git_error(:updated) do
create_wiki_repository unless repository_exists?
repository.update_file(
user,
sluggified_full_path(title, extension),
content,
previous_path: page.path,
**multi_commit_options(:updated, message, title))
repository.move_dir_files(
user,
sluggified_title(title),
page.url_path,
**multi_commit_options(:moved, message, title))
after_wiki_activity
true
rescue Gitlab::Git::Index::IndexError
duplicated_page_error
end
end
end
|
#wiki_base_path ⇒ Object
396
397
398
|
# File 'app/models/wiki.rb', line 396
def wiki_base_path
web_url(only_path: true).sub(%r{/#{Wiki::HOMEPAGE}\z}o, '')
end
|