diff options
Diffstat (limited to 'app/models/wiki_page.rb')
-rw-r--r-- | app/models/wiki_page.rb | 159 |
1 files changed, 114 insertions, 45 deletions
diff --git a/app/models/wiki_page.rb b/app/models/wiki_page.rb index 224eb3cd4d0..0f5536415f7 100644 --- a/app/models/wiki_page.rb +++ b/app/models/wiki_page.rb @@ -1,4 +1,7 @@ class WikiPage + PageChangedError = Class.new(StandardError) + PageRenameError = Class.new(StandardError) + include ActiveModel::Validations include ActiveModel::Conversion include StaticModel @@ -48,7 +51,7 @@ class WikiPage # The Gitlab ProjectWiki instance. attr_reader :wiki - # The raw Gollum::Page instance. + # The raw Gitlab::Git::WikiPage instance. attr_reader :page # The attributes Hash used for storing and validating @@ -73,7 +76,7 @@ class WikiPage if @attributes[:slug].present? @attributes[:slug] else - wiki.wiki.preview_page(title, '', format).url_path + wiki.wiki.preview_slug(title, format) end end @@ -82,7 +85,7 @@ class WikiPage # The formatted title of this page. def title if @attributes[:title] - self.class.unhyphenize(@attributes[:title]) + CGI.unescape_html(self.class.unhyphenize(@attributes[:title])) else "" end @@ -100,12 +103,15 @@ class WikiPage # The hierarchy of the directory this page is contained in. def directory - wiki.page_title_and_dir(slug).last + wiki.page_title_and_dir(slug)&.last.to_s end # The processed/formatted content of this page. def formatted_content - @attributes[:formatted_content] ||= @page&.formatted_data + # Assuming @page exists, nil formatted_data means we didn't load it + # before hand (i.e. page was fetched by Gitaly), so we fetch it separately. + # If the page was fetched by Gollum, formatted_data would've been a String. + @attributes[:formatted_content] ||= @page&.formatted_data || @wiki.page_formatted_data(@page) end # The markup format for the page. @@ -125,15 +131,24 @@ class WikiPage @version ||= @page.version end - # Returns an array of Gitlab Commit instances. - def versions + def versions(options = {}) + return [] unless persisted? + + wiki.wiki.page_versions(@page.path, options) + end + + def count_versions return [] unless persisted? - @page.versions + wiki.wiki.count_page_versions(@page.path) end - def commit - versions.first + def last_version + @last_version ||= versions(limit: 1).first + end + + def last_commit_sha + last_version&.sha end # Returns the Date that this latest version was @@ -145,7 +160,7 @@ class WikiPage # Returns boolean True or False if this instance # is an old version of the page. def historical? - @page.historical? && versions.first.sha != version.sha + @page.historical? && last_version.sha != version.sha end # Returns boolean True or False if this instance @@ -163,7 +178,7 @@ class WikiPage # Creates a new Wiki Page. # # attr - Hash of attributes to set on the new page. - # :title - The title for the new page. + # :title - The title (optionally including dir) for the new page. # :content - The raw markup content. # :format - Optional symbol representing the # content format. Can be any type @@ -174,26 +189,55 @@ class WikiPage # # Returns the String SHA1 of the newly created page # or False if the save was unsuccessful. - def create(attr = {}) - @attributes.merge!(attr) + def create(attrs = {}) + update_attributes(attrs) - save :create_page, title, content, format, message + save(page_details: title) do + wiki.create_page(title, content, format, message) + end end # Updates an existing Wiki Page, creating a new version. # - # new_content - The raw markup content to replace the existing. - # format - Optional symbol representing the content format. - # See ProjectWiki::MARKUPS Hash for available formats. - # message - Optional commit message to set on the new version. + # attrs - Hash of attributes to be updated on the page. + # :content - The raw markup content to replace the existing. + # :format - Optional symbol representing the content format. + # See ProjectWiki::MARKUPS Hash for available formats. + # :message - Optional commit message to set on the new version. + # :last_commit_sha - Optional last commit sha to validate the page unchanged. + # :title - The Title (optionally including dir) to replace existing title # # Returns the String SHA1 of the newly created page # or False if the save was unsuccessful. - def update(new_content = "", format = :markdown, message = nil) - @attributes[:content] = new_content - @attributes[:format] = format + def update(attrs = {}) + last_commit_sha = attrs.delete(:last_commit_sha) + + if last_commit_sha && last_commit_sha != self.last_commit_sha + raise PageChangedError + end - save :update_page, @page, content, format, message + update_attributes(attrs) + + if title_changed? + page_details = title + + if wiki.find_page(page_details).present? + @attributes[:title] = @page.url_path + raise PageRenameError + end + else + page_details = @page.url_path + end + + save(page_details: page_details) do + wiki.update_page( + @page, + content: content, + format: format, + message: attrs[:message], + title: title + ) + end end # Destroys the Wiki Page. @@ -217,38 +261,63 @@ class WikiPage page.version.to_s end + def title_changed? + title.present? && self.class.unhyphenize(@page.url_path) != title + end + private + # Process and format the title based on the user input. + def process_title(title) + return if title.blank? + + title = deep_title_squish(title) + current_dirname = File.dirname(title) + + if @page.present? + return title[1..-1] if current_dirname == '/' + return File.join([directory.presence, title].compact) if current_dirname == '.' + end + + title + end + + # This method squishes all the filename + # i.e: ' foo / bar / page_name' => 'foo/bar/page_name' + def deep_title_squish(title) + components = title.split(File::SEPARATOR).map(&:squish) + + File.join(components) + end + + # Updates the current @attributes hash by merging a hash of params + def update_attributes(attrs) + attrs[:title] = process_title(attrs[:title]) if attrs[:title].present? + + attrs.slice!(:content, :format, :message, :title) + + @attributes.merge!(attrs) + end + def set_attributes attributes[:slug] = @page.url_path attributes[:title] = @page.title attributes[:format] = @page.format end - def save(method, *args) - saved = false - - project_wiki = wiki - if valid? && project_wiki.send(method, *args) + def save(page_details:) + return unless valid? - page_details = if method == :update_page - # Use url_path instead of path to omit format extension - @page.url_path - else - title - end - - page_title, page_dir = project_wiki.page_title_and_dir(page_details) - gollum_wiki = project_wiki.wiki - @page = gollum_wiki.paged(page_title, page_dir) + unless yield + errors.add(:base, wiki.error_message) + return false + end - set_attributes + page_title, page_dir = wiki.page_title_and_dir(page_details) + gitlab_git_wiki = wiki.wiki + @page = gitlab_git_wiki.page(title: page_title, dir: page_dir) - @persisted = true - saved = true - else - errors.add(:base, project_wiki.error_message) if project_wiki.error_message - end - saved + set_attributes + @persisted = errors.blank? end end |