diff options
Diffstat (limited to 'app/models/wiki.rb')
-rw-r--r-- | app/models/wiki.rb | 126 |
1 files changed, 90 insertions, 36 deletions
diff --git a/app/models/wiki.rb b/app/models/wiki.rb index b3f09b20463..32d70fcd3b7 100644 --- a/app/models/wiki.rb +++ b/app/models/wiki.rb @@ -13,43 +13,65 @@ class Wiki 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 + default_extension: :textile, + extension_regex: Regexp.new('textile', 'i') }, creole: { name: 'Creole', - default_extension: :creole + default_extension: :creole, + extension_regex: Regexp.new('creole', 'i') }, rest: { name: 'reStructuredText', - default_extension: :rst + default_extension: :rst, + extension_regex: Regexp.new('re?st(\.txt)?', 'i') }, mediawiki: { name: 'MediaWiki', - default_extension: :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 unless defined?(MARKUPS) VALID_USER_MARKUPS = MARKUPS.select { |_, v| v[:created_by_user] }.freeze unless defined?(VALID_USER_MARKUPS) + unless defined?(ALLOWED_EXTENSIONS_REGEX) + ALLOWED_EXTENSIONS_REGEX = Regexp.union(MARKUPS.map { |key, value| value[:extension_regex] }).freeze + end + CouldNotCreateWikiError = Class.new(StandardError) HOMEPAGE = 'home' @@ -205,50 +227,61 @@ class Wiki end def create_page(title, content, format = :markdown, message = nil) - commit = commit_details(:created, message, title) - - wiki.write_page(title, format.to_sym, content, commit) - repository.expire_status_cache if repository.empty? - after_wiki_activity - - true - rescue Gitlab::Git::Wiki::DuplicatePageError => e - @error_message = "Duplicate page: #{e.message}" - false - end - - def update_page(page, content:, title: nil, format: :markdown, message: nil) - if Feature.enabled?(:gitaly_replace_wiki_update_page, container, default_enabled: :yaml) + if Feature.enabled?(:gitaly_replace_wiki_create_page, container, type: :undefined) with_valid_format(format) do |default_extension| - title = title.presence || Pathname(page.path).sub_ext('').to_s - - # If the format is the same we keep the former extension. This check is for formats - # that can have more than one extension like Markdown (.md, .markdown) - # If we don't do this we will override the existing extension. - extension = page.format != format.to_sym ? default_extension : File.extname(page.path).downcase[1..] - - capture_git_error(:updated) do - repository.update_file( - user, - sluggified_full_path(title, extension), - content, - previous_path: page.path, - **multi_commit_options(:updated, message, title)) + if file_exists_by_regex?(title) + raise_duplicate_page_error! + end + 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 + raise_duplicate_page_error! end end else - commit = commit_details(:updated, message, page.title) - - wiki.update_page(page.path, title || page.name, format.to_sym, content, commit) + commit = commit_details(:created, message, title) + wiki.write_page(title, format.to_sym, content, commit) + repository.expire_status_cache if repository.empty? after_wiki_activity true end + rescue Gitlab::Git::Wiki::DuplicatePageError => e + @error_message = _("Duplicate page: %{error_message}" % { error_message: e.message }) + + false + end + + 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 + + # If the format is the same we keep the former extension. This check is for formats + # that can have more than one extension like Markdown (.md, .markdown) + # If we don't do this we will override the existing extension. + extension = page.format != format.to_sym ? default_extension : File.extname(page.path).downcase[1..] + + capture_git_error(:updated) do + repository.update_file( + user, + sluggified_full_path(title, extension), + content, + previous_path: page.path, + **multi_commit_options(:updated, message, title)) + + after_wiki_activity + + true + end + end end def delete_page(page, message = nil) @@ -393,12 +426,33 @@ class Wiki yield default_extension end + def file_exists_by_regex?(title) + return false unless repository_exists? + + escaped_title = Regexp.escape(sluggified_title(title)) + regex = Regexp.new("^#{escaped_title}\.#{ALLOWED_EXTENSIONS_REGEX}$", 'i') + + repository.ls_files('HEAD').any? { |s| s =~ regex } + end + + def raise_duplicate_page_error! + raise Gitlab::Git::Wiki::DuplicatePageError, _('A page with that title already exists') + end + def sluggified_full_path(title, extension) sluggified_title(title) + '.' + extension end def sluggified_title(title) - Gitlab::EncodingHelper.encode_utf8_no_detect(title).tr(' ', '-') + utf8_encoded_title = Gitlab::EncodingHelper.encode_utf8_no_detect(title) + + sanitized_title(utf8_encoded_title).tr(' ', '-') + end + + def sanitized_title(title) + clean_absolute_path = File.expand_path(title, '/') + + Pathname.new(clean_absolute_path).relative_path_from('/').to_s end end |