BigW Consortium Gitlab

wiki_page.rb 6.5 KB
Newer Older
1
class WikiPage
2 3
  PageChangedError = Class.new(StandardError)

4 5 6 7 8 9 10 11 12 13 14 15 16
  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

17 18 19 20
  # Sorts and groups pages by directory.
  #
  # pages - an array of WikiPage objects.
  #
21 22 23
  # 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.
24
  def self.group_by_directory(pages)
25 26
    return [] if pages.blank?

27 28 29
    pages.sort_by { |page| [page.directory, page.slug] }
      .group_by(&:directory)
      .map do |dir, pages|
30
        if dir.present?
31
          WikiDirectory.new(dir, pages)
32 33
        else
          pages
34
        end
35 36
      end
      .flatten
37 38
  end

39 40 41 42
  def self.unhyphenize(name)
    name.gsub(/-+/, ' ')
  end

43 44 45 46 47 48 49
  def to_key
    [:slug]
  end

  validates :title, presence: true
  validates :content, presence: true

50
  # The Gitlab ProjectWiki instance.
51 52
  attr_reader :wiki

53
  # The raw Gitlab::Git::WikiPage instance.
54 55 56 57 58 59
  attr_reader :page

  # The attributes Hash used for storing and validating
  # new Page values before writing to the Gollum repository.
  attr_accessor :attributes

60 61 62 63
  def hook_attrs
    attributes
  end

64 65 66 67 68 69 70 71 72 73 74
  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
75 76 77
    if @attributes[:slug].present?
      @attributes[:slug]
    else
78
      wiki.wiki.preview_slug(title, format)
79
    end
80 81
  end

82
  alias_method :to_param, :slug
83 84 85

  # The formatted title of this page.
  def title
86
    if @attributes[:title]
87
      CGI.unescape_html(self.class.unhyphenize(@attributes[:title]))
88 89 90
    else
      ""
    end
91 92 93 94 95 96 97 98 99
  end

  # Sets the title of this page.
  def title=(new_title)
    @attributes[:title] = new_title
  end

  # The raw content of this page.
  def content
100
    @attributes[:content] ||= @page&.text_data
101 102
  end

103 104
  # The hierarchy of the directory this page is contained in.
  def directory
105
    wiki.page_title_and_dir(slug).last
106 107
  end

108 109
  # The processed/formatted content of this page.
  def formatted_content
110
    @attributes[:formatted_content] ||= @page&.formatted_data
111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
  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?

Dmitriy Zaporozhets committed
127
    @version ||= @page.version
128 129
  end

130
  def versions(options = {})
131 132
    return [] unless persisted?

133
    wiki.wiki.page_versions(@page.path, options)
134 135
  end

136 137 138 139 140 141 142 143
  def count_versions
    return [] unless persisted?

    wiki.wiki.count_page_versions(@page.path)
  end

  def last_version
    @last_version ||= versions(limit: 1).first
144 145
  end

146
  def last_commit_sha
147
    last_version&.sha
148 149
  end

150 151 152 153 154 155 156 157 158
  # 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?
159
    @page.historical? && last_version.sha != version.sha
160 161 162
  end

  # Returns boolean True or False if this instance
163 164 165 166 167
  # is the latest commit version of the page.
  def latest?
    !historical?
  end

168
  # Returns boolean True or False if this instance
Dongqing Hu committed
169
  # has been fully created on disk or not.
170 171 172 173 174 175 176 177 178 179 180
  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
181
  #                  listed in the ProjectWiki::MARKUPS
182 183 184 185 186 187
  #                  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.
188 189
  def create(attrs = {})
    @attributes.merge!(attrs)
190

191 192 193
    save(page_details: title) do
      wiki.create_page(title, content, format, message)
    end
194 195 196 197
  end

  # Updates an existing Wiki Page, creating a new version.
  #
198 199 200 201 202 203 204
  # 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
205 206 207
  #
  # Returns the String SHA1 of the newly created page
  # or False if the save was unsuccessful.
208 209
  def update(attrs = {})
    last_commit_sha = attrs.delete(:last_commit_sha)
210
    if last_commit_sha && last_commit_sha != self.last_commit_sha
211 212 213
      raise PageChangedError.new("You are attempting to update a page that has changed since you started editing it.")
    end

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231
    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
232 233
  end

Johannes Schleifenbaum committed
234
  # Destroys the Wiki Page.
235 236 237 238 239 240 241 242 243 244
  #
  # Returns boolean True or False.
  def delete
    if wiki.delete_page(@page)
      true
    else
      false
    end
  end

245 246 247 248 249 250
  # Relative path to the partial to be used when rendering collections
  # of this object.
  def to_partial_path
    'projects/wikis/wiki_page'
  end

251 252 253 254
  def id
    page.version.to_s
  end

255 256 257
  private

  def set_attributes
258
    attributes[:slug] = @page.url_path
259 260 261 262
    attributes[:title] = @page.title
    attributes[:format] = @page.format
  end

263 264
  def save(page_details:)
    return unless valid?
Dongqing Hu committed
265

266 267 268 269
    unless yield
      errors.add(:base, wiki.error_message)
      return false
    end
270

271
    page_title, page_dir = wiki.page_title_and_dir(page_details)
272 273
    gitlab_git_wiki = wiki.wiki
    @page = gitlab_git_wiki.page(title: page_title, dir: page_dir)
274

275 276
    set_attributes
    @persisted = errors.blank?
277 278
  end
end