2020-06-04 00:08:17 +00:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
|
|
|
module WikiActions
|
2020-07-02 15:09:08 +00:00
|
|
|
include DiffHelper
|
2020-07-12 09:09:08 +00:00
|
|
|
include PreviewMarkdown
|
2020-06-04 00:08:17 +00:00
|
|
|
include SendsBlob
|
|
|
|
include Gitlab::Utils::StrongMemoize
|
2020-10-15 00:08:42 +00:00
|
|
|
include RedisTracking
|
2020-06-04 00:08:17 +00:00
|
|
|
extend ActiveSupport::Concern
|
|
|
|
|
2020-11-19 15:09:13 +00:00
|
|
|
RESCUE_GIT_TIMEOUTS_IN = %w[show edit history diff pages].freeze
|
|
|
|
|
2020-06-04 00:08:17 +00:00
|
|
|
included do
|
2020-07-28 21:09:30 +00:00
|
|
|
before_action { respond_to :html }
|
|
|
|
|
2020-06-04 00:08:17 +00:00
|
|
|
before_action :authorize_read_wiki!
|
2021-07-05 15:07:38 +00:00
|
|
|
before_action :authorize_create_wiki!, only: [:edit, :create, :destroy]
|
2020-06-04 00:08:17 +00:00
|
|
|
|
|
|
|
before_action :wiki
|
2020-07-02 15:09:08 +00:00
|
|
|
before_action :page, only: [:show, :edit, :update, :history, :destroy, :diff]
|
2020-06-04 00:08:17 +00:00
|
|
|
before_action :load_sidebar, except: [:pages]
|
2020-07-02 15:09:08 +00:00
|
|
|
before_action :set_content_class
|
2020-06-04 00:08:17 +00:00
|
|
|
|
|
|
|
before_action only: [:show, :edit, :update] do
|
|
|
|
@valid_encoding = valid_encoding?
|
|
|
|
end
|
|
|
|
|
|
|
|
before_action only: [:edit, :update], unless: :valid_encoding? do
|
2020-06-26 12:08:51 +00:00
|
|
|
if params[:id].present?
|
|
|
|
redirect_to wiki_page_path(wiki, page || params[:id])
|
|
|
|
else
|
|
|
|
redirect_to wiki_path(wiki)
|
|
|
|
end
|
2020-06-04 00:08:17 +00:00
|
|
|
end
|
2020-07-02 15:09:08 +00:00
|
|
|
|
2020-10-15 00:08:42 +00:00
|
|
|
# NOTE: We want to include wiki page views in the same counter as the other
|
|
|
|
# Event-based wiki actions tracked through TrackUniqueEvents, so we use the same event name.
|
2021-02-11 15:09:11 +00:00
|
|
|
track_redis_hll_event :show, name: Gitlab::UsageDataCounters::TrackUniqueEvents::WIKI_ACTION.to_s
|
2020-10-15 00:08:42 +00:00
|
|
|
|
2020-07-02 15:09:08 +00:00
|
|
|
helper_method :view_file_button, :diff_file_html_data
|
2020-11-19 15:09:13 +00:00
|
|
|
|
|
|
|
rescue_from ::Gitlab::Git::CommandTimedOut do |exc|
|
|
|
|
raise exc unless RESCUE_GIT_TIMEOUTS_IN.include?(action_name)
|
|
|
|
|
|
|
|
render 'shared/wikis/git_error'
|
|
|
|
end
|
2020-06-04 00:08:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
def new
|
|
|
|
redirect_to wiki_page_path(wiki, SecureRandom.uuid, random_title: true)
|
|
|
|
end
|
|
|
|
|
|
|
|
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
|
|
|
def pages
|
2020-11-19 15:09:13 +00:00
|
|
|
@wiki_entries = WikiDirectory.group_pages(wiki_pages)
|
2020-06-11 15:08:36 +00:00
|
|
|
|
|
|
|
render 'shared/wikis/pages'
|
2020-06-04 00:08:17 +00:00
|
|
|
end
|
|
|
|
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
|
|
|
|
|
|
|
# `#show` handles a number of scenarios:
|
|
|
|
#
|
|
|
|
# - If `id` matches a WikiPage, then show the wiki page.
|
|
|
|
# - If `id` is a file in the wiki repository, then send the file.
|
|
|
|
# - If we know the user wants to create a new page with the given `id`,
|
|
|
|
# then display a create form.
|
|
|
|
# - Otherwise show the empty wiki page and invite the user to create a page.
|
|
|
|
#
|
|
|
|
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
|
|
|
def show
|
|
|
|
if page
|
|
|
|
set_encoding_error unless valid_encoding?
|
|
|
|
|
|
|
|
# Assign vars expected by MarkupHelper
|
|
|
|
@ref = params[:version_id]
|
|
|
|
@path = page.path
|
|
|
|
|
2020-08-07 18:09:53 +00:00
|
|
|
Gitlab::UsageDataCounters::WikiPageCounter.count(:view)
|
|
|
|
|
2020-06-11 15:08:36 +00:00
|
|
|
render 'shared/wikis/show'
|
2020-06-04 00:08:17 +00:00
|
|
|
elsif file_blob
|
2021-11-19 09:13:48 +00:00
|
|
|
# This is needed by [GitLab JH](https://gitlab.com/gitlab-jh/gitlab/-/issues/247)
|
|
|
|
send_wiki_file_blob(wiki, file_blob)
|
2020-06-04 00:08:17 +00:00
|
|
|
elsif show_create_form?
|
|
|
|
# Assign a title to the WikiPage unless `id` is a randomly generated slug from #new
|
|
|
|
title = params[:id] unless params[:random_title].present?
|
|
|
|
|
|
|
|
@page = build_page(title: title)
|
|
|
|
|
2020-06-11 15:08:36 +00:00
|
|
|
render 'shared/wikis/edit'
|
2020-06-04 00:08:17 +00:00
|
|
|
else
|
2020-06-11 15:08:36 +00:00
|
|
|
render 'shared/wikis/empty'
|
2020-06-04 00:08:17 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
|
|
|
|
|
|
|
def edit
|
2020-06-11 15:08:36 +00:00
|
|
|
render 'shared/wikis/edit'
|
2020-06-04 00:08:17 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
|
|
|
def update
|
2020-06-11 18:08:28 +00:00
|
|
|
return render('shared/wikis/empty') unless can?(current_user, :create_wiki, container)
|
2020-06-04 00:08:17 +00:00
|
|
|
|
2020-08-27 15:10:21 +00:00
|
|
|
response = WikiPages::UpdateService.new(container: container, current_user: current_user, params: wiki_params).execute(page)
|
|
|
|
@page = response.payload[:page]
|
2020-06-04 00:08:17 +00:00
|
|
|
|
2020-08-27 15:10:21 +00:00
|
|
|
if response.success?
|
2020-11-05 12:09:05 +00:00
|
|
|
flash[:toast] = _('Wiki page was successfully updated.')
|
|
|
|
|
2020-06-04 00:08:17 +00:00
|
|
|
redirect_to(
|
2020-11-05 12:09:05 +00:00
|
|
|
wiki_page_path(wiki, page)
|
2020-06-04 00:08:17 +00:00
|
|
|
)
|
|
|
|
else
|
2021-02-23 21:10:44 +00:00
|
|
|
@error = response.message
|
2020-06-11 15:08:36 +00:00
|
|
|
render 'shared/wikis/edit'
|
2020-06-04 00:08:17 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
|
|
|
|
|
|
|
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
|
|
|
def create
|
2020-08-17 18:10:01 +00:00
|
|
|
response = WikiPages::CreateService.new(container: container, current_user: current_user, params: wiki_params).execute
|
|
|
|
@page = response.payload[:page]
|
2020-06-04 00:08:17 +00:00
|
|
|
|
2020-08-17 18:10:01 +00:00
|
|
|
if response.success?
|
2020-11-05 12:09:05 +00:00
|
|
|
flash[:toast] = _('Wiki page was successfully created.')
|
|
|
|
|
2020-06-04 00:08:17 +00:00
|
|
|
redirect_to(
|
2020-11-05 12:09:05 +00:00
|
|
|
wiki_page_path(wiki, page)
|
2020-06-04 00:08:17 +00:00
|
|
|
)
|
|
|
|
else
|
2020-06-11 15:08:36 +00:00
|
|
|
render 'shared/wikis/edit'
|
2020-06-04 00:08:17 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
|
|
|
|
|
|
|
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
|
|
|
def history
|
|
|
|
if page
|
2021-05-14 12:10:58 +00:00
|
|
|
@commits = Kaminari.paginate_array(page.versions(page: params[:page].to_i),
|
|
|
|
total_count: page.count_versions)
|
2020-06-04 00:08:17 +00:00
|
|
|
.page(params[:page])
|
2020-06-11 15:08:36 +00:00
|
|
|
|
|
|
|
render 'shared/wikis/history'
|
2020-06-04 00:08:17 +00:00
|
|
|
else
|
|
|
|
redirect_to(
|
2020-06-11 15:08:36 +00:00
|
|
|
wiki_path(wiki),
|
2020-06-04 00:08:17 +00:00
|
|
|
notice: _("Page not found")
|
|
|
|
)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
|
|
|
|
2020-07-02 15:09:08 +00:00
|
|
|
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
|
|
|
def diff
|
|
|
|
return render_404 unless page
|
|
|
|
|
|
|
|
apply_diff_view_cookie!
|
|
|
|
|
|
|
|
@diffs = page.diffs(diff_options)
|
|
|
|
@diff_notes_disabled = true
|
|
|
|
|
|
|
|
render 'shared/wikis/diff'
|
|
|
|
end
|
|
|
|
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
|
|
|
|
2020-06-04 00:08:17 +00:00
|
|
|
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
|
|
|
def destroy
|
2020-08-21 03:10:16 +00:00
|
|
|
return render_404 unless page
|
2020-06-04 00:08:17 +00:00
|
|
|
|
2020-08-21 03:10:16 +00:00
|
|
|
response = WikiPages::DestroyService.new(container: container, current_user: current_user).execute(page)
|
|
|
|
|
|
|
|
if response.success?
|
2020-11-05 12:09:05 +00:00
|
|
|
flash[:toast] = _("Wiki page was successfully deleted.")
|
|
|
|
|
2020-08-21 03:10:16 +00:00
|
|
|
redirect_to wiki_path(wiki),
|
2020-11-05 12:09:05 +00:00
|
|
|
status: :found
|
2020-08-21 03:10:16 +00:00
|
|
|
else
|
2021-05-25 18:10:42 +00:00
|
|
|
@error = response.message
|
2020-08-21 03:10:16 +00:00
|
|
|
render 'shared/wikis/edit'
|
|
|
|
end
|
2020-06-04 00:08:17 +00:00
|
|
|
end
|
|
|
|
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
|
|
|
|
2020-12-15 15:09:59 +00:00
|
|
|
def git_access
|
|
|
|
render 'shared/wikis/git_access'
|
|
|
|
end
|
|
|
|
|
2020-06-04 00:08:17 +00:00
|
|
|
private
|
|
|
|
|
|
|
|
def container
|
|
|
|
raise NotImplementedError
|
|
|
|
end
|
|
|
|
|
|
|
|
def show_create_form?
|
|
|
|
can?(current_user, :create_wiki, container) &&
|
|
|
|
page.nil? &&
|
|
|
|
# Always show the create form when the wiki has had at least one page created.
|
|
|
|
# Otherwise, we only show the form when the user has navigated from
|
|
|
|
# the 'empty wiki' page
|
|
|
|
(wiki.exists? || params[:view] == 'create')
|
|
|
|
end
|
|
|
|
|
|
|
|
def wiki
|
|
|
|
strong_memoize(:wiki) do
|
|
|
|
wiki = Wiki.for_container(container, current_user)
|
|
|
|
|
|
|
|
# Call #wiki to make sure the Wiki Repo is initialized
|
|
|
|
wiki.wiki
|
|
|
|
|
|
|
|
wiki
|
|
|
|
end
|
|
|
|
rescue Wiki::CouldNotCreateWikiError
|
|
|
|
flash[:notice] = _("Could not create Wiki Repository at this time. Please try again later.")
|
|
|
|
redirect_to container
|
|
|
|
false
|
|
|
|
end
|
|
|
|
|
|
|
|
def page
|
|
|
|
strong_memoize(:page) do
|
2022-03-24 06:08:31 +00:00
|
|
|
wiki.find_page(*page_params, load_content: load_content?)
|
2020-06-04 00:08:17 +00:00
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# rubocop:disable Gitlab/ModuleWithInstanceVariables
|
|
|
|
def load_sidebar
|
|
|
|
@sidebar_page = wiki.find_sidebar(params[:version_id])
|
|
|
|
|
|
|
|
unless @sidebar_page # Fallback to default sidebar
|
|
|
|
@sidebar_wiki_entries, @sidebar_limited = wiki.sidebar_entries
|
|
|
|
end
|
2020-11-19 15:09:13 +00:00
|
|
|
rescue ::Gitlab::Git::CommandTimedOut => e
|
|
|
|
@sidebar_error = e
|
2020-06-04 00:08:17 +00:00
|
|
|
end
|
|
|
|
# rubocop:enable Gitlab/ModuleWithInstanceVariables
|
|
|
|
|
2020-11-19 15:09:13 +00:00
|
|
|
def wiki_pages
|
|
|
|
strong_memoize(:wiki_pages) do
|
|
|
|
Kaminari.paginate_array(
|
|
|
|
wiki.list_pages(sort: params[:sort], direction: params[:direction])
|
|
|
|
).page(params[:page])
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
2020-06-04 00:08:17 +00:00
|
|
|
def wiki_params
|
|
|
|
params.require(:wiki).permit(:title, :content, :format, :message, :last_commit_sha)
|
|
|
|
end
|
|
|
|
|
|
|
|
def build_page(args = {})
|
|
|
|
WikiPage.new(wiki).tap do |page|
|
|
|
|
page.update_attributes(args) # rubocop:disable Rails/ActiveRecordAliases
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
def page_params
|
|
|
|
keys = [:id]
|
2020-07-02 15:09:08 +00:00
|
|
|
keys << :version_id if %w[show diff].include?(params[:action])
|
2020-06-04 00:08:17 +00:00
|
|
|
|
|
|
|
params.values_at(*keys)
|
|
|
|
end
|
|
|
|
|
|
|
|
def valid_encoding?
|
|
|
|
page_encoding == Encoding::UTF_8
|
|
|
|
end
|
|
|
|
|
|
|
|
def page_encoding
|
|
|
|
strong_memoize(:page_encoding) { page&.content&.encoding }
|
|
|
|
end
|
|
|
|
|
|
|
|
def set_encoding_error
|
|
|
|
flash.now[:notice] = _("The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.")
|
|
|
|
end
|
|
|
|
|
|
|
|
def file_blob
|
|
|
|
strong_memoize(:file_blob) do
|
|
|
|
commit = wiki.repository.commit(wiki.default_branch)
|
|
|
|
|
|
|
|
next unless commit
|
|
|
|
|
|
|
|
wiki.repository.blob_at(commit.id, params[:id])
|
|
|
|
end
|
|
|
|
end
|
2020-07-02 15:09:08 +00:00
|
|
|
|
|
|
|
def set_content_class
|
|
|
|
@content_class = 'limit-container-width' unless fluid_layout # rubocop:disable Gitlab/ModuleWithInstanceVariables
|
|
|
|
end
|
|
|
|
|
|
|
|
# Override CommitsHelper#view_file_button
|
|
|
|
def view_file_button(commit_sha, *args)
|
|
|
|
path = wiki_page_path(wiki, page, version_id: page.version.id)
|
|
|
|
|
|
|
|
helpers.link_to(path, class: 'btn') do
|
|
|
|
helpers.raw(_('View page @ ')) + helpers.content_tag(:span, Commit.truncate_sha(commit_sha), class: 'commit-sha')
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
# Override DiffHelper#diff_file_html_data
|
|
|
|
def diff_file_html_data(_project, _diff_file_path, diff_commit_id)
|
|
|
|
{
|
|
|
|
blob_diff_path: wiki_page_path(wiki, page, action: :diff, version_id: diff_commit_id),
|
|
|
|
view: diff_view
|
|
|
|
}
|
|
|
|
end
|
2021-11-19 09:13:48 +00:00
|
|
|
|
|
|
|
def send_wiki_file_blob(wiki, file_blob)
|
|
|
|
send_blob(wiki.repository, file_blob)
|
|
|
|
end
|
2022-03-24 06:08:31 +00:00
|
|
|
|
|
|
|
def load_content?
|
2022-04-12 15:08:32 +00:00
|
|
|
return false if %w[history destroy diff show].include?(params[:action])
|
2022-03-24 06:08:31 +00:00
|
|
|
|
|
|
|
true
|
|
|
|
end
|
2020-06-04 00:08:17 +00:00
|
|
|
end
|
2021-11-19 09:13:48 +00:00
|
|
|
|
|
|
|
WikiActions.prepend_mod
|