debian-mirror-gitlab/app/models/wiki_page.rb

327 lines
7.9 KiB
Ruby
Raw Normal View History

2018-11-18 11:00:15 +05:30
# frozen_string_literal: true
# rubocop:disable Rails/ActiveRecordAliases
2014-09-02 18:07:02 +05:30
class WikiPage
2017-09-10 17:25:29 +05:30
PageChangedError = Class.new(StandardError)
2018-03-17 18:26:18 +05:30
PageRenameError = Class.new(StandardError)
2017-09-10 17:25:29 +05:30
2014-09-02 18:07:02 +05:30
include ActiveModel::Validations
include ActiveModel::Conversion
include StaticModel
extend ActiveModel::Naming
def self.primary_key
'slug'
end
def self.model_name
ActiveModel::Name.new(self, nil, 'wiki')
end
2017-08-17 22:00:37 +05:30
# Sorts and groups pages by directory.
#
# pages - an array of WikiPage objects.
#
# Returns an array of WikiPage and WikiDirectory objects. The entries are
# sorted by alphabetical order (directories and pages inside each directory).
# Pages at the root level come before everything.
def self.group_by_directory(pages)
return [] if pages.blank?
2017-09-10 17:25:29 +05:30
pages.sort_by { |page| [page.directory, page.slug] }
.group_by(&:directory)
.map do |dir, pages|
2017-08-17 22:00:37 +05:30
if dir.present?
WikiDirectory.new(dir, pages)
else
pages
end
2017-09-10 17:25:29 +05:30
end
.flatten
2017-08-17 22:00:37 +05:30
end
def self.unhyphenize(name)
name.gsub(/-+/, ' ')
end
2014-09-02 18:07:02 +05:30
def to_key
[:slug]
end
validates :title, presence: true
validates :content, presence: true
# The Gitlab ProjectWiki instance.
attr_reader :wiki
2018-03-17 18:26:18 +05:30
# The raw Gitlab::Git::WikiPage instance.
2014-09-02 18:07:02 +05:30
attr_reader :page
# The attributes Hash used for storing and validating
# new Page values before writing to the Gollum repository.
attr_accessor :attributes
2016-06-02 11:05:42 +05:30
def hook_attrs
2018-11-18 11:00:15 +05:30
Gitlab::HookData::WikiPageBuilder.new(self).build
2016-06-02 11:05:42 +05:30
end
2014-09-02 18:07:02 +05:30
def initialize(wiki, page = nil, persisted = false)
@wiki = wiki
@page = page
@persisted = persisted
@attributes = {}.with_indifferent_access
set_attributes if persisted?
end
# The escaped URL path of this page.
def slug
2016-08-24 12:49:21 +05:30
if @attributes[:slug].present?
@attributes[:slug]
else
2018-03-17 18:26:18 +05:30
wiki.wiki.preview_slug(title, format)
2016-08-24 12:49:21 +05:30
end
2014-09-02 18:07:02 +05:30
end
2015-04-26 12:48:37 +05:30
alias_method :to_param, :slug
2014-09-02 18:07:02 +05:30
# The formatted title of this page.
def title
if @attributes[:title]
2018-03-17 18:26:18 +05:30
CGI.unescape_html(self.class.unhyphenize(@attributes[:title]))
2014-09-02 18:07:02 +05:30
else
""
end
end
# Sets the title of this page.
def title=(new_title)
@attributes[:title] = new_title
end
# The raw content of this page.
def content
2017-08-17 22:00:37 +05:30
@attributes[:content] ||= @page&.text_data
end
# The hierarchy of the directory this page is contained in.
def directory
2018-03-17 18:26:18 +05:30
wiki.page_title_and_dir(slug)&.last.to_s
2014-09-02 18:07:02 +05:30
end
# The processed/formatted content of this page.
def formatted_content
2018-03-17 18:26:18 +05:30
# 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)
2014-09-02 18:07:02 +05:30
end
# The markup format for the page.
def format
@attributes[:format] || :markdown
end
# The commit message for this page version.
def message
version.try(:message)
end
# The Gitlab Commit instance for this page.
def version
return nil unless persisted?
2015-04-26 12:48:37 +05:30
@version ||= @page.version
2014-09-02 18:07:02 +05:30
end
2018-03-17 18:26:18 +05:30
def versions(options = {})
2014-09-02 18:07:02 +05:30
return [] unless persisted?
2018-03-17 18:26:18 +05:30
wiki.wiki.page_versions(@page.path, options)
2014-09-02 18:07:02 +05:30
end
2018-03-17 18:26:18 +05:30
def count_versions
return [] unless persisted?
wiki.wiki.count_page_versions(@page.path)
end
def last_version
@last_version ||= versions(limit: 1).first
2014-09-02 18:07:02 +05:30
end
2017-09-10 17:25:29 +05:30
def last_commit_sha
2018-03-17 18:26:18 +05:30
last_version&.sha
2017-09-10 17:25:29 +05:30
end
2014-09-02 18:07:02 +05:30
# Returns the Date that this latest version was
# created on.
def created_at
@page.version.date
end
# Returns boolean True or False if this instance
# is an old version of the page.
def historical?
2018-03-17 18:26:18 +05:30
@page.historical? && last_version.sha != version.sha
2014-09-02 18:07:02 +05:30
end
# Returns boolean True or False if this instance
2017-08-17 22:00:37 +05:30
# is the latest commit version of the page.
def latest?
!historical?
end
# Returns boolean True or False if this instance
# has been fully created on disk or not.
2014-09-02 18:07:02 +05:30
def persisted?
@persisted == true
end
# Creates a new Wiki Page.
#
# attr - Hash of attributes to set on the new page.
2018-03-17 18:26:18 +05:30
# :title - The title (optionally including dir) for the new page.
2014-09-02 18:07:02 +05:30
# :content - The raw markup content.
# :format - Optional symbol representing the
# content format. Can be any type
# listed in the ProjectWiki::MARKUPS
# Hash.
# :message - Optional commit message to set on
# the new page.
#
# Returns the String SHA1 of the newly created page
# or False if the save was unsuccessful.
2017-09-10 17:25:29 +05:30
def create(attrs = {})
2018-03-17 18:26:18 +05:30
update_attributes(attrs)
2014-09-02 18:07:02 +05:30
2017-09-10 17:25:29 +05:30
save(page_details: title) do
wiki.create_page(title, content, format, message)
end
2014-09-02 18:07:02 +05:30
end
# Updates an existing Wiki Page, creating a new version.
#
2017-09-10 17:25:29 +05:30
# 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.
2018-03-17 18:26:18 +05:30
# :title - The Title (optionally including dir) to replace existing title
2014-09-02 18:07:02 +05:30
#
# Returns the String SHA1 of the newly created page
# or False if the save was unsuccessful.
2017-09-10 17:25:29 +05:30
def update(attrs = {})
last_commit_sha = attrs.delete(:last_commit_sha)
2018-03-17 18:26:18 +05:30
2017-09-10 17:25:29 +05:30
if last_commit_sha && last_commit_sha != self.last_commit_sha
2018-03-17 18:26:18 +05:30
raise PageChangedError
2017-09-10 17:25:29 +05:30
end
2014-09-02 18:07:02 +05:30
2018-03-17 18:26:18 +05:30
update_attributes(attrs)
if title_changed?
page_details = title
if wiki.find_page(page_details).present?
@attributes[:title] = @page.url_path
raise PageRenameError
2017-09-10 17:25:29 +05:30
end
2018-03-17 18:26:18 +05:30
else
page_details = @page.url_path
end
2017-09-10 17:25:29 +05:30
save(page_details: page_details) do
wiki.update_page(
@page,
content: content,
format: format,
message: attrs[:message],
title: title
)
end
2014-09-02 18:07:02 +05:30
end
# Destroys the Wiki Page.
#
# Returns boolean True or False.
def delete
if wiki.delete_page(@page)
true
else
false
end
end
2017-08-17 22:00:37 +05:30
# Relative path to the partial to be used when rendering collections
# of this object.
def to_partial_path
'projects/wikis/wiki_page'
end
def id
page.version.to_s
end
2018-03-17 18:26:18 +05:30
def title_changed?
title.present? && self.class.unhyphenize(@page.url_path) != title
end
2018-10-15 14:42:47 +05:30
# 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
2014-09-02 18:07:02 +05:30
private
2018-03-17 18:26:18 +05:30
# 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
2014-09-02 18:07:02 +05:30
def set_attributes
attributes[:slug] = @page.url_path
2014-09-02 18:07:02 +05:30
attributes[:title] = @page.title
attributes[:format] = @page.format
end
2017-09-10 17:25:29 +05:30
def save(page_details:)
return unless valid?
2017-08-17 22:00:37 +05:30
2017-09-10 17:25:29 +05:30
unless yield
errors.add(:base, wiki.error_message)
return false
end
2014-09-02 18:07:02 +05:30
2017-09-10 17:25:29 +05:30
page_title, page_dir = wiki.page_title_and_dir(page_details)
2018-03-17 18:26:18 +05:30
gitlab_git_wiki = wiki.wiki
@page = gitlab_git_wiki.page(title: page_title, dir: page_dir)
2014-09-02 18:07:02 +05:30
2017-09-10 17:25:29 +05:30
set_attributes
@persisted = errors.blank?
2014-09-02 18:07:02 +05:30
end
end