378ee1dac2
The special characters of a wiki title are now escaped correctly.
273 lines
6.4 KiB
Ruby
273 lines
6.4 KiB
Ruby
class WikiPage
|
|
PageChangedError = Class.new(StandardError)
|
|
|
|
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
|
|
|
|
# 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?
|
|
|
|
pages.sort_by { |page| [page.directory, page.slug] }
|
|
.group_by(&:directory)
|
|
.map do |dir, pages|
|
|
if dir.present?
|
|
WikiDirectory.new(dir, pages)
|
|
else
|
|
pages
|
|
end
|
|
end
|
|
.flatten
|
|
end
|
|
|
|
def self.unhyphenize(name)
|
|
name.gsub(/-+/, ' ')
|
|
end
|
|
|
|
def to_key
|
|
[:slug]
|
|
end
|
|
|
|
validates :title, presence: true
|
|
validates :content, presence: true
|
|
|
|
# The Gitlab ProjectWiki instance.
|
|
attr_reader :wiki
|
|
|
|
# The raw Gollum::Page instance.
|
|
attr_reader :page
|
|
|
|
# The attributes Hash used for storing and validating
|
|
# new Page values before writing to the Gollum repository.
|
|
attr_accessor :attributes
|
|
|
|
def hook_attrs
|
|
attributes
|
|
end
|
|
|
|
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
|
|
if @attributes[:slug].present?
|
|
@attributes[:slug]
|
|
else
|
|
wiki.wiki.preview_page(title, '', format).url_path
|
|
end
|
|
end
|
|
|
|
alias_method :to_param, :slug
|
|
|
|
# The formatted title of this page.
|
|
def title
|
|
if @attributes[:title]
|
|
CGI.unescape_html(self.class.unhyphenize(@attributes[:title]))
|
|
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
|
|
@attributes[:content] ||= @page&.text_data
|
|
end
|
|
|
|
# The hierarchy of the directory this page is contained in.
|
|
def directory
|
|
wiki.page_title_and_dir(slug).last
|
|
end
|
|
|
|
# The processed/formatted content of this page.
|
|
def formatted_content
|
|
@attributes[:formatted_content] ||= @page&.formatted_data
|
|
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?
|
|
|
|
@version ||= @page.version
|
|
end
|
|
|
|
# Returns an array of Gitlab Commit instances.
|
|
def versions
|
|
return [] unless persisted?
|
|
|
|
@page.versions
|
|
end
|
|
|
|
def commit
|
|
versions.first
|
|
end
|
|
|
|
def last_commit_sha
|
|
commit&.sha
|
|
end
|
|
|
|
# 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?
|
|
@page.historical? && versions.first.sha != version.sha
|
|
end
|
|
|
|
# Returns boolean True or False if this instance
|
|
# 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.
|
|
def persisted?
|
|
@persisted == true
|
|
end
|
|
|
|
# Creates a new Wiki Page.
|
|
#
|
|
# attr - Hash of attributes to set on the new page.
|
|
# :title - The title for the new page.
|
|
# :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.
|
|
def create(attrs = {})
|
|
@attributes.merge!(attrs)
|
|
|
|
save(page_details: title) do
|
|
wiki.create_page(title, content, format, message)
|
|
end
|
|
end
|
|
|
|
# Updates an existing Wiki Page, creating a 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 to replace existing title
|
|
#
|
|
# Returns the String SHA1 of the newly created page
|
|
# or False if the save was unsuccessful.
|
|
def update(attrs = {})
|
|
last_commit_sha = attrs.delete(:last_commit_sha)
|
|
if last_commit_sha && last_commit_sha != self.last_commit_sha
|
|
raise PageChangedError.new("You are attempting to update a page that has changed since you started editing it.")
|
|
end
|
|
|
|
attrs.slice!(:content, :format, :message, :title)
|
|
@attributes.merge!(attrs)
|
|
page_details =
|
|
if title.present? && @page.title != title
|
|
title
|
|
else
|
|
@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.
|
|
#
|
|
# Returns boolean True or False.
|
|
def delete
|
|
if wiki.delete_page(@page)
|
|
true
|
|
else
|
|
false
|
|
end
|
|
end
|
|
|
|
# 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
|
|
|
|
private
|
|
|
|
def set_attributes
|
|
attributes[:slug] = @page.url_path
|
|
attributes[:title] = @page.title
|
|
attributes[:format] = @page.format
|
|
end
|
|
|
|
def save(page_details:)
|
|
return unless valid?
|
|
|
|
unless yield
|
|
errors.add(:base, wiki.error_message)
|
|
return false
|
|
end
|
|
|
|
page_title, page_dir = wiki.page_title_and_dir(page_details)
|
|
gollum_wiki = wiki.wiki
|
|
@page = gollum_wiki.paged(page_title, page_dir)
|
|
|
|
set_attributes
|
|
@persisted = errors.blank?
|
|
end
|
|
end
|