Commit 8f210aeb authored by GitLab Bot's avatar GitLab Bot

Add latest changes from gitlab-org/gitlab@master

parent 996f7009
...@@ -239,6 +239,7 @@ static-analysis: ...@@ -239,6 +239,7 @@ static-analysis:
dependencies: ["setup-test-env", "compile-assets pull-cache"] dependencies: ["setup-test-env", "compile-assets pull-cache"]
variables: variables:
SETUP_DB: "false" SETUP_DB: "false"
parallel: 2
script: script:
- scripts/static-analysis - scripts/static-analysis
cache: cache:
......
...@@ -343,6 +343,8 @@ linters: ...@@ -343,6 +343,8 @@ linters:
- 'app/views/projects/triggers/_index.html.haml' - 'app/views/projects/triggers/_index.html.haml'
- 'app/views/projects/triggers/_trigger.html.haml' - 'app/views/projects/triggers/_trigger.html.haml'
- 'app/views/projects/triggers/edit.html.haml' - 'app/views/projects/triggers/edit.html.haml'
- 'app/views/projects/wikis/_new.html.haml'
- 'app/views/projects/wikis/_pages_wiki_page.html.haml'
- 'app/views/projects/wikis/edit.html.haml' - 'app/views/projects/wikis/edit.html.haml'
- 'app/views/projects/wikis/history.html.haml' - 'app/views/projects/wikis/history.html.haml'
- 'app/views/repository_check_mailer/notify.html.haml' - 'app/views/repository_check_mailer/notify.html.haml'
......
...@@ -16,10 +16,25 @@ ...@@ -16,10 +16,25 @@
# Uncomment the following lines to make the configuration take effect. # Uncomment the following lines to make the configuration take effect.
PreCommit: PreCommit:
AuthorName:
enabled: false
EsLint:
enabled: true
# https://github.com/sds/overcommit/issues/338
command: './node_modules/eslint/bin/eslint.js'
HamlLint:
enabled: true
MergeConflicts:
enabled: true
exclude:
- '**/conflict/file_spec.rb'
- '**/git/conflict/parser_spec.rb'
# prettier? https://github.com/sds/overcommit/issues/614 https://github.com/sds/overcommit/issues/390#issuecomment-495703284
RuboCop: RuboCop:
enabled: true enabled: true
# on_warn: fail # Treat all warnings as failures # on_warn: fail # Treat all warnings as failures
# ScssLint:
enabled: true
#PostCheckout: #PostCheckout:
# ALL: # Special hook name that customizes all hooks of this type # ALL: # Special hook name that customizes all hooks of this type
# quiet: true # Change all post-checkout hooks to only display output on failure # quiet: true # Change all post-checkout hooks to only display output on failure
......
// currently, this controller inherits all behaviors from wikis
import '../wikis/index';
import $ from 'jquery';
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import csrf from '~/lib/utils/csrf';
import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki';
import ZenMode from '~/zen_mode';
import GLForm from '~/gl_form';
import deleteWikiModal from '../wikis/components/delete_wiki_modal.vue';
import Wikis from '../wikis/wikis';
document.addEventListener('DOMContentLoaded', () => {
new Wikis(); // eslint-disable-line no-new
new ShortcutsWiki(); // eslint-disable-line no-new
new ZenMode(); // eslint-disable-line no-new
new GLForm($('.wiki-form')); // eslint-disable-line no-new
const deleteWikiModalWrapperEl = document.getElementById('delete-wiki-modal-wrapper');
if (deleteWikiModalWrapperEl) {
Vue.use(Translate);
const { deleteWikiUrl, pageTitle } = deleteWikiModalWrapperEl.dataset;
// eslint-disable-next-line no-new
new Vue({
el: deleteWikiModalWrapperEl,
data: {
deleteWikiUrl: '',
},
render(createElement) {
return createElement(deleteWikiModal, {
props: {
pageTitle,
deleteWikiUrl,
csrfToken: csrf.token,
},
});
},
});
}
});
import $ from 'jquery'; import $ from 'jquery';
import Vue from 'vue';
import Translate from '~/vue_shared/translate';
import csrf from '~/lib/utils/csrf';
import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki'; import ShortcutsWiki from '~/behaviors/shortcuts/shortcuts_wiki';
import GLForm from '~/gl_form';
import Wikis from './wikis'; import Wikis from './wikis';
import ZenMode from '../../../zen_mode';
import GLForm from '../../../gl_form';
import deleteWikiModal from './components/delete_wiki_modal.vue';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
new Wikis(); // eslint-disable-line no-new new Wikis(); // eslint-disable-line no-new
new ShortcutsWiki(); // eslint-disable-line no-new new ShortcutsWiki(); // eslint-disable-line no-new
new ZenMode(); // eslint-disable-line no-new
new GLForm($('.wiki-form')); // eslint-disable-line no-new new GLForm($('.wiki-form')); // eslint-disable-line no-new
const deleteWikiModalWrapperEl = document.getElementById('delete-wiki-modal-wrapper');
if (deleteWikiModalWrapperEl) {
Vue.use(Translate);
const { deleteWikiUrl, pageTitle } = deleteWikiModalWrapperEl.dataset;
// eslint-disable-next-line no-new
new Vue({
el: deleteWikiModalWrapperEl,
data: {
deleteWikiUrl: '',
},
render(createElement) {
return createElement(deleteWikiModal, {
props: {
pageTitle,
deleteWikiUrl,
csrfToken: csrf.token,
},
});
},
});
}
}); });
...@@ -12,8 +12,8 @@ export default class Wikis { ...@@ -12,8 +12,8 @@ export default class Wikis {
} }
this.isNewWikiPage = Boolean(document.querySelector('.js-new-wiki-page')); this.isNewWikiPage = Boolean(document.querySelector('.js-new-wiki-page'));
this.editTitleInput = document.querySelector('form.wiki-form #wiki_page_title'); this.editTitleInput = document.querySelector('form.wiki-form #wiki_title');
this.commitMessageInput = document.querySelector('form.wiki-form #wiki_page_message'); this.commitMessageInput = document.querySelector('form.wiki-form #wiki_message');
this.commitMessageI18n = this.isNewWikiPage this.commitMessageI18n = this.isNewWikiPage
? s__('WikiPageCreate|Create %{pageTitle}') ? s__('WikiPageCreate|Create %{pageTitle}')
: s__('WikiPageEdit|Update %{pageTitle}'); : s__('WikiPageEdit|Update %{pageTitle}');
......
...@@ -99,18 +99,3 @@ ...@@ -99,18 +99,3 @@
justify-content: center; justify-content: center;
color: $gray-700; color: $gray-700;
} }
.svg-icon {
display: inline-flex;
align-self: center;
svg {
height: 1em;
width: 1em;
}
&.svg-baseline svg {
top: 0.125em;
position: relative;
}
}
.new-wiki-page {
.new-wiki-page-slug-tip {
display: inline-block;
max-width: 100%;
margin-top: 5px;
}
}
.wiki-history,
.wiki-page,
.edit-wiki-page {
margin-right: $gutter-width;
}
.edit-wiki-page {
@media only screen and (min-width: map-get($grid-breakpoints, lg) + (2 * $gutter-width)) and (max-width: map-get($grid-breakpoints, lg) + (3 * $gutter-width)) {
margin-right: $gutter-width * 1.5;
}
}
.wiki-form {
.edit-wiki-page-slug-tip {
display: inline-block;
max-width: 100%;
margin-top: 5px;
}
}
.title .edit-wiki-header { .title .edit-wiki-header {
width: 780px; width: 780px;
margin-left: auto; margin-left: auto;
...@@ -33,15 +5,6 @@ ...@@ -33,15 +5,6 @@
padding-right: 7px; padding-right: 7px;
} }
.container-fluid.wiki-page,
.container-fluid.edit-wiki-page {
width: initial;
}
.wiki-history.breadcrumbs {
min-height: (2 * $gl-padding) + 22;
}
.wiki-page-header { .wiki-page-header {
position: relative; position: relative;
...@@ -116,7 +79,7 @@ ...@@ -116,7 +79,7 @@
} }
.sidebar-container { .sidebar-container {
padding-bottom: $gl-padding; padding: $gl-padding 0;
width: calc(100% + 100px); width: calc(100% + 100px);
padding-right: 100px; padding-right: 100px;
height: 100%; height: 100%;
...@@ -162,7 +125,7 @@ ...@@ -162,7 +125,7 @@
} }
.wiki-sidebar-header { .wiki-sidebar-header {
padding: $gl-padding; padding: 0 $gl-padding $gl-padding;
.gutter-toggle { .gutter-toggle {
margin-top: 0; margin-top: 0;
......
...@@ -9,10 +9,11 @@ module PreviewMarkdown ...@@ -9,10 +9,11 @@ module PreviewMarkdown
markdown_params = markdown_params =
case controller_name case controller_name
when 'wikis' then { pipeline: :wiki, project_wiki: @project_wiki, page_slug: params[:id] }
when 'snippets' then { skip_project_check: true } when 'snippets' then { skip_project_check: true }
when 'groups' then { group: group } when 'groups' then { group: group }
when 'projects' then projects_filter_params when 'projects' then projects_filter_params
else preview_markdown_params else {}
end end
render json: { render json: {
...@@ -24,7 +25,6 @@ module PreviewMarkdown ...@@ -24,7 +25,6 @@ module PreviewMarkdown
} }
} }
end end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
private private
...@@ -35,12 +35,8 @@ module PreviewMarkdown ...@@ -35,12 +35,8 @@ module PreviewMarkdown
} }
end end
# Override this method to customise the markdown for your controller
def preview_markdown_params
{}
end
def markdown_service_params def markdown_service_params
params params
end end
# rubocop:enable Gitlab/ModuleWithInstanceVariables
end end
# frozen_string_literal: true
# Controllers that include this concern must provide:
# * project
# * current_user
module ProjectWikiActions
extend ActiveSupport::Concern
included do
before_action :authorize_read_wiki!
before_action :init_wiki_actions
attr_accessor :project_wiki, :sidebar_page, :sidebar_wiki_entries
end
def init_wiki_actions
load_project_wiki
load_wiki_sidebar
rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = _("Could not create Wiki Repository at this time. Please try again later.")
redirect_to project_path(project)
end
def load_project_wiki
self.project_wiki = load_wiki
end
def load_wiki_sidebar
self.sidebar_page = project_wiki.find_sidebar(params[:version_id])
return if sidebar_page.present?
# Fallback to default sidebar
self.sidebar_wiki_entries = WikiDirectory.group_by_directory(project_wiki.list_pages(limit: 15))
end
def load_wiki
# Call #wiki to make sure the Wiki Repo is initialized
ProjectWiki.new(project, current_user).tap(&:wiki)
end
end
# frozen_string_literal: true
class Projects::WikiDirectoriesController < Projects::ApplicationController
include ProjectWikiActions
def self.local_prefixes
[controller_path, 'shared/wiki']
end
def show
@wiki_dir = find_dir || WikiDirectory.new(params[:id])
return render('empty') if @wiki_dir.empty?
@wiki_entries = @wiki_pages = Kaminari
.paginate_array(@wiki_dir.pages)
.page(params[:page])
render 'show'
end
private
def find_dir
dir_params = params.permit(:id, :sort, :direction).dup.tap do |h|
Gitlab::Utils.allow_hash_values(h, sort_params_config[:allowed])
end
project_wiki.find_dir(dir_params[:id], dir_params[:sort], dir_params[:direction])
end
end
# frozen_string_literal: true
class Projects::WikiPagesController < Projects::ApplicationController
include ProjectWikiActions
include SendsBlob
include PreviewMarkdown
include Gitlab::Utils::StrongMemoize
def self.local_prefixes
[controller_path, 'shared/wiki']
end
before_action :authorize_create_wiki!, only: [:edit, :create, :update]
before_action :authorize_admin_wiki!, only: :destroy
before_action :load_page, only: [:show, :edit, :update, :history, :destroy]
before_action :valid_encoding?,
if: -> { %w[show edit update].include?(action_name) && load_page }
before_action only: [:edit, :update], unless: :valid_encoding? do
redirect_to(project_wiki_path(@project, @page))
end
def new
redirect_to project_wiki_path(@project, SecureRandom.uuid, random_title: true)
end
# `#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.
def show
if @page
show_page
elsif file_blob
show_blob
elsif should_create_missing_page?
create_missing_page
else
render 'missing_page'
end
end
def edit
end
def update
@page = WikiPages::UpdateService
.new(@project, current_user, wiki_params)
.execute(@page)
return saved(:updated) if @page.valid?
render 'edit'
rescue WikiPage::PageChangedError, WikiPage::PageRenameError, Gitlab::Git::Wiki::OperationError => e
@error = e
render 'edit'
end
def create
@page = WikiPages::CreateService
.new(@project, current_user, wiki_params)
.execute
return saved(:created) if @page.persisted?
render action: "edit"
rescue Gitlab::Git::Wiki::OperationError => e
@page = project_wiki.build_page(wiki_params)
@error = e
render 'edit'
end
def history
if @page
@page_versions = Kaminari.paginate_array(@page.versions(page: params[:page].to_i),
total_count: @page.count_versions)
.page(params[:page])
else
redirect_to(
project_wiki_path(@project, :home),
notice: _("Page not found")
)
end
end
def destroy
WikiPages::DestroyService.new(@project, current_user).execute(@page)
redirect_to project_wiki_path(@project, :home),
status: 302,
notice: _("Page was successfully deleted")
rescue Gitlab::Git::Wiki::OperationError => e
@error = e
render 'edit'
end
private
# Callback for PreviewMarkdown
def preview_markdown_params
{ pipeline: :wiki, project_wiki: project_wiki, page_slug: params[:id] }
end
def show_page
set_encoding_error unless valid_encoding?
@page_dir = @project_wiki.find_dir(@page.directory) if @page.directory.present?
@show_children = true
render 'show'
end
def show_blob
send_blob(@project_wiki.repository, file_blob)
end
def should_create_missing_page?
view_param = @project_wiki.exists? ? 'create' : params[:view]
view_param == 'create' && can?(current_user, :create_wiki, @project)
end
def create_missing_page
# Assign a title to the WikiPage unless `id` is a randomly generated slug from #new
title = params[:id] unless params[:random_title].present?
@page = project_wiki.build_page(title: title)
render 'edit'
end
def wiki_params
params.require(:wiki_page).permit(:title, :content, :format, :message, :last_commit_sha)
end
def load_page
@page ||= @project_wiki.find_page(*page_params)
end
def page_params
keys = [:id]
keys << :version_id if params[:action] == 'show'
params.values_at(*keys)
end
def valid_encoding?
strong_memoize(:valid_encoding) do
@page.content.encoding == Encoding::UTF_8
end
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 = @project_wiki.repository.commit(@project_wiki.default_branch)
next unless commit
@project_wiki.repository.blob_at(commit.id, params[:id])
end
end
def saved(action)
msg = case action
when :updated
_('Wiki was successfully updated')
when :created
_('Wiki was successfully created')
end
redirect_to(project_wiki_path(@project, @page), notice: msg)
end
end
# frozen_string_literal: true # frozen_string_literal: true
class Projects::WikisController < Projects::ApplicationController class Projects::WikisController < Projects::ApplicationController
include ProjectWikiActions include PreviewMarkdown
include WikiHelper include SendsBlob
include Gitlab::Utils::StrongMemoize
def self.local_prefixes before_action :authorize_read_wiki!
[controller_path, 'shared/wiki'] before_action :authorize_create_wiki!, only: [:edit, :create]
before_action :authorize_admin_wiki!, only: :destroy
before_action :load_project_wiki
before_action :load_page, only: [:show, :edit, :update, :history, :destroy]
before_action :valid_encoding?,
if: -> { %w[show edit update].include?(action_name) && load_page }
before_action only: [:edit, :update], unless: :valid_encoding? do
redirect_to(project_wiki_path(@project, @page))
end
def new
redirect_to project_wiki_path(@project, SecureRandom.uuid, random_title: true)
end end
def pages def pages
@nesting = show_children_param
@show_children = @nesting != ProjectWiki::NESTING_CLOSED
@wiki_pages = Kaminari.paginate_array( @wiki_pages = Kaminari.paginate_array(
project_wiki.list_pages(**sort_params) @project_wiki.list_pages(sort: params[:sort], direction: params[:direction])
).page(params[:page]) ).page(params[:page])
@wiki_entries = case @nesting @wiki_entries = WikiPage.group_by_directory(@wiki_pages)
when ProjectWiki::NESTING_FLAT end
@wiki_pages
else # `#show` handles a number of scenarios:
WikiDirectory.group_by_directory(@wiki_pages) #
end # - 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.
def show
if @page
set_encoding_error unless valid_encoding?
render 'show'
elsif file_blob
send_blob(@project_wiki.repository, file_blob)
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)
render 'edit'
else
render 'empty'
end
end
def edit
end
def update
return render('empty') unless can?(current_user, :create_wiki, @project)
@page = WikiPages::UpdateService.new(@project, current_user, wiki_params).execute(@page)
if @page.valid?
redirect_to(
project_wiki_path(@project, @page),
notice: _('Wiki was successfully updated.')
)
else
render 'edit'
end
rescue WikiPage::PageChangedError, WikiPage::PageRenameError, Gitlab::Git::Wiki::OperationError => e
@error = e
render 'edit'
end
def create
@page = WikiPages::CreateService.new(@project, current_user, wiki_params).execute
if @page.persisted?
redirect_to(
project_wiki_path(@project, @page),
notice: _('Wiki was successfully updated.')
)
else
render action: "edit"
end
rescue Gitlab::Git::Wiki::OperationError => e
@page = build_page(wiki_params)
@error = e
render 'edit'
end
def history
if @page
@page_versions = Kaminari.paginate_array(@page.versions(page: params[:page].to_i),
total_count: @page.count_versions)
.page(params[:page])
else
redirect_to(
project_wiki_path(@project, :home),
notice: _("Page not found")
)
end
end
def destroy
WikiPages::DestroyService.new(@project, current_user).execute(@page)
render 'show' redirect_to project_wiki_path(@project, :home),
status: 302,
notice: _("Page was successfully deleted")
rescue Gitlab::Git::Wiki::OperationError => e
@error = e
render 'edit'
end end
def git_access def git_access
...@@ -30,13 +122,74 @@ class Projects::WikisController < Projects::ApplicationController ...@@ -30,13 +122,74 @@ class Projects::WikisController < Projects::ApplicationController
private private
def sort_params def show_create_form?
process_params(sort_params_config) can?(current_user, :create_wiki, @project) &&
@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
(@project_wiki.exists? || params[:view] == 'create')
end end
def show_children_param def load_project_wiki
config = nesting_params_config(params[:sort]) @project_wiki = load_wiki
# Call #wiki to make sure the Wiki Repo is initialized
@project_wiki.wiki
@sidebar_page = @project_wiki.find_sidebar(params[:version_id])
unless @sidebar_page # Fallback to default sidebar
@sidebar_wiki_entries = WikiPage.group_by_directory(@project_wiki.list_pages(limit: 15))
end
rescue ProjectWiki::CouldNotCreateWikiError
flash[:notice] = _("Could not create Wiki Repository at this time. Please try again later.")
redirect_to project_path(@project)
false
end
def load_wiki
ProjectWiki.new(@project, current_user)
end
def wiki_params
params.require(:wiki).permit(:title, :content, :format, :message, :last_commit_sha)
end
def build_page(args = {})
WikiPage.new(@project_wiki).tap do |page|
page.update_attributes(args) # rubocop:disable Rails/ActiveRecordAliases
end
end
def load_page
@page ||= @project_wiki.find_page(*page_params)
end
def page_params
keys = [:id]
keys << :version_id if params[:action] == 'show'
params.values_at(*keys)
end
def valid_encoding?
strong_memoize(:valid_encoding) do
@page.content.encoding == Encoding::UTF_8
end
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 = @project_wiki.repository.commit(@project_wiki.default_branch)
next unless commit
process_params(config) @project_wiki.repository.blob_at(commit.id, params[:id])
end
end end
end end
...@@ -290,6 +290,7 @@ module ApplicationSettingsHelper ...@@ -290,6 +290,7 @@ module ApplicationSettingsHelper
:snowplow_cookie_domain, :snowplow_cookie_domain,
:snowplow_enabled, :snowplow_enabled,
:snowplow_site_id, :snowplow_site_id,
:snowplow_iglu_registry_url,
:push_event_hooks_limit, :push_event_hooks_limit,
:push_event_activities_limit, :push_event_activities_limit,
:custom_http_clone_url_root, :custom_http_clone_url_root,
......
...@@ -41,12 +41,6 @@ module IconsHelper ...@@ -41,12 +41,6 @@ module IconsHelper
ActionController::Base.helpers.image_path('file_icons.svg', host: sprite_base_url) ActionController::Base.helpers.image_path('file_icons.svg', host: sprite_base_url)
end end
def sprite_icon_with_text(icon_name, content, opts = {})
wrapper_class = opts.delete(:wrapper_class)
icon = sprite_icon(icon_name, opts)
content_tag(:span, [icon, content].join('').html_safe, class: wrapper_class)
end
def sprite_icon(icon_name, size: nil, css_class: nil) def sprite_icon(icon_name, size: nil, css_class: nil)
if Gitlab::Sentry.should_raise_for_dev? if Gitlab::Sentry.should_raise_for_dev?
unless known_sprites.include?(icon_name) unless known_sprites.include?(icon_name)
......
...@@ -48,23 +48,14 @@ module WikiHelper ...@@ -48,23 +48,14 @@ module WikiHelper
expose_url(api_v4_projects_wikis_attachments_path(id: @project.id)) expose_url(api_v4_projects_wikis_attachments_path(id: @project.id))
end end
WIKI_SORT_CSS_CLASSES = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort' def wiki_sort_controls(project, sort, direction)
sort ||= ProjectWiki::TITLE_ORDER
def wiki_sort_controls(sort_params = {}, &block) link_class = 'btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort'
current_sort = sort_params[:sort] || ProjectWiki::TITLE_ORDER reversed_direction = direction == 'desc' ? 'asc' : 'desc'
current_direction = (sort_params[:direction] || 'asc').inquiry icon_class = direction == 'desc' ? 'highest' : 'lowest'
reversed_direction = current_direction.desc? ? 'asc' : 'desc' link_to(project_wikis_pages_path(project, sort: sort, direction: reversed_direction),
icon_class = current_direction.desc? ? 'highest' : 'lowest' type: 'button', class: link_class, title: _('Sort direction')) do
sorting = sort_params.merge(sort: current_sort, direction: reversed_direction)
opts = {
type: 'button',
class: WIKI_SORT_CSS_CLASSES,
title: _('Sort direction')
}
link_to(yield(sorting), opts) do
sprite_icon("sort-#{icon_class}", size: 16) sprite_icon("sort-#{icon_class}", size: 16)
end end
end end
...@@ -76,86 +67,4 @@ module WikiHelper ...@@ -76,86 +67,4 @@ module WikiHelper
s_("Wiki|Title") s_("Wiki|Title")
end end
end end
# Render the sprite icon given the current show_children state
def wiki_show_children_icon(nesting)
icon_name, icon_text =
case nesting
when ProjectWiki::NESTING_TREE
['folder-open', s_("Wiki|Show folder contents")]
when ProjectWiki::NESTING_CLOSED
['folder-o', s_("Wiki|Hide folder contents")]
else
['list-bulleted', s_("Wiki|Show files separately")]
end
sprite_icon_with_text(icon_name, icon_text, size: 16)
end
def wiki_page_link(wiki_page, nesting, project)
link = link_to(wiki_page.title,
project_wiki_path(project, wiki_page),
class: 'wiki-page-title')
case nesting
when ProjectWiki::NESTING_FLAT
tags = []
if wiki_page.directory.present?
wiki_dir = WikiDirectory.new(wiki_page.directory)
tags << link_to(wiki_dir.slug, project_wiki_dir_path(project, wiki_dir), class: 'wiki-page-dir-name')
tags << content_tag(:span, '/', class: 'wiki-page-name-separator')
end
tags << link
tags.join.html_safe
else
link
end
end
def sort_params_config
{
keys: [:sort, :direction],
defaults: {
sort: ProjectWiki::TITLE_ORDER, direction: ProjectWiki::DIRECTION_ASC
},
allowed: {
sort: ProjectWiki::SORT_ORDERS, direction: ProjectWiki::SORT_DIRECTIONS
}
}
end
def nesting_params_config(sort_key)
default_val = case sort_key
when ProjectWiki::CREATED_AT_ORDER
ProjectWiki::NESTING_FLAT
else
ProjectWiki::NESTING_CLOSED
end
{
keys: [:show_children],
defaults: { show_children: default_val },
allowed: { show_children: ProjectWiki::NESTINGS }
}
end
def process_params(config)
unprocessed = params.permit(*config[:keys])
processed = unprocessed
.with_defaults(config[:defaults])
.tap { |h| Gitlab::Utils.allow_hash_values(h, config[:allowed]) }
.to_hash
.transform_keys(&:to_sym)
if processed.keys == config[:keys]
processed.size == 1 ? processed.values.first : processed
else
raise ActionController::BadRequest, "illegal parameters: #{unprocessed}"
end
end
def home_page?
params[:id] == 'home'
end
end end
...@@ -104,6 +104,11 @@ class ApplicationSetting < ApplicationRecord ...@@ -104,6 +104,11 @@ class ApplicationSetting < ApplicationRecord
hostname: true, hostname: true,
if: :snowplow_enabled if: :snowplow_enabled
validates :snowplow_iglu_registry_url,
addressable_url: true,
allow_blank: true,
if: :snowplow_enabled
validates :pendo_url, validates :pendo_url,
presence: true, presence: true,
public_url: true, public_url: true,
......
...@@ -129,6 +129,7 @@ module ApplicationSettingImplementation ...@@ -129,6 +129,7 @@ module ApplicationSettingImplementation
snowplow_cookie_domain: nil, snowplow_cookie_domain: nil,
snowplow_enabled: false, snowplow_enabled: false,
snowplow_site_id: nil, snowplow_site_id: nil,
snowplow_iglu_registry_url: nil,
custom_http_clone_url_root: nil, custom_http_clone_url_root: nil,
pendo_enabled: false, pendo_enabled: false,
pendo_url: nil pendo_url: nil
......
...@@ -75,6 +75,11 @@ class Deployment < ApplicationRecord ...@@ -75,6 +75,11 @@ class Deployment < ApplicationRecord
find(ids) find(ids)
end end
def self.distinct_on_environment
order('environment_id, deployments.id DESC')
.select('DISTINCT ON (environment_id) deployments.*')
end
def self.find_successful_deployment!(iid) def self.find_successful_deployment!(iid)
success.find_by!(iid: iid) success.find_by!(iid: iid)
end end
......
...@@ -10,6 +10,7 @@ class Environment < ApplicationRecord ...@@ -10,6 +10,7 @@ class Environment < ApplicationRecord
has_many :successful_deployments, -> { success }, class_name: 'Deployment' has_many :successful_deployments, -> { success }, class_name: 'Deployment'
has_one :last_deployment, -> { success.order('deployments.id DESC') }, class_name: 'Deployment' has_one :last_deployment, -> { success.order('deployments.id DESC') }, class_name: 'Deployment'
has_one :last_visible_deployment, -> { visible.distinct_on_environment }, class_name: 'Deployment'
before_validation :nullify_external_url before_validation :nullify_external_url
before_validation :generate_slug, if: ->(env) { env.slug.blank? } before_validation :generate_slug, if: ->(env) { env.slug.blank? }
......
...@@ -17,13 +17,6 @@ class ProjectWiki ...@@ -17,13 +17,6 @@ class ProjectWiki
CREATED_AT_ORDER = 'created_at' CREATED_AT_ORDER = 'created_at'
DIRECTION_DESC = 'desc' DIRECTION_DESC = 'desc'
DIRECTION_ASC = 'asc' DIRECTION_ASC = 'asc'
SORT_ORDERS = [TITLE_ORDER, CREATED_AT_ORDER].freeze
SORT_DIRECTIONS = [DIRECTION_ASC, DIRECTION_DESC].freeze
NESTING_FLAT = 'flat'
NESTING_TREE = 'tree'
NESTING_CLOSED = 'hidden'
NESTINGS = [NESTING_TREE, NESTING_CLOSED, NESTING_FLAT].freeze
# Returns a string describing what went wrong after # Returns a string describing what went wrong after
# an operation fails. # an operation fails.
...@@ -65,11 +58,7 @@ class ProjectWiki ...@@ -65,11 +58,7 @@ class ProjectWiki
end end
def wiki_base_path def wiki_base_path
::File.join(project_base_path, 'wikis') [Gitlab.config.gitlab.relative_url_root, '/', @project.full_path, '/wikis'].join('')
end
def wiki_page_path
::File.join(project_base_path, '-', 'wiki_pages')
end end
# Returns the Gitlab::Git::Wiki object. # Returns the Gitlab::Git::Wiki object.
...@@ -136,23 +125,6 @@ class ProjectWiki ...@@ -136,23 +125,6 @@ class ProjectWiki
end end
end end
# Finds directory within the repository based on a slug
#
# dir_name - The directory prefix.
#
# Returns an initialized WikiDirectory instance or nil
def find_dir(dir_name, sort = nil, direction = DIRECTION_ASC)
descending = direction == DIRECTION_DESC
# WikiListPagesRequest currently does not support server-side
# filtering. Ideally this logic should be moved to the gitaly
# side.
pages = wiki
.list_pages(sort: sort, direction_desc: descending)
.map { |page| WikiPage.new(self, page, true) }
.select { |wp| wp.directory == dir_name }
WikiDirectory.new(dir_name, pages) if pages.present?
end
def find_sidebar(version = nil) def find_sidebar(version = nil)
find_page(SIDEBAR, version) find_page(SIDEBAR, version)
end end
...@@ -172,12 +144,6 @@ class ProjectWiki ...@@ -172,12 +144,6 @@ class ProjectWiki
false false
end end
def build_page(attrs)
WikiPage.new(self).tap do |page|
page.update_attributes(attrs) # rubocop:disable Rails/ActiveRecordAliases
end
end
def update_page(page, content:, title: nil, format: :markdown, message: nil) def update_page(page, content:, title: nil, format: :markdown, message: nil)
commit = commit_details(:updated, message, page.title) commit = commit_details(:updated, message, page.title)
...@@ -205,7 +171,7 @@ class ProjectWiki ...@@ -205,7 +171,7 @@ class ProjectWiki
title_array = title.split("/") title_array = title.split("/")
title = title_array.pop title = title_array.pop
[title, ::File.join(title_array)] [title, title_array.join("/")]
end end
def repository def repository
...@@ -232,10 +198,6 @@ class ProjectWiki ...@@ -232,10 +198,6 @@ class ProjectWiki
private private
def project_base_path
::File.join(Gitlab.config.gitlab.relative_url_root, @project.full_path)
end
def create_repo!(raw_repository) def create_repo!(raw_repository)
gitlab_shell.create_wiki_repository(project) gitlab_shell.create_wiki_repository(project)
......
# frozen_string_literal: true # frozen_string_literal: true
class WikiDirectory class WikiDirectory
include StaticModel
include ActiveModel::Validations include ActiveModel::Validations
attr_accessor :slug, :pages attr_accessor :slug, :pages
validates :slug, presence: true validates :slug, presence: true
# StaticModel overrides and configuration:
def self.primary_key
'slug'
end
def id
"#{slug}@#{last_version&.sha}"
end
def self.model_name
ActiveModel::Name.new(self, nil, 'wiki_dir')
end
alias_method :to_param, :slug
alias_method :title, :slug
# Sorts and groups pages by directory.
#
# pages - an array of WikiPage objects.
#
# Returns an array of WikiPage and WikiDirectory objects.
# The entries are sorted in the order of the input array, where
# directories appear in the position of their first member.
def self.group_by_directory(pages)
grouped = []
dirs = Hash.new do |h, k|
new(k).tap { |dir| grouped << (h[k] = dir) }
end
Array.wrap(pages).each_with_object(grouped) do |page, top_level|
group = page.directory.present? ? dirs[page.directory] : top_level
group << page
end
end
def initialize(slug, pages = []) def initialize(slug, pages = [])
@slug = slug @slug = slug
@pages = pages @pages = pages
end end
def <<(page) # Relative path to the partial to be used when rendering collections
@pages << page # of this object.
@last_version = nil def to_partial_path
end 'projects/wikis/wiki_directory'
def last_version
@last_version ||= @pages.map(&:last_version).max_by(&:authored_date)
end
def page_count
@pages.size
end
def empty?
page_count.zero?
end
def to_partial_path(context = nil)
name = [context, 'wiki_directory'].compact.join('_')
"projects/wiki_directories/#{name}"
end end
end end
...@@ -15,7 +15,30 @@ class WikiPage ...@@ -15,7 +15,30 @@ class WikiPage
end end
def self.model_name def self.model_name
ActiveModel::Name.new(self, nil, 'wiki_page') 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.each_with_object([]) do |page, grouped_pages|
next grouped_pages << page unless page.directory.present?
directory = grouped_pages.find do |obj|
obj.is_a?(WikiDirectory) && obj.slug == page.directory
end
next directory.pages << page if directory
grouped_pages << WikiDirectory.new(page.directory, [page])
end
end end
def self.unhyphenize(name) def self.unhyphenize(name)
...@@ -43,16 +66,6 @@ class WikiPage ...@@ -43,16 +66,6 @@ class WikiPage
Gitlab::HookData::WikiPageBuilder.new(self).build Gitlab::HookData::WikiPageBuilder.new(self).build
end end
# Create a new WikiPage
#
# == Parameters:
# wiki::
# A `ProjectWiki` model object
# page::
# A `Gitlab::Git::WikiPage` business object, to which this class provides a facade
# persisted::
# Is this page fully saved on disk?
#
def initialize(wiki, page = nil, persisted = false) def initialize(wiki, page = nil, persisted = false)
@wiki = wiki @wiki = wiki
@page = page @page = page
...@@ -243,10 +256,10 @@ class WikiPage ...@@ -243,10 +256,10 @@ class WikiPage
end end
end end
def to_partial_path(context = nil) # Relative path to the partial to be used when rendering collections
name = [context, 'wiki_page'].compact.join('_') # of this object.
def to_partial_path
"projects/wiki_pages/#{name}" 'projects/wikis/wiki_page'
end end
def id def id
......
...@@ -42,6 +42,10 @@ module Notes ...@@ -42,6 +42,10 @@ module Notes
clear_noteable_diffs_cache(note) clear_noteable_diffs_cache(note)
Suggestions::CreateService.new(note).execute Suggestions::CreateService.new(note).execute
increment_usage_counter(note) increment_usage_counter(note)
if Feature.enabled?(:notes_create_service_tracking, project)
Gitlab::Tracking.event('Notes::CreateService', 'execute', tracking_data_for(note))
end
end end
if quick_actions_service.commands_executed_count.to_i > 0 if quick_actions_service.commands_executed_count.to_i > 0
...@@ -59,5 +63,16 @@ module Notes ...@@ -59,5 +63,16 @@ module Notes
note note
end end
private
def tracking_data_for(note)
label = Gitlab.ee? && note.author == User.visual_review_bot ? 'anonymous_visual_review_note' : 'note'
{
label: label,
value: note.id
}
end
end end
end end
...@@ -26,5 +26,8 @@ ...@@ -26,5 +26,8 @@
.form-group .form-group
= f.label :snowplow_cookie_domain, _('Cookie domain'), class: 'label-light' = f.label :snowplow_cookie_domain, _('Cookie domain'), class: 'label-light'
= f.text_field :snowplow_cookie_domain, class: 'form-control' = f.text_field :snowplow_cookie_domain, class: 'form-control'
.form-group
= f.label :snowplow_iglu_registry_url, _('Iglu registry URL (optional)'), class: 'label-light'
= f.text_field :snowplow_iglu_registry_url, class: 'form-control'
= f.submit _('Save changes'), class: 'btn btn-success' = f.submit _('Save changes'), class: 'btn btn-success'
...@@ -264,7 +264,7 @@ ...@@ -264,7 +264,7 @@
dismiss_endpoint: user_callouts_path } } dismiss_endpoint: user_callouts_path } }
- if show_cluster_hint - if show_cluster_hint
.feature-highlight-popover-content .feature-highlight-popover-content
= image_tag 'illustrations/cluster_popover.svg', class: 'feature-highlight-illustration' = image_tag 'illustrations/cluster_popover.svg', class: 'feature-highlight-illustration', lazy: false, alt: _('Kubernetes popover')
.feature-highlight-popover-sub-content .feature-highlight-popover-sub-content
%p= _('Allows you to add and manage Kubernetes clusters.') %p= _('Allows you to add and manage Kubernetes clusters.')
%p %p
...@@ -282,14 +282,14 @@ ...@@ -282,14 +282,14 @@
- if project_nav_tab? :wiki - if project_nav_tab? :wiki
- wiki_url = project_wiki_path(@project, :home) - wiki_url = project_wiki_path(@project, :home)
= nav_link(controller: [:wikis, :wiki_pages, :wiki_directories]) do = nav_link(controller: :wikis) do
= link_to wiki_url, class: 'shortcuts-wiki', data: { qa_selector: 'wiki_link' } do = link_to wiki_url, class: 'shortcuts-wiki', data: { qa_selector: 'wiki_link' } do
.nav-icon-container .nav-icon-container
= sprite_icon('book') = sprite_icon('book')
%span.nav-item-name %span.nav-item-name
= _('Wiki') = _('Wiki')
%ul.sidebar-sub-level-items.is-fly-out-only %ul.sidebar-sub-level-items.is-fly-out-only
= nav_link(controller: [:wikis, :wiki_pages, :wiki_directories], html_options: { class: "fly-out-top-item" } ) do = nav_link(controller: :wikis, html_options: { class: "fly-out-top-item" } ) do
= link_to wiki_url do = link_to wiki_url do
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
= _('Wiki') = _('Wiki')
......
%li
%span.text-secondary-500.svg-icon.svg-baseline
- if @show_children
= sprite_icon('folder-open', size: 16)
- else
= sprite_icon('folder-o', size: 16)
= link_to wiki_dir.slug, project_wiki_dir_path(@project, wiki_dir)
- unless @show_children
%span.badge.badge-pill.wiki-dir-page-count= wiki_dir.page_count
.float-right
%small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_dir.last_version.authored_date) }).html_safe
- if @show_children
%ul
= render wiki_dir.pages, context: context
%li
%span.text-secondary-300.svg-icon.svg-baseline
= sprite_icon('folder-open', size: 16)
= link_to wiki_dir.slug, project_wiki_dir_path(@project, wiki_dir)
%ul= render wiki_dir.pages, context: context
= render wiki_directory.to_partial_path(context), wiki_dir: wiki_directory, context: context
- layout_path = 'shared/empty_states/wikis_layout'
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, :home)
- add_to_breadcrumbs s_("Wiki|Pages"), project_wikis_pages_path(@project)
- breadcrumb_title s_(@wiki_dir.slug)
- page_title @wiki_dir.slug
- if can?(current_user, :create_wiki, @project)
- create_path = project_wiki_path(@project, params[:id], { view: 'create', params: { title: "#{params[:id]}/" } })
- create_link = link_to s_('WikiDirEmpty|Create a page in this directory'), create_path, class: 'btn btn-success qa-create-first-page-link', title: s_('WikiDirEmpty|Create a page')
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_login_empty.svg' } do
%h4.text-left
= s_('WikiDirEmpty|This directory has no wiki pages')
%p.text-left
= s_("WikiDirEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on.")
= create_link
- elsif can?(current_user, :read_issue, @project)
- issues_link = link_to s_('WikiEmptyIssueMessage|issue tracker'), project_issues_path(@project)
- new_issue_link = link_to s_('WikiEmpty|Suggest wiki improvement'), new_project_issue_path(@project), class: 'btn btn-success', title: s_('WikiEmptyIssueMessage|Suggest wiki improvement')
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
%h4
= s_('WikiDirEmpty|This directory has no wiki pages')
%p.text-left
= s_('WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}.').html_safe % { issues_link: issues_link }
= new_issue_link
- else
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
%h4
= s_('WikiDirEmpty|This directory has no wiki pages')
%p
= s_('WikiEmpty|You must be a project member in order to add wiki pages.')
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, :home)
- add_to_breadcrumbs s_("Wiki|Pages"), project_wikis_pages_path(@project)
- breadcrumb_title s_(@wiki_dir.slug)
- page_title @wiki_dir.slug
= render 'page_listing', { allow_change_nesting: false, wiki_page_title: page_title, page_path: ->(opts) { project_wiki_dir_path(@project, @wiki_dir, opts) } }
= link_to @page.human_title, project_wiki_path(@project, @page)
%span.light
= _('&middot;').html_safe
= subtitle
- is_active = params[:id] == wiki_page.slug
- icon_active_class = is_active ? 'text-secondary-800' : 'text-secondary-300'
%li{ class: active_when(is_active) }
%span.svg-icon.svg-baseline{ class: icon_active_class }
= sprite_icon('book', size: 16)
- if is_active
= wiki_page.human_title
- else
= link_to project_wiki_path(@project, wiki_page) do
= wiki_page.human_title
= render wiki_page.to_partial_path(context), wiki_page: wiki_page
- @content_class = 'wiki-page' + (fluid_layout ? '' : ' limit-container-width')
- breadcrumb_title @page.human_title
- page_title @page.human_title, _("Wiki")
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, :home)
- add_to_breadcrumbs s_("Wiki|Pages"), project_wikis_pages_path(@project)
- if @page_dir.present?
- add_to_breadcrumbs _(@page_dir.slug), project_wiki_dir_path(@project, @page_dir)
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
%button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
= icon('angle-double-left')
.nav-text.flex-fill
%h2.wiki-page-title= @page.human_title
%span.wiki-last-edit-by
- if @page.last_version
= (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
#{time_ago_with_tooltip(@page.last_version.authored_date)}
.nav-controls.pb-md-3.pb-lg-0
= render 'main_links'
- if @page.historical?
.warning_message
= s_("WikiHistoricalPage|This is an old version of this page.")
- most_recent_link = link_to s_("WikiHistoricalPage|most recent version"), project_wiki_path(@project, @page)
- history_link = link_to s_("WikiHistoricalPage|history"), project_wiki_history_path(@project, @page)
= (s_("WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}.") % { most_recent_link: most_recent_link, history_link: history_link }).html_safe
.prepend-top-default.append-bottom-default
.md.md-file.qa-wiki-page-content
= render_wiki_content(@page)
= render 'sidebar'
- form_classes = 'wiki-form common-note-form prepend-top-default js-quick-submit' - form_classes = 'wiki-form common-note-form prepend-top-default js-quick-submit'
- form_classes += ' js-new-wiki-page' unless @page.persisted? - form_classes += ' js-new-wiki-page' unless @page.persisted?
= form_for [@project.namespace.becomes(Namespace), @project, @page], = form_for [@project.namespace.becomes(Namespace), @project, @page], method: @page.persisted? ? :put : :post,
method: @page.persisted? ? 'put' : 'post', html: { class: form_classes },
url: { controller: 'wiki_pages', action: @page.persisted? ? :update : :create }, data: { uploads_path: uploads_path } do |f|
html: { class: form_classes },
data: { uploads_path: uploads_path } do |f|
= form_errors(@page) = form_errors(@page)
- if @page.persisted? - if @page.persisted?
...@@ -14,7 +12,7 @@ ...@@ -14,7 +12,7 @@
.form-group.row .form-group.row
.col-sm-12= f.label :title, class: 'control-label-full-width' .col-sm-12= f.label :title, class: 'control-label-full-width'
.col-sm-12 .col-sm-12
= f.text_field :title, class: 'form-control qa-wiki-title-textbox', value: @page.title, required: true, autofocus: !@page.persisted?, placeholder: s_('Wiki|Page title') = f.text_field :title, class: 'form-control qa-wiki-title-textbox', value: @page.title, required: true, autofocus: !@page.persisted?, placeholder: _('Wiki|Page title')
%span.d-inline-block.mw-100.prepend-top-5 %span.d-inline-block.mw-100.prepend-top-5
= icon('lightbulb-o') = icon('lightbulb-o')
- if @page.persisted? - if @page.persisted?
......
- if (@page && @page.persisted?) - if (@page && @page.persisted?)
- if can?(current_user, :create_wiki, @project) - if can?(current_user, :create_wiki, @project)
= link_to project_wiki_pages_new_path(@project), class: "add-new-wiki btn btn-success", role: "button" do = link_to project_wikis_new_path(@project), class: "add-new-wiki btn btn-success", role: "button" do
= s_("Wiki|New page") = s_("Wiki|New page")
= link_to project_wiki_history_path(@project, @page), class: "btn", role: "button" do = link_to project_wiki_history_path(@project, @page), class: "btn", role: "button" do
= s_("Wiki|Page history") = s_("Wiki|Page history")
......
%li %li
%span.text-secondary-500.svg-icon.svg-baseline= sprite_icon('book', size: 16) = link_to wiki_page.title, project_wiki_path(@project, wiki_page)
= wiki_page_link(wiki_page, @nesting, @project) %small (#{wiki_page.format})
.float-right .float-right
%span.badge.badge-pill.wiki-page-format= _(wiki_page.format)
- if wiki_page.last_version - if wiki_page.last_version
= '/'
%small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe %small= (s_("Last edited %{date}") % { date: time_ago_with_tooltip(wiki_page.last_version.authored_date) }).html_safe
%li{ class: active_when(params[:id] == wiki_page.slug) }
= link_to project_wiki_path(@project, wiki_page) do
= wiki_page.human_title
%li
= wiki_directory.slug
%ul
= render wiki_directory.pages, context: context
= render "#{context}_wiki_page", wiki_page: wiki_page
- @content_class = 'edit-wiki-page' + (fluid_layout ? '' : ' limit-container-width') - @content_class = "limit-container-width" unless fluid_layout
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, :home) - add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, @page)
- add_to_breadcrumbs s_("Wiki|Pages"), project_wikis_pages_path(@project)
- if @page.persisted? && @page_dir.present?
- add_to_breadcrumbs _(@page_dir.slug), project_wiki_dir_path(@project, @page_dir)
- if @page.persisted?
- add_to_breadcrumbs @page.human_title, project_wiki_path(@project, @page)
- breadcrumb_title @page.persisted? ? _("Edit") : _("New") - breadcrumb_title @page.persisted? ? _("Edit") : _("New")
- page_title @page.persisted? ? _("Edit") : _("New"), @page.human_title, _("Wiki") - page_title @page.persisted? ? _("Edit") : _("New"), @page.human_title, _("Wiki")
...@@ -17,7 +12,10 @@ ...@@ -17,7 +12,10 @@
.nav-text .nav-text
%h2.wiki-page-title %h2.wiki-page-title
- if @page.persisted? - if @page.persisted?
= render partial: 'page_title', locals: { subtitle: s_("Wiki|Edit Page") } = link_to @page.human_title, project_wiki_path(@project, @page)
%span.light
&middot;
= s_("Wiki|Edit Page")
- else - else
= s_("Wiki|Create New Page") = s_("Wiki|Create New Page")
......
- @content_class = 'wiki-history'
- page_title _("History"), @page.human_title, _("Wiki") - page_title _("History"), @page.human_title, _("Wiki")
.wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row .wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
...@@ -7,7 +6,10 @@ ...@@ -7,7 +6,10 @@
.nav-text .nav-text
%h2.wiki-page-title %h2.wiki-page-title
= render partial: 'page_title', locals: { subtitle: _("History") } = link_to @page.human_title, project_wiki_path(@project, @page)
%span.light
&middot;
= _("History")
.table-holder .table-holder
%table.table %table.table
...@@ -37,4 +39,4 @@ ...@@ -37,4 +39,4 @@
= version.format = version.format
= paginate @page_versions, theme: 'gitlab' = paginate @page_versions, theme: 'gitlab'
= render 'shared/wiki/sidebar' = render 'sidebar'
- add_to_breadcrumbs "Wiki", project_wiki_path(@project, :home)
- breadcrumb_title s_("Wiki|Pages")
- page_title s_("Wiki|Pages"), _("Wiki")
- sort_title = wiki_sort_title(params[:sort])
.wiki-page-header.top-area.flex-column.flex-lg-row
.nav-text.flex-fill
%h2.wiki-page-title
= s_("Wiki|Wiki Pages")
.nav-controls.pb-md-3.pb-lg-0
= link_to project_wikis_git_access_path(@project), class: 'btn' do
= icon('cloud-download')
= _("Clone repository")
.dropdown.inline.wiki-sort-dropdown
.btn-group{ role: 'group' }
.btn-group{ role: 'group' }
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
= sort_title
= icon('chevron-down')
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
%li
= sortable_item(s_("Wiki|Title"), project_wikis_pages_path(@project, sort: ProjectWiki::TITLE_ORDER), sort_title)
= sortable_item(s_("Wiki|Created date"), project_wikis_pages_path(@project, sort: ProjectWiki::CREATED_AT_ORDER), sort_title)
= wiki_sort_controls(@project, params[:sort], params[:direction])
%ul.wiki-pages-list.content-list
= render @wiki_entries, context: 'pages'
= paginate @wiki_pages, theme: 'gitlab'
- add_to_breadcrumbs "Wiki", project_wiki_path(@project, :home) - @content_class = "limit-container-width" unless fluid_layout
- breadcrumb_title s_("Wiki|Pages") - breadcrumb_title @page.human_title
- page_title s_("Wiki|Contents"), _("Wiki") - wiki_breadcrumb_dropdown_links(@page.slug)
- page_title @page.human_title, _("Wiki")
- add_to_breadcrumbs _("Wiki"), project_wiki_path(@project, :home)
= render 'page_listing', { allow_change_nesting: ::Feature.enabled?(:wikis_allow_change_nesting), wiki_page_title: page_title, page_path: ->(opts) { project_wikis_pages_path(@project, opts) } } .wiki-page-header.top-area.has-sidebar-toggle.flex-column.flex-lg-row
%button.btn.btn-default.sidebar-toggle.js-sidebar-wiki-toggle{ role: "button", type: "button" }
= icon('angle-double-left')
.nav-text.flex-fill
%h2.wiki-page-title= @page.human_title
%span.wiki-last-edit-by
- if @page.last_version
= (_("Last edited by %{name}") % { name: "<strong>#{@page.last_version.author_name}</strong>" }).html_safe
#{time_ago_with_tooltip(@page.last_version.authored_date)}
.nav-controls.pb-md-3.pb-lg-0
= render 'main_links'
- if @page.historical?
.warning_message
= s_("WikiHistoricalPage|This is an old version of this page.")
- most_recent_link = link_to s_("WikiHistoricalPage|most recent version"), project_wiki_path(@project, @page)
- history_link = link_to s_("WikiHistoricalPage|history"), project_wiki_history_path(@project, @page)
= (s_("WikiHistoricalPage|You can view the %{most_recent_link} or browse the %{history_link}.") % { most_recent_link: most_recent_link, history_link: history_link }).html_safe
.prepend-top-default.append-bottom-default
.md.md-file{ data: { qa_selector: 'wiki_page_content' } }
= render_wiki_content(@page)
= render 'sidebar'
- layout_path = 'shared/empty_states/wikis_layout' - layout_path = 'shared/empty_states/wikis_layout'
- wiki_is_empty = @project_wiki.empty?
- empty_msg = wiki_is_empty ? s_('WikiEmpty|This project has no wiki pages') : s_('WikiEmpty|This page does not exist')
- create_msg = wiki_is_empty ? s_('WikiEmpty|Create your first page') : s_('WikiEmpty|Create this page')
- if can?(current_user, :create_wiki, @project) - if can?(current_user, :create_wiki, @project)
- create_path = project_wiki_path(@project, params[:id], { view: 'create' }) - create_path = project_wiki_path(@project, params[:id], { view: 'create' })
- create_link = link_to create_msg, create_path, class: 'btn btn-success qa-create-first-page-link', title: create_msg - create_link = link_to s_('WikiEmpty|Create your first page'), create_path, class: 'btn btn-success qa-create-first-page-link', title: s_('WikiEmpty|Create your first page')
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_login_empty.svg' } do = render layout: layout_path, locals: { image_path: 'illustrations/wiki_login_empty.svg' } do
%h4.text-left %h4.text-left
...@@ -20,7 +17,7 @@ ...@@ -20,7 +17,7 @@
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do = render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
%h4 %h4
= empty_msg = s_('WikiEmpty|This project has no wiki pages')
%p.text-left %p.text-left
= s_('WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}.').html_safe % { issues_link: issues_link } = s_('WikiEmptyIssueMessage|You must be a project member in order to add wiki pages. If you have suggestions for how to improve the wiki for this project, consider opening an issue in the %{issues_link}.').html_safe % { issues_link: issues_link }
= new_issue_link = new_issue_link
...@@ -28,6 +25,6 @@ ...@@ -28,6 +25,6 @@
- else - else
= render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do = render layout: layout_path, locals: { image_path: 'illustrations/wiki_logout_empty.svg' } do
%h4 %h4
= empty_msg = s_('WikiEmpty|This project has no wiki pages')
%p %p
= s_('WikiEmpty|You must be a project member in order to add wiki pages.') = s_('WikiEmpty|You must be a project member in order to add wiki pages.')
- @no_container = true
- current_sorting = params.permit(:sort, :direction)
- sort_title = wiki_sort_title(params[:sort])
%div{ class: container_class }
.wiki-page-header.top-area.flex-column.flex-lg-row
.nav-text.flex-fill
%h2.wiki-page-title
= wiki_page_title
.nav-controls.pb-md-3.pb-lg-0
- if can?(current_user, :create_wiki, @project)
= link_to project_wiki_pages_new_path(@project), class: "add-new-wiki btn btn-success" do
= s_("Wiki|New page")
= link_to project_wikis_git_access_path(@project), class: 'btn qa-clone-repository-link' do
= sprite_icon('download', size: 16)
= _("Clone repository")
- if @nesting.present? && allow_change_nesting
.dropdown.inline.wiki-nesting-dropdown
.btn-group{ role: 'group' }
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
= wiki_show_children_icon(@nesting)
= sprite_icon('chevron-down', size: 16)
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
- ProjectWiki::NESTINGS.each do |choice|
%li= link_to wiki_show_children_icon(choice), page_path.call(current_sorting.merge(show_children: choice)), class: @nesting == choice ? 'is-active' : ''
.dropdown.inline.wiki-sort-dropdown
.btn-group{ role: 'group' }
%button.dropdown-toggle{ type: 'button', data: { toggle: 'dropdown', display: 'static' }, class: 'btn btn-default' }
= sort_title
= sprite_icon('chevron-down', size: 16)
%ul.dropdown-menu.dropdown-menu-right.dropdown-menu-selectable.dropdown-menu-sort
%li
= sortable_item(s_("Wiki|Title"), page_path.call(sort: ProjectWiki::TITLE_ORDER), sort_title)
= sortable_item(s_("Wiki|Created date"), page_path.call(sort: ProjectWiki::CREATED_AT_ORDER), sort_title)
= wiki_sort_controls(current_sorting.merge(show_children: @nesting), &page_path)
%ul.wiki-pages-list.content-list
= render @wiki_entries, context: 'pages'
= paginate @wiki_pages, theme: 'gitlab'
---
title: Fix cluster feature highlight popover image
merge_request: 19372
author:
type: fixed
---
title: Add ApplicationSetting for snowplow_iglu_registry_url
merge_request: 18449
author:
type: added
---
title: Sort wiki pages by date
merge_request: 30245
type: added
...@@ -617,7 +617,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do ...@@ -617,7 +617,7 @@ constraints(::Constraints::ProjectUrlConstrainer.new) do
end end
# Since both wiki and repository routing contains wildcard characters # Since both wiki and repository routing contains wildcard characters
# its preferable to keep them below all other project routes # its preferable to keep it below all other project routes
draw :wiki draw :wiki
draw :repository draw :repository
......
scope(controller: :wikis) do scope(controller: :wikis) do
scope(path: 'wikis/pages', as: :wiki_pages, format: false) do
get :new, to: 'wiki_pages#new'
post '/', to: 'wiki_pages#create'
end
scope(path: 'wikis', as: :wikis) do scope(path: 'wikis', as: :wikis) do
get :git_access get :git_access
get :pages get :pages
get '/', to: redirect('%{namespace_id}/%{project_id}/-/wiki_pages/home') get :new
get '/*id', to: redirect('%{namespace_id}/%{project_id}/-/wiki_pages/%{id}') get '/', to: redirect('%{namespace_id}/%{project_id}/wikis/home')
end post '/', to: 'wikis#create'
scope(path: '-/wiki_pages', as: :wiki_page, format: false) do
post '/', to: 'wiki_pages#create'
end end
scope(path: '-/wiki_pages/*id', as: :wiki, format: false, controller: :wiki_pages) do scope(path: 'wikis/*id', as: :wiki, format: false) do
get :edit get :edit
get :history get :history
post :preview_markdown post :preview_markdown
...@@ -23,8 +15,4 @@ scope(controller: :wikis) do ...@@ -23,8 +15,4 @@ scope(controller: :wikis) do
put '/', action: :update put '/', action: :update
delete '/', action: :destroy delete '/', action: :destroy
end end
scope(path: '-/wiki_dirs/*id', as: :wiki_dir, format: false, controller: :wiki_directories) do
get '/', action: :show
end
end end
# frozen_string_literal: true
class AddSnowplowIgluRegistryUrlToApplicationSettings < ActiveRecord::Migration[5.2]
DOWNTIME = false
def change
add_column :application_settings, :snowplow_iglu_registry_url, :string, limit: 255
end
end
# frozen_string_literal: true
class AddIndexToZoomMeetings < ActiveRecord::Migration[5.2]
include Gitlab::Database::MigrationHelpers
DOWNTIME = false
disable_ddl_transaction!
def up
add_concurrent_index :zoom_meetings, :issue_status
end
def down
remove_concurrent_index :zoom_meetings, :issue_status if index_exists?(:zoom_meetings, :issue_status)
end
end
...@@ -338,6 +338,7 @@ ActiveRecord::Schema.define(version: 2019_10_26_124116) do ...@@ -338,6 +338,7 @@ ActiveRecord::Schema.define(version: 2019_10_26_124116) do
t.boolean "throttle_incident_management_notification_enabled", default: false, null: false t.boolean "throttle_incident_management_notification_enabled", default: false, null: false
t.integer "throttle_incident_management_notification_period_in_seconds", default: 3600 t.integer "throttle_incident_management_notification_period_in_seconds", default: 3600
t.integer "throttle_incident_management_notification_per_period", default: 3600 t.integer "throttle_incident_management_notification_per_period", default: 3600
t.string "snowplow_iglu_registry_url", limit: 255
t.integer "push_event_hooks_limit", default: 3, null: false t.integer "push_event_hooks_limit", default: 3, null: false
t.integer "push_event_activities_limit", default: 3, null: false t.integer "push_event_activities_limit", default: 3, null: false
t.string "custom_http_clone_url_root", limit: 511 t.string "custom_http_clone_url_root", limit: 511
...@@ -4041,6 +4042,7 @@ ActiveRecord::Schema.define(version: 2019_10_26_124116) do ...@@ -4041,6 +4042,7 @@ ActiveRecord::Schema.define(version: 2019_10_26_124116) do
t.string "url", limit: 255 t.string "url", limit: 255
t.index ["issue_id", "issue_status"], name: "index_zoom_meetings_on_issue_id_and_issue_status", unique: true, where: "(issue_status = 1)" t.index ["issue_id", "issue_status"], name: "index_zoom_meetings_on_issue_id_and_issue_status", unique: true, where: "(issue_status = 1)"
t.index ["issue_id"], name: "index_zoom_meetings_on_issue_id" t.index ["issue_id"], name: "index_zoom_meetings_on_issue_id"
t.index ["issue_status"], name: "index_zoom_meetings_on_issue_status"
t.index ["project_id"], name: "index_zoom_meetings_on_project_id" t.index ["project_id"], name: "index_zoom_meetings_on_project_id"
end end
......
...@@ -316,6 +316,7 @@ are listed in the descriptions of the relevant settings. ...@@ -316,6 +316,7 @@ are listed in the descriptions of the relevant settings.
| `snowplow_cookie_domain` | string | no | The Snowplow cookie domain. (e.g. `.gitlab.com`) | | `snowplow_cookie_domain` | string | no | The Snowplow cookie domain. (e.g. `.gitlab.com`) |
| `snowplow_enabled` | boolean | no | Enable snowplow tracking. | | `snowplow_enabled` | boolean | no | Enable snowplow tracking. |
| `snowplow_site_id` | string | no | The Snowplow site name / application id. (e.g. `gitlab`) | | `snowplow_site_id` | string | no | The Snowplow site name / application id. (e.g. `gitlab`) |
| `snowplow_iglu_registry_url` | string | no | The Snowplow base Iglu Schema Registry URL to use for custom context and self describing events'|
| `pendo_url` | string | required by: `pendo_enabled` | The Pendo endpoint url with js snippet. (e.g. `https://cdn.pendo.io/agent/static/your-api-key/pendo.js`) | | `pendo_url` | string | required by: `pendo_enabled` | The Pendo endpoint url with js snippet. (e.g. `https://cdn.pendo.io/agent/static/your-api-key/pendo.js`) |
| `pendo_enabled` | boolean | no | Enable pendo tracking. | | `pendo_enabled` | boolean | no | Enable pendo tracking. |
| `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to `0` for unlimited time. | | `terminal_max_session_time` | integer | no | Maximum time for web terminal websocket connection (in seconds). Set to `0` for unlimited time. |
......
...@@ -135,6 +135,7 @@ module API ...@@ -135,6 +135,7 @@ module API
optional :local_markdown_version, type: Integer, desc: 'Local markdown version, increase this value when any cached markdown should be invalidated' optional :local_markdown_version, type: Integer, desc: 'Local markdown version, increase this value when any cached markdown should be invalidated'
optional :allow_local_requests_from_hooks_and_services, type: Boolean, desc: 'Deprecated: Use :allow_local_requests_from_web_hooks_and_services instead. Allow requests to the local network from hooks and services.' # support legacy names, can be removed in v5 optional :allow_local_requests_from_hooks_and_services, type: Boolean, desc: 'Deprecated: Use :allow_local_requests_from_web_hooks_and_services instead. Allow requests to the local network from hooks and services.' # support legacy names, can be removed in v5
optional :snowplow_enabled, type: Grape::API::Boolean, desc: 'Enable Snowplow tracking' optional :snowplow_enabled, type: Grape::API::Boolean, desc: 'Enable Snowplow tracking'
optional :snowplow_iglu_registry_url, type: String, desc: 'The Snowplow base Iglu Schema Registry URL to use for custom context and self describing events'
given snowplow_enabled: ->(val) { val } do given snowplow_enabled: ->(val) { val } do
requires :snowplow_collector_hostname, type: String, desc: 'The Snowplow collector hostname' requires :snowplow_collector_hostname, type: String, desc: 'The Snowplow collector hostname'
optional :snowplow_cookie_domain, type: String, desc: 'The Snowplow cookie domain' optional :snowplow_cookie_domain, type: String, desc: 'The Snowplow cookie domain'
......
...@@ -6,7 +6,7 @@ module Banzai ...@@ -6,7 +6,7 @@ module Banzai
class Rewriter class Rewriter
def initialize(link_string, wiki:, slug:) def initialize(link_string, wiki:, slug:)
@uri = Addressable::URI.parse(link_string) @uri = Addressable::URI.parse(link_string)
@wiki_base_path = wiki && wiki.wiki_page_path @wiki_base_path = wiki && wiki.wiki_base_path
@slug = slug @slug = slug
end end
......
...@@ -47,7 +47,8 @@ module Gitlab ...@@ -47,7 +47,8 @@ module Gitlab
cookie_domain: Gitlab::CurrentSettings.snowplow_cookie_domain, cookie_domain: Gitlab::CurrentSettings.snowplow_cookie_domain,
app_id: Gitlab::CurrentSettings.snowplow_site_id, app_id: Gitlab::CurrentSettings.snowplow_site_id,
form_tracking: additional_features, form_tracking: additional_features,
link_click_tracking: additional_features link_click_tracking: additional_features,
iglu_registry_url: Gitlab::CurrentSettings.snowplow_iglu_registry_url
}.transform_keys! { |key| key.to_s.camelize(:lower).to_sym } }.transform_keys! { |key| key.to_s.camelize(:lower).to_sym }
end end
......
...@@ -78,6 +78,8 @@ module Gitlab ...@@ -78,6 +78,8 @@ module Gitlab
in_review_folder: count(::Environment.in_review_folder), in_review_folder: count(::Environment.in_review_folder),
groups: count(Group), groups: count(Group),
issues: count(Issue), issues: count(Issue),
issues_with_associated_zoom_link: count(ZoomMeeting.added_to_issue),
issues_using_zoom_quick_actions: count(ZoomMeeting.select(:issue_id).distinct),
keys: count(Key), keys: count(Key),
label_lists: count(List.label), label_lists: count(List.label),
lfs_objects: count(LfsObject), lfs_objects: count(LfsObject),
......
...@@ -130,15 +130,5 @@ module Gitlab ...@@ -130,15 +130,5 @@ module Gitlab
IPAddr.new(str) IPAddr.new(str)
rescue IPAddr::InvalidAddressError rescue IPAddr::InvalidAddressError
end end
# Filter a Hash against a mapping of keys to sets of allowed values.
#
# Keys that do not pass the filter will be removed from the Hash.
# This mutates the input hash.
def allow_hash_values(hash, allowed)
allowed.each do |key, allowed_values|
hash.delete(key) if hash.key?(key) && !allowed_values.include?(hash[key])
end
end
end end
end end
...@@ -411,9 +411,6 @@ msgstr "" ...@@ -411,9 +411,6 @@ msgstr ""
msgid "%{verb} %{time_spent_value} spent time." msgid "%{verb} %{time_spent_value} spent time."
msgstr "" msgstr ""
msgid "&middot;"
msgstr ""
msgid "'%{level}' is not a valid visibility level" msgid "'%{level}' is not a valid visibility level"
msgstr "" msgstr ""
...@@ -8848,6 +8845,9 @@ msgstr "" ...@@ -8848,6 +8845,9 @@ msgstr ""
msgid "If your HTTP repository is not publicly accessible, add your credentials." msgid "If your HTTP repository is not publicly accessible, add your credentials."
msgstr "" msgstr ""
msgid "Iglu registry URL (optional)"
msgstr ""
msgid "ImageDiffViewer|2-up" msgid "ImageDiffViewer|2-up"
msgstr "" msgstr ""
...@@ -9543,6 +9543,9 @@ msgstr "" ...@@ -9543,6 +9543,9 @@ msgstr ""
msgid "Kubernetes error: %{error_code}" msgid "Kubernetes error: %{error_code}"
msgstr "" msgstr ""
msgid "Kubernetes popover"
msgstr ""
msgid "LDAP" msgid "LDAP"
msgstr "" msgstr ""
...@@ -18891,10 +18894,7 @@ msgstr "" ...@@ -18891,10 +18894,7 @@ msgstr ""
msgid "Wiki pages" msgid "Wiki pages"
msgstr "" msgstr ""
msgid "Wiki was successfully created" msgid "Wiki was successfully updated."
msgstr ""
msgid "Wiki was successfully updated"
msgstr "" msgstr ""
msgid "WikiClone|Clone your wiki" msgid "WikiClone|Clone your wiki"
...@@ -18912,18 +18912,6 @@ msgstr "" ...@@ -18912,18 +18912,6 @@ msgstr ""
msgid "WikiClone|Start Gollum and edit locally" msgid "WikiClone|Start Gollum and edit locally"
msgstr "" msgstr ""
msgid "WikiDirEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
msgstr ""
msgid "WikiDirEmpty|Create a page"
msgstr ""
msgid "WikiDirEmpty|Create a page in this directory"
msgstr ""
msgid "WikiDirEmpty|This directory has no wiki pages"
msgstr ""
msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title." msgid "WikiEditPageTip|Tip: You can move this page by adding the path to the beginning of the title."
msgstr "" msgstr ""
...@@ -18942,9 +18930,6 @@ msgstr "" ...@@ -18942,9 +18930,6 @@ msgstr ""
msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on." msgid "WikiEmpty|A wiki is where you can store all the details about your project. This can include why you've created it, its principles, how to use it, and so on."
msgstr "" msgstr ""
msgid "WikiEmpty|Create this page"
msgstr ""
msgid "WikiEmpty|Create your first page" msgid "WikiEmpty|Create your first page"
msgstr "" msgstr ""
...@@ -18954,9 +18939,6 @@ msgstr "" ...@@ -18954,9 +18939,6 @@ msgstr ""
msgid "WikiEmpty|The wiki lets you write documentation for your project" msgid "WikiEmpty|The wiki lets you write documentation for your project"
msgstr "" msgstr ""
msgid "WikiEmpty|This page does not exist"
msgstr ""
msgid "WikiEmpty|This project has no wiki pages" msgid "WikiEmpty|This project has no wiki pages"
msgstr "" msgstr ""
...@@ -19011,9 +18993,6 @@ msgstr "" ...@@ -19011,9 +18993,6 @@ msgstr ""
msgid "WikiPage|Write your content or drag files here…" msgid "WikiPage|Write your content or drag files here…"
msgstr "" msgstr ""
msgid "Wiki|Contents"
msgstr ""
msgid "Wiki|Create New Page" msgid "Wiki|Create New Page"
msgstr "" msgstr ""
...@@ -19026,9 +19005,6 @@ msgstr "" ...@@ -19026,9 +19005,6 @@ msgstr ""
msgid "Wiki|Edit Page" msgid "Wiki|Edit Page"
msgstr "" msgstr ""
msgid "Wiki|Hide folder contents"
msgstr ""
msgid "Wiki|More Pages" msgid "Wiki|More Pages"
msgstr "" msgstr ""
...@@ -19047,13 +19023,10 @@ msgstr "" ...@@ -19047,13 +19023,10 @@ msgstr ""
msgid "Wiki|Pages" msgid "Wiki|Pages"
msgstr "" msgstr ""
msgid "Wiki|Show files separately" msgid "Wiki|Title"
msgstr ""
msgid "Wiki|Show folder contents"
msgstr "" msgstr ""
msgid "Wiki|Title" msgid "Wiki|Wiki Pages"
msgstr "" msgstr ""
msgid "Will deploy to" msgid "Will deploy to"
......
...@@ -5,7 +5,7 @@ module QA ...@@ -5,7 +5,7 @@ module QA
module Project module Project
module Wiki module Wiki
class Edit < Page::Base class Edit < Page::Base
view 'app/views/shared/wiki/_main_links.html.haml' do view 'app/views/projects/wikis/_main_links.html.haml' do
element :new_page_link, 'New page' # rubocop:disable QA/ElementWithPattern element :new_page_link, 'New page' # rubocop:disable QA/ElementWithPattern
element :page_history_link, 'Page history' # rubocop:disable QA/ElementWithPattern element :page_history_link, 'Page history' # rubocop:disable QA/ElementWithPattern
element :edit_page_link, 'Edit' # rubocop:disable QA/ElementWithPattern element :edit_page_link, 'Edit' # rubocop:disable QA/ElementWithPattern
......
...@@ -7,7 +7,7 @@ module QA ...@@ -7,7 +7,7 @@ module QA
class New < Page::Base class New < Page::Base
include Component::LazyLoader include Component::LazyLoader
view 'app/views/projects/wiki_pages/_form.html.haml' do view 'app/views/projects/wikis/_form.html.haml' do
element :wiki_title_textbox element :wiki_title_textbox
element :wiki_content_textarea element :wiki_content_textarea
element :wiki_message_textbox element :wiki_message_textbox
......
...@@ -7,11 +7,11 @@ module QA ...@@ -7,11 +7,11 @@ module QA
class Show < Page::Base class Show < Page::Base
include Page::Component::LegacyClonePanel include Page::Component::LegacyClonePanel
view 'app/views/shared/wiki/_page_listing.html.haml' do view 'app/views/projects/wikis/pages.html.haml' do
element :clone_repository_link, 'Clone repository' # rubocop:disable QA/ElementWithPattern element :clone_repository_link, 'Clone repository' # rubocop:disable QA/ElementWithPattern
end end
view 'app/views/projects/wiki_pages/show.html.haml' do view 'app/views/projects/wikis/show.html.haml' do
element :wiki_page_content element :wiki_page_content
end end
......
...@@ -13,7 +13,7 @@ module QA ...@@ -13,7 +13,7 @@ module QA
resource.message = 'Update home' resource.message = 'Update home'
end end
validate_created('My First Wiki Content') validate_content('My First Wiki Content')
Page::Project::Wiki::Edit.perform(&:click_edit) Page::Project::Wiki::Edit.perform(&:click_edit)
Page::Project::Wiki::New.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName Page::Project::Wiki::New.perform do |page| # rubocop:disable QA/AmbiguousPageObjectName
...@@ -21,7 +21,7 @@ module QA ...@@ -21,7 +21,7 @@ module QA
page.save_changes page.save_changes
end end
validate_edited('My Second Wiki Content') validate_content('My Second Wiki Content')
Resource::Repository::WikiPush.fabricate! do |push| Resource::Repository::WikiPush.fabricate! do |push|
push.wiki = wiki push.wiki = wiki
...@@ -34,12 +34,7 @@ module QA ...@@ -34,12 +34,7 @@ module QA
expect(page).to have_content('My Third Wiki Content') expect(page).to have_content('My Third Wiki Content')
end end
def validate_created(content) def validate_content(content)
expect(page).to have_content('Wiki was successfully created')
expect(page).to have_content(/#{content}/)
end
def validate_edited(content)
expect(page).to have_content('Wiki was successfully updated') expect(page).to have_content('Wiki was successfully updated')
expect(page).to have_content(/#{content}/) expect(page).to have_content(/#{content}/)
end end
......
...@@ -26,17 +26,35 @@ def emit_errors(static_analysis) ...@@ -26,17 +26,35 @@ def emit_errors(static_analysis)
end end
end end
tasks = [ def jobs_to_run(node_index, node_total)
%w[bin/rake lint:all], all_tasks = [
%w[bundle exec license_finder], %w[bin/rake lint:all],
%w[yarn run eslint], %w[bundle exec license_finder],
%w[yarn run stylelint], %w[yarn run eslint],
%w[yarn run prettier-all], %w[yarn run stylelint],
%w[bundle exec rubocop --parallel], %w[yarn run prettier-all],
%w[scripts/lint-conflicts.sh], %w[bundle exec rubocop --parallel],
%w[scripts/lint-rugged] %w[scripts/lint-conflicts.sh],
] %w[scripts/lint-rugged]
]
case node_total
when 1
all_tasks
when 2
rake_lint_all, *rest_jobs = all_tasks
case node_index
when 1
[rake_lint_all]
else
rest_jobs
end
else
raise "Parallelization > 2 (currently set to #{node_total}) isn't supported yet!"
end
end
tasks = jobs_to_run((ENV['CI_NODE_INDEX'] || 1).to_i, (ENV['CI_NODE_TOTAL'] || 1).to_i)
static_analysis = Gitlab::Popen::Runner.new static_analysis = Gitlab::Popen::Runner.new
static_analysis.run(tasks) do |cmd, &run| static_analysis.run(tasks) do |cmd, &run|
......
# frozen_string_literal: true
require 'spec_helper'
describe Projects::WikiDirectoriesController do
set(:project) { create(:project, :public, :repository) }
let(:user) { project.owner }
let(:project_wiki) { ProjectWiki.new(project, user) }
let(:wiki) { project_wiki.wiki }
let(:dir_slug) { 'the-directory' }
let(:dir_contents) { [create(:wiki_page)] }
let(:the_dir) { WikiDirectory.new(dir_slug, dir_contents) }
before do
allow(controller).to receive(:find_dir).and_return(the_dir)
sign_in(user)
end
describe 'GET #show' do
let(:show_params) do
{
namespace_id: project.namespace,
project_id: project,
id: dir_slug
}
end
before do
get :show, params: show_params
end
context 'the directory is empty' do
let(:the_dir) { nil }
it { is_expected.to render_template('empty') }
end
context 'the directory does exist' do
it { is_expected.to render_template('show') }
it 'sets the wiki_dir attribute' do
expect(assigns(:wiki_dir)).to eq(the_dir)
end
it 'assigns the wiki pages' do
expect(assigns(:wiki_pages)).to eq(dir_contents)
end
end
end
end
...@@ -4,10 +4,10 @@ require 'spec_helper' ...@@ -4,10 +4,10 @@ require 'spec_helper'
describe Projects::WikisController do describe Projects::WikisController do
let_it_be(:project) { create(:project, :public, :repository) } let_it_be(:project) { create(:project, :public, :repository) }
let_it_be(:user) { project.owner } let(:user) { project.owner }
let_it_be(:project_wiki) { ProjectWiki.new(project, user) } let(:project_wiki) { ProjectWiki.new(project, user) }
let_it_be(:wiki) { project_wiki.wiki } let(:wiki) { project_wiki.wiki }
let_it_be(:wiki_title) { 'page title test' } let(:wiki_title) { 'page title test' }
before do before do
create_page(wiki_title, 'hello world') create_page(wiki_title, 'hello world')
...@@ -19,86 +19,231 @@ describe Projects::WikisController do ...@@ -19,86 +19,231 @@ describe Projects::WikisController do
destroy_page(wiki_title) destroy_page(wiki_title)
end end
describe 'GET #pages' do describe 'GET #new' do
subject do subject { get :new, params: { namespace_id: project.namespace, project_id: project } }
get :pages, params: { namespace_id: project.namespace, project_id: project, id: wiki_title }.merge(extra_params)
it 'redirects to #show and appends a `random_title` param' do
subject
expect(response).to have_http_status(302)
expect(Rails.application.routes.recognize_path(response.redirect_url)).to include(
controller: 'projects/wikis',
action: 'show'
)
expect(response.redirect_url).to match(/\?random_title=true\Z/)
end end
end
let(:extra_params) { {} } describe 'GET #pages' do
subject { get :pages, params: { namespace_id: project.namespace, project_id: project, id: wiki_title } }
it 'does not load the pages content' do it 'does not load the pages content' do
expect(controller).to receive(:load_wiki).and_return(project_wiki) expect(controller).to receive(:load_wiki).and_return(project_wiki)
expect(project_wiki).to receive(:list_pages).twice.and_call_original expect(project_wiki).to receive(:list_pages).twice.and_call_original
subject subject
end end
end
describe 'illegal params' do describe 'GET #history' do
shared_examples :a_bad_request do before do
it do allow(controller)
expect { subject }.to raise_error(ActionController::BadRequest) .to receive(:can?)
end .with(any_args)
end .and_call_original
describe ':sort' do # The :create_wiki permission is irrelevant to reading history.
let(:extra_params) { { sort: 'wibble' } } expect(controller)
.not_to receive(:can?)
.with(anything, :create_wiki, any_args)
it_behaves_like :a_bad_request allow(controller)
end .to receive(:can?)
.with(anything, :read_wiki, any_args)
.and_return(allow_read_wiki)
end
describe ':direction' do shared_examples 'fetching history' do |expected_status|
let(:extra_params) { { direction: 'wibble' } } before do
get :history, params: { namespace_id: project.namespace, project_id: project, id: wiki_title }
end
it_behaves_like :a_bad_request it "returns status #{expected_status}" do
expect(response).to have_http_status(expected_status)
end end
end
describe ':show_children' do it_behaves_like 'fetching history', :ok do
let(:extra_params) { { show_children: 'wibble' } } let(:allow_read_wiki) { true }
it_behaves_like :a_bad_request it 'assigns @page_versions' do
expect(assigns(:page_versions)).to be_present
end end
end end
shared_examples 'sorting-and-nesting' do |sort_key, default_nesting| it_behaves_like 'fetching history', :not_found do
context "the user is sorting by #{sort_key}" do let(:allow_read_wiki) { false }
let(:extra_params) { sort_params.merge(nesting_params) } end
let(:sort_params) { { sort: sort_key } } end
let(:nesting_params) { {} }
describe 'GET #show' do
render_views
let(:random_title) { nil }
subject { get :show, params: { namespace_id: project.namespace, project_id: project, id: id, random_title: random_title } }
context 'when page exists' do
let(:id) { wiki_title }
it 'limits the retrieved pages for the sidebar' do
expect(controller).to receive(:load_wiki).and_return(project_wiki)
expect(project_wiki).to receive(:list_pages).with(limit: 15).and_call_original
subject
expect(response).to have_http_status(:ok)
expect(assigns(:page).title).to eq(wiki_title)
end
context 'when page content encoding is invalid' do
it 'sets flash error' do
allow(controller).to receive(:valid_encoding?).and_return(false)
before do
subject subject
expect(response).to have_http_status(:ok)
expect(flash[:notice]).to eq('The content of this page is not encoded in UTF-8. Edits can only be made via the Git repository.')
end end
end
end
context 'when the page does not exist' do
let(:id) { 'does not exist' }
it "sets nesting to #{default_nesting} by default" do before do
expect(assigns :nesting).to eq default_nesting subject
end
it 'builds a new wiki page with the id as the title' do
expect(assigns(:page).title).to eq(id)
end
context 'when a random_title param is present' do
let(:random_title) { true }
it 'builds a new wiki page with no title' do
expect(assigns(:page).title).to be_empty
end end
end
end
it 'hides children if the default requires it' do context 'when page is a file' do
expect(assigns :show_children).to be(default_nesting != ProjectWiki::NESTING_CLOSED) include WikiHelpers
let(:id) { upload_file_to_wiki(project, user, file_name) }
before do
subject
end
context 'when file is an image' do
let(:file_name) { 'dk.png' }
it 'delivers the image' do
expect(response.headers['Content-Disposition']).to match(/^inline/)
expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end end
ProjectWiki::NESTINGS.each do |nesting| context 'when file is a svg' do
context "the user explicitly passes show_children = #{nesting}" do let(:file_name) { 'unsanitized.svg' }
let(:nesting_params) { { show_children: nesting } }
it 'sets nesting to the provided value' do it 'delivers the image' do
expect(assigns :nesting).to eq nesting expect(response.headers['Content-Disposition']).to match(/^inline/)
end expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end end
end end
end
context 'the user wants children hidden' do context 'when file is a pdf' do
let(:nesting_params) { { show_children: 'hidden' } } let(:file_name) { 'git-cheat-sheet.pdf' }
it 'hides children' do it 'sets the content type to sets the content response headers' do
expect(assigns :show_children).to be false expect(response.headers['Content-Disposition']).to match(/^inline/)
end expect(response.headers[Gitlab::Workhorse::DETECT_HEADER]).to eq "true"
end end
end end
end end
end
describe 'POST #preview_markdown' do
it 'renders json in a correct format' do
post :preview_markdown, params: { namespace_id: project.namespace, project_id: project, id: 'page/path', text: '*Markdown* text' }
expect(json_response.keys).to match_array(%w(body references))
end
end
describe 'GET #edit' do
subject { get(:edit, params: { namespace_id: project.namespace, project_id: project, id: wiki_title }) }
context 'when page content encoding is invalid' do
it 'redirects to show' do
allow(controller).to receive(:valid_encoding?).and_return(false)
subject
expect(response).to redirect_to(project_wiki_path(project, project_wiki.list_pages.first))
end
end
include_examples 'sorting-and-nesting', ProjectWiki::CREATED_AT_ORDER, ProjectWiki::NESTING_FLAT context 'when page content encoding is valid' do
include_examples 'sorting-and-nesting', ProjectWiki::TITLE_ORDER, ProjectWiki::NESTING_CLOSED render_views
it 'shows the edit page' do
subject
expect(response).to have_http_status(:ok)
expect(response.body).to include('Edit Page')
end
end
end
describe 'PATCH #update' do
let(:new_title) { 'New title' }
let(:new_content) { 'New content' }
subject do
patch(:update,
params: {
namespace_id: project.namespace,
project_id: project,
id: wiki_title,
wiki: { title: new_title, content: new_content }
})
end
context 'when page content encoding is invalid' do
it 'redirects to show' do
allow(controller).to receive(:valid_encoding?).and_return(false)
subject
expect(response).to redirect_to(project_wiki_path(project, project_wiki.list_pages.first))
end
end
context 'when page content encoding is valid' do
render_views
it 'updates the page' do
subject
wiki_page = project_wiki.list_pages(load_content: true).first
expect(wiki_page.title).to eq new_title
expect(wiki_page.content).to eq new_content
end
end
end end
def create_page(name, content) def create_page(name, content)
......
...@@ -18,12 +18,12 @@ FactoryBot.define do ...@@ -18,12 +18,12 @@ FactoryBot.define do
association :wiki, factory: :project_wiki, strategy: :build association :wiki, factory: :project_wiki, strategy: :build
initialize_with { new(wiki, page, true) } initialize_with { new(wiki, page, true) }
before(:create) do |wiki_page, evaluator| before(:create) do |page, evaluator|
wiki_page.attributes = evaluator.attrs.with_indifferent_access page.attributes = evaluator.attrs
end end
to_create do |wiki_page| to_create do |page|
wiki_page.create page.create
end end
end end
end end
...@@ -3,9 +3,8 @@ ...@@ -3,9 +3,8 @@
require 'spec_helper' require 'spec_helper'
describe 'Edit Project Settings' do describe 'Edit Project Settings' do
set(:project) { create(:project, :public, :repository) }
let(:member) { create(:user) } let(:member) { create(:user) }
let!(:project) { create(:project, :public, :repository) }
let!(:issue) { create(:issue, project: project) } let!(:issue) { create(:issue, project: project) }
let(:non_member) { create(:user) } let(:non_member) { create(:user) }
...@@ -82,88 +81,85 @@ describe 'Edit Project Settings' do ...@@ -82,88 +81,85 @@ describe 'Edit Project Settings' do
end end
describe 'project features visibility pages' do describe 'project features visibility pages' do
set(:pipeline) { create(:ci_empty_pipeline, project: project) } let(:pipeline) { create(:ci_empty_pipeline, project: project) }
set(:job) { create(:ci_build, pipeline: pipeline) } let(:job) { create(:ci_build, pipeline: pipeline) }
where(:method_name, :build_url) do let(:tools) do
[ {
[:builds, -> { project_job_path(project, job) }], builds: project_job_path(project, job),
[:issues, -> { project_issues_path(project) }], issues: project_issues_path(project),
[:wiki, -> { project_wiki_path(project, :home) }], wiki: project_wiki_path(project, :home),
[:snippets, -> { project_snippets_path(project) }], snippets: project_snippets_path(project),
[:merge_requests, -> { project_merge_requests_path(project) }] merge_requests: project_merge_requests_path(project)
] }
end end
with_them do context 'normal user' do
let(:url) { build_url.call } before do
let(:attr_name) { "#{method_name}_access_level" } sign_in(member)
end
context 'normal user' do
before do
project.team.truncate
sign_in(member)
end
it 'renders 200 if tool is enabled' do
project.project_feature.update_attribute(attr_name, ProjectFeature::ENABLED)
it 'renders 200 if tool is enabled' do
tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::ENABLED)
visit url visit url
expect(page.status_code).to eq(200) expect(page.status_code).to eq(200)
end end
end
it 'renders 404 if feature is disabled' do it 'renders 404 if feature is disabled' do
project.project_feature.update_attribute(attr_name, ProjectFeature::DISABLED) tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::DISABLED)
visit url visit url
expect(page.status_code).to eq(404) expect(page.status_code).to eq(404)
end end
end
it 'renders 404 if feature is enabled only for team members' do it 'renders 404 if feature is enabled only for team members' do
project.project_feature.update_attribute(attr_name, ProjectFeature::PRIVATE) project.team.truncate
tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url visit url
expect(page.status_code).to eq(404) expect(page.status_code).to eq(404)
end end
end
it 'renders 200 if user is member of group' do it 'renders 200 if user is member of group' do
group = create(:group) group = create(:group)
project.group = group project.group = group
project.save project.save
group.add_owner(member)
project.project_feature.update_attribute(attr_name, ProjectFeature::PRIVATE) group.add_owner(member)
tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url visit url
expect(page.status_code).to eq(200) expect(page.status_code).to eq(200)
end end
end end
end
context 'admin user' do context 'admin user' do
before do before do
non_member.update_attribute(:admin, true) non_member.update_attribute(:admin, true)
project.team.truncate sign_in(non_member)
sign_in(non_member) end
end
it 'renders 404 if feature is disabled' do it 'renders 404 if feature is disabled' do
tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::DISABLED) project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::DISABLED)
visit url visit url
expect(page.status_code).to eq(404) expect(page.status_code).to eq(404)
end end
end
it 'renders 200 if feature is enabled only for team members' do it 'renders 200 if feature is enabled only for team members' do
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE) project.team.truncate
tools.each do |method_name, url|
project.project_feature.update_attribute("#{method_name}_access_level", ProjectFeature::PRIVATE)
visit url visit url
expect(page.status_code).to eq(200) expect(page.status_code).to eq(200)
end end
end end
......
...@@ -4,54 +4,164 @@ require 'spec_helper' ...@@ -4,54 +4,164 @@ require 'spec_helper'
describe 'Projects > Wiki > User previews markdown changes', :js do describe 'Projects > Wiki > User previews markdown changes', :js do
set(:user) { create(:user) } set(:user) { create(:user) }
set(:project) { create(:project, :wiki_repo, namespace: user.namespace) } let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:project_wiki) { ProjectWiki.new(project, user) } let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: { title: 'home', content: '[some link](other-page)' }) }
let(:wiki_content) do
<<-HEREDOC
[regular link](regular)
[relative link 1](../relative)
[relative link 2](./relative)
[relative link 3](./e/f/relative)
[spaced link](title with spaces)
HEREDOC
end
before do before do
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
init_home!
end end
def init_home! context "while creating a new wiki page" do
create(:wiki_page, wiki: project.wiki, attrs: { title: 'home', content: '[some link](other-page)' }) context "when there are no spaces or hyphens in the page name" do
end it "rewrites relative links as expected" do
create_wiki_page('a/b/c/d', content: wiki_content)
def fill_in_content! expect(page).to have_content("regular link")
page.within '.wiki-form' do
fill_in :wiki_page_content, with: wiki_content expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end end
end
def show_preview! context "when there are spaces in the page name" do
page.within '.wiki-form' do it "rewrites relative links as expected" do
click_on 'Preview' create_wiki_page('a page/b page/c page/d page', content: wiki_content)
expect(page).to have_content("regular link")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end end
end
context 'when writing a new page' do context "when there are hyphens in the page name" do
let(:new_wiki_path) { 'a/b/c/d' } it "rewrites relative links as expected" do
let(:wiki_content) { 'Some [awesome wiki](content)' } create_wiki_page('a-page/b-page/c-page/d-page', content: wiki_content)
it 'can show a preview of markdown content' do expect(page).to have_content("regular link")
visit project_wiki_pages_new_path(project, id: new_wiki_path)
fill_in_content!
show_preview!
expect(page).to have_link('awesome wiki') expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end end
end end
context 'when editing an existing page' do context "while editing a wiki page" do
let(:wiki_content) { 'Some [bemusing](content)' } context "when there are no spaces or hyphens in the page name" do
let(:wiki_page) { create(:wiki_page, wiki: project_wiki) } it "rewrites relative links as expected" do
create_wiki_page('a/b/c/d')
click_link 'Edit'
fill_in :wiki_content, with: wiki_content
click_on "Preview"
expect(page).to have_content("regular link")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a/b/c/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
context "when there are spaces in the page name" do
it "rewrites relative links as expected" do
create_wiki_page('a page/b page/c page/d page')
click_link 'Edit'
fill_in :wiki_content, with: wiki_content
click_on "Preview"
expect(page).to have_content("regular link")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end
context "when there are hyphens in the page name" do
it "rewrites relative links as expected" do
create_wiki_page('a-page/b-page/c-page/d-page')
click_link 'Edit'
fill_in :wiki_content, with: wiki_content
click_on "Preview"
it 'can show a preview of markdown content, when writing' do expect(page).to have_content("regular link")
visit project_wiki_edit_path(project, wiki_page)
fill_in_content!
show_preview!
expect(page).to have_link('bemusing') expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/regular\">regular link</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/relative\">relative link 1</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/relative\">relative link 2</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/a-page/b-page/c-page/e/f/relative\">relative link 3</a>")
expect(page.html).to include("<a href=\"/#{project.full_path}/wikis/title%20with%20spaces\">spaced link</a>")
end
end end
context 'when rendering the preview' do
it 'renders content with CommonMark' do
create_wiki_page('a-page/b-page/c-page/common-mark')
click_link 'Edit'
fill_in :wiki_content, with: "1. one\n - sublist\n"
click_on "Preview"
# the above generates two separate lists (not embedded) in CommonMark
expect(page).to have_content("sublist")
expect(page).not_to have_xpath("//ol//li//ul")
end
end
end
it "does not linkify double brackets inside code blocks as expected" do
wiki_content = <<-HEREDOC
`[[do_not_linkify]]`
```
[[also_do_not_linkify]]
```
HEREDOC
create_wiki_page('linkify_test', wiki_content)
expect(page).to have_content("do_not_linkify")
expect(page.html).to include('[[do_not_linkify]]')
expect(page.html).to include('[[also_do_not_linkify]]')
end
private
def create_wiki_page(path, content = 'content')
visit project_wiki_path(project, wiki_page)
click_link 'New page'
fill_in :wiki_title, with: path
fill_in :wiki_content, with: content
click_button 'Create page'
end end
end end
...@@ -6,7 +6,6 @@ describe 'User deletes wiki page', :js do ...@@ -6,7 +6,6 @@ describe 'User deletes wiki page', :js do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) } let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:wiki_page) { create(:wiki_page, wiki: project.wiki) } let(:wiki_page) { create(:wiki_page, wiki: project.wiki) }
let(:project_wiki) { ProjectWiki.new(project, user) }
before do before do
sign_in(user) sign_in(user)
...@@ -19,6 +18,5 @@ describe 'User deletes wiki page', :js do ...@@ -19,6 +18,5 @@ describe 'User deletes wiki page', :js do
find('.modal-footer .btn-danger').click find('.modal-footer .btn-danger').click
expect(page).to have_content('Page was successfully deleted') expect(page).to have_content('Page was successfully deleted')
expect(project_wiki.find_page(wiki_page.slug)).to be nil
end end
end end
...@@ -10,13 +10,6 @@ describe 'User updates wiki page' do ...@@ -10,13 +10,6 @@ describe 'User updates wiki page' do
sign_in(user) sign_in(user)
end end
def create_page(attrs = {})
page.within('.wiki-form') do
attrs.each { |k, v| fill_in("wiki_page_#{k}".to_sym, with: v) }
click_on('Create page')
end
end
context 'when wiki is empty' do context 'when wiki is empty' do
before do before do
visit(project_wikis_path(project)) visit(project_wikis_path(project))
...@@ -35,7 +28,12 @@ describe 'User updates wiki page' do ...@@ -35,7 +28,12 @@ describe 'User updates wiki page' do
end end
it 'updates a page that has a path', :js do it 'updates a page that has a path', :js do
create_page(title: 'one/two/three-test', content: 'wiki content') fill_in(:wiki_title, with: 'one/two/three-test')
page.within '.wiki-form' do
fill_in(:wiki_content, with: 'wiki content')
click_on('Create page')
end
expect(current_path).to include('one/two/three-test') expect(current_path).to include('one/two/three-test')
expect(find('.wiki-pages')).to have_content('three') expect(find('.wiki-pages')).to have_content('three')
...@@ -74,9 +72,9 @@ describe 'User updates wiki page' do ...@@ -74,9 +72,9 @@ describe 'User updates wiki page' do
it 'updates a page', :js do it 'updates a page', :js do
# Commit message field should have correct value. # Commit message field should have correct value.
expect(page).to have_field('wiki_page[message]', with: 'Update home') expect(page).to have_field('wiki[message]', with: 'Update home')
fill_in(:wiki_page_content, with: 'My awesome wiki!') fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Save changes') click_button('Save changes')
expect(page).to have_content('Home') expect(page).to have_content('Home')
...@@ -85,31 +83,31 @@ describe 'User updates wiki page' do ...@@ -85,31 +83,31 @@ describe 'User updates wiki page' do
end end
it 'updates the commit message as the title is changed', :js do it 'updates the commit message as the title is changed', :js do
fill_in(:wiki_page_title, with: 'Wiki title') fill_in(:wiki_title, with: 'Wiki title')
expect(page).to have_field('wiki_page[message]', with: 'Update Wiki title') expect(page).to have_field('wiki[message]', with: 'Update Wiki title')
end end
it 'does not allow XSS', :js do it 'does not allow XSS', :js do
fill_in(:wiki_page_title, with: '<script>') fill_in(:wiki_title, with: '<script>')
expect(page).to have_field('wiki_page[message]', with: 'Update &lt;script&gt;') expect(page).to have_field('wiki[message]', with: 'Update &lt;script&gt;')
end end
it 'shows a validation error message' do it 'shows a validation error message' do
fill_in(:wiki_page_content, with: '') fill_in(:wiki_content, with: '')
click_button('Save changes') click_button('Save changes')
expect(page).to have_selector('.wiki-form') expect(page).to have_selector('.wiki-form')
expect(page).to have_content('Edit Page') expect(page).to have_content('Edit Page')
expect(page).to have_content('The form contains the following error:') expect(page).to have_content('The form contains the following error:')
expect(page).to have_content("Content can't be blank") expect(page).to have_content("Content can't be blank")
expect(find('textarea#wiki_page_content').value).to eq('') expect(find('textarea#wiki_content').value).to eq('')
end end
it 'shows the emoji autocompletion dropdown', :js do it 'shows the emoji autocompletion dropdown', :js do
find('#wiki_page_content').native.send_keys('') find('#wiki_content').native.send_keys('')
fill_in(:wiki_page_content, with: ':') fill_in(:wiki_content, with: ':')
expect(page).to have_selector('.atwho-view') expect(page).to have_selector('.atwho-view')
end end
...@@ -145,9 +143,9 @@ describe 'User updates wiki page' do ...@@ -145,9 +143,9 @@ describe 'User updates wiki page' do
it 'updates a page', :js do it 'updates a page', :js do
# Commit message field should have correct value. # Commit message field should have correct value.
expect(page).to have_field('wiki_page[message]', with: 'Update home') expect(page).to have_field('wiki[message]', with: 'Update home')
fill_in(:wiki_page_content, with: 'My awesome wiki!') fill_in(:wiki_content, with: 'My awesome wiki!')
click_button('Save changes') click_button('Save changes')
...@@ -171,43 +169,50 @@ describe 'User updates wiki page' do ...@@ -171,43 +169,50 @@ describe 'User updates wiki page' do
visit(project_wiki_edit_path(project, wiki_page)) visit(project_wiki_edit_path(project, wiki_page))
end end
def edit_title!(title) it 'moves the page to the root folder' do
fill_in(:wiki_page_title, with: title) fill_in(:wiki_title, with: "/#{page_name}")
click_button('Save changes') click_button('Save changes')
end
it 'moves the page to the root folder' do
edit_title!("/#{page_name}")
expect(current_path).to eq(project_wiki_path(project, page_name)) expect(current_path).to eq(project_wiki_path(project, page_name))
end end
it 'moves the page to other dir' do it 'moves the page to other dir' do
new_page_path = "baz/quux/#{page_name}" new_page_dir = "foo1/bar1/#{page_name}"
edit_title!(new_page_path)
fill_in(:wiki_title, with: new_page_dir)
click_button('Save changes')
expect(current_path).to eq(project_wiki_path(project, new_page_path)) expect(current_path).to eq(project_wiki_path(project, new_page_dir))
end end
it 'remains in the same place if title has not changed' do it 'remains in the same place if title has not changed' do
original_path = project_wiki_path(project, wiki_page) original_path = project_wiki_path(project, wiki_page)
edit_title!(page_name)
fill_in(:wiki_title, with: page_name)
click_button('Save changes')
expect(current_path).to eq(original_path) expect(current_path).to eq(original_path)
end end
it 'can be moved to a different dir with a different name' do it 'can be moved to a different dir with a different name' do
new_page_path = "quux/baz/new_page_name" new_page_dir = "foo1/bar1/new_page_name"
edit_title!(new_page_path)
fill_in(:wiki_title, with: new_page_dir)
expect(current_path).to eq(project_wiki_path(project, new_page_path)) click_button('Save changes')
expect(current_path).to eq(project_wiki_path(project, new_page_dir))
end end
it 'can be renamed and moved to the root folder' do it 'can be renamed and moved to the root folder' do
new_name = 'new_page_name' new_name = 'new_page_name'
edit_title!("/#{new_name}") fill_in(:wiki_title, with: "/#{new_name}")
click_button('Save changes')
expect(current_path).to eq(project_wiki_path(project, new_name)) expect(current_path).to eq(project_wiki_path(project, new_name))
end end
...@@ -215,7 +220,9 @@ describe 'User updates wiki page' do ...@@ -215,7 +220,9 @@ describe 'User updates wiki page' do
it 'squishes the title before creating the page' do it 'squishes the title before creating the page' do
new_page_dir = " foo1 / bar1 / #{page_name} " new_page_dir = " foo1 / bar1 / #{page_name} "
edit_title!(new_page_dir) fill_in(:wiki_title, with: new_page_dir)
click_button('Save changes')
expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}")) expect(current_path).to eq(project_wiki_path(project, "foo1/bar1/#{page_name}"))
end end
......
...@@ -8,11 +8,10 @@ describe 'User views a wiki page' do ...@@ -8,11 +8,10 @@ describe 'User views a wiki page' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) } let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:path) { 'image.png' } let(:path) { 'image.png' }
let(:wiki_content) { "Look at this [image](#{path})\n\n ![alt text](#{path})" }
let(:wiki_page) do let(:wiki_page) do
create(:wiki_page, create(:wiki_page,
wiki: project.wiki, wiki: project.wiki,
attrs: { title: 'home', content: wiki_content }) attrs: { title: 'home', content: "Look at this [image](#{path})\n\n ![alt text](#{path})" })
end end
before do before do
...@@ -20,18 +19,17 @@ describe 'User views a wiki page' do ...@@ -20,18 +19,17 @@ describe 'User views a wiki page' do
sign_in(user) sign_in(user)
end end
def create_page(attrs = {})
page.within('.wiki-form') do
attrs.each { |k, v| fill_in("wiki_page_#{k}".to_sym, with: v) }
click_on('Create page')
end
end
context 'when wiki is empty' do context 'when wiki is empty' do
before do before do
visit(project_wikis_path(project)) visit(project_wikis_path(project))
click_link "Create your first page" click_link "Create your first page"
create_page(title: 'one/two/three-test', content: 'wiki content')
fill_in(:wiki_title, with: 'one/two/three-test')
page.within('.wiki-form') do
fill_in(:wiki_content, with: 'wiki content')
click_on('Create page')
end
end end
it 'shows the history of a page that has a path', :js do it 'shows the history of a page that has a path', :js do
...@@ -85,27 +83,24 @@ describe 'User views a wiki page' do ...@@ -85,27 +83,24 @@ describe 'User views a wiki page' do
context 'shows a file stored in a page' do context 'shows a file stored in a page' do
let(:path) { upload_file_to_wiki(project, user, 'dk.png') } let(:path) { upload_file_to_wiki(project, user, 'dk.png') }
let(:image_path) { project_wiki_path(project, path) }
it do it do
expect(page).to have_xpath("//img[@data-src='#{image_path}']") expect(page).to have_xpath("//img[@data-src='#{project.wiki.wiki_base_path}/#{path}']")
expect(page).to have_link('image', href: "#{image_path}") expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
click_on('image') click_on('image')
expect(current_path).to match(path) expect(current_path).to match("wikis/#{path}")
expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved expect(page).not_to have_xpath('/html') # Page should render the image which means there is no html involved
end end
end end
it 'shows the creation page if file does not exist' do it 'shows the creation page if file does not exist' do
href = project_wiki_path(project, path) expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/#{path}")
expect(page).to have_link('image', href: href)
click_on('image') click_on('image')
expect(current_path).to match(href) expect(current_path).to match("wikis/#{path}")
expect(page).to have_content('Create New Page') expect(page).to have_content('Create New Page')
end end
end end
......
...@@ -7,7 +7,6 @@ describe 'User views wiki pages' do ...@@ -7,7 +7,6 @@ describe 'User views wiki pages' do
let(:user) { create(:user) } let(:user) { create(:user) }
let(:project) { create(:project, :wiki_repo, namespace: user.namespace) } let(:project) { create(:project, :wiki_repo, namespace: user.namespace) }
let(:project_wiki) { ProjectWiki.new(project, user) }
let!(:wiki_page1) do let!(:wiki_page1) do
create(:wiki_page, wiki: project.wiki, attrs: { title: '3 home', content: '3' }) create(:wiki_page, wiki: project.wiki, attrs: { title: '3 home', content: '3' })
...@@ -18,182 +17,73 @@ describe 'User views wiki pages' do ...@@ -18,182 +17,73 @@ describe 'User views wiki pages' do
let!(:wiki_page3) do let!(:wiki_page3) do
create(:wiki_page, wiki: project.wiki, attrs: { title: '2 home', content: '2' }) create(:wiki_page, wiki: project.wiki, attrs: { title: '2 home', content: '2' })
end end
let!(:wiki_page4) do
create(:wiki_page, wiki: project.wiki, attrs: { title: 'sub-folder/0', content: 'a' })
end
let!(:wiki_page5) do
create(:wiki_page, wiki: project.wiki, attrs: { title: 'sub-folder/b', content: 'b' })
end
let(:page_link_selector) { 'a' }
let(:pages) do let(:pages) do
page.all(".wiki-pages-list li #{page_link_selector}") page.find('.wiki-pages-list').all('li').map { |li| li.find('a') }
end end
let(:wikis_allow_change_nesting) { false }
before do before do
stub_feature_flags(wikis_allow_change_nesting: wikis_allow_change_nesting)
project.add_maintainer(user) project.add_maintainer(user)
sign_in(user) sign_in(user)
visit(project_wikis_pages_path(project)) visit(project_wikis_pages_path(project))
end end
def sort_desc!
page.within('.wiki-sort-dropdown') do
page.find('.qa-reverse-sort').click
end
end
def sort_by_created_at!
page.within('.wiki-sort-dropdown') do
click_button('Title')
click_link('Created date')
end
end
shared_examples 'correctly_sorted_pages' do
it 'has pages displayed in correct order' do
displayed_texts = pages.map(&:text)
expect(displayed_texts).to eq expected_sequence.map(&:title)
end
end
context 'ordered by title' do context 'ordered by title' do
let(:sub_folder) { project_wiki.find_dir('sub-folder') } let(:pages_ordered_by_title) { [wiki_page2, wiki_page3, wiki_page1] }
context 'default display settings' do context 'asc' do
context 'asc' do it 'pages are displayed in direct order' do
let(:expected_sequence) { [wiki_page2, wiki_page3, wiki_page1, sub_folder] } pages.each.with_index do |page_title, index|
expect(page_title.text).to eq(pages_ordered_by_title[index].title)
it_behaves_like 'correctly_sorted_pages'
end
context 'desc' do
before do
sort_desc!
end end
let(:expected_sequence) { [sub_folder, wiki_page1, wiki_page3, wiki_page2] }
it_behaves_like 'correctly_sorted_pages'
end
end
context 'changing nesting is disabled' do
let(:wikis_allow_change_nesting) { false }
it 'does not display a nesting controller' do
expect(page).not_to have_css('.wiki-nesting-dropdown')
end end
end end
context 'changing nesting is enabled' do context 'desc' do
let(:wikis_allow_change_nesting) { true } before do
page.within('.wiki-sort-dropdown') do
it 'displays a nesting controller' do page.find('.rspec-reverse-sort').click
expect(page).to have_css('.wiki-nesting-dropdown')
end
context 'tree' do
before do
page.within('.wiki-nesting-dropdown') do
click_link 'Show folder contents'
end
end
context 'asc' do
let(:expected_sequence) { [wiki_page2, wiki_page3, wiki_page1, sub_folder, wiki_page4, wiki_page5] }
it_behaves_like 'correctly_sorted_pages'
end
context 'desc' do
before do
sort_desc!
end
let(:expected_sequence) { [sub_folder, wiki_page5, wiki_page4, wiki_page1, wiki_page3, wiki_page2] }
it_behaves_like 'correctly_sorted_pages'
end
end
context 'nested' do
before do
page.within('.wiki-nesting-dropdown') do
click_link 'Hide folder contents'
end
end
context 'asc' do
let(:expected_sequence) { [wiki_page2, wiki_page3, wiki_page1, sub_folder] }
it_behaves_like 'correctly_sorted_pages'
end
context 'desc' do
before do
sort_desc!
end
let(:expected_sequence) { [sub_folder, wiki_page1, wiki_page3, wiki_page2] }
it_behaves_like 'correctly_sorted_pages'
end end
end end
context 'flat' do it 'pages are displayed in reversed order' do
before do pages.reverse_each.with_index do |page_title, index|
page.within('.wiki-nesting-dropdown') do expect(page_title.text).to eq(pages_ordered_by_title[index].title)
click_link 'Show files separately'
end
end
let(:page_link_selector) { 'a.wiki-page-title' }
context 'asc' do
let(:expected_sequence) { [wiki_page2, wiki_page3, wiki_page1, wiki_page4, wiki_page5] }
it_behaves_like 'correctly_sorted_pages'
end
context 'desc' do
before do
sort_desc!
end
let(:expected_sequence) { [wiki_page5, wiki_page4, wiki_page1, wiki_page3, wiki_page2] }
it_behaves_like 'correctly_sorted_pages'
end end
end end
end end
end end
context 'ordered by created_at' do context 'ordered by created_at' do
let(:pages_ordered_by_created_at) { [wiki_page1, wiki_page2, wiki_page3, wiki_page4, wiki_page5] } let(:pages_ordered_by_created_at) { [wiki_page1, wiki_page2, wiki_page3] }
before do before do
sort_by_created_at! page.within('.wiki-sort-dropdown') do
click_button('Title')
click_link('Created date')
end
end end
let(:page_link_selector) { 'a.wiki-page-title' }
context 'asc' do context 'asc' do
let(:expected_sequence) { [wiki_page1, wiki_page2, wiki_page3, wiki_page4, wiki_page5] } it 'pages are displayed in direct order' do
pages.each.with_index do |page_title, index|
it_behaves_like 'correctly_sorted_pages' expect(page_title.text).to eq(pages_ordered_by_created_at[index].title)
end
end
end end
context 'desc' do context 'desc' do
before do before do
sort_desc! page.within('.wiki-sort-dropdown') do
page.find('.rspec-reverse-sort').click
end
end end
let(:expected_sequence) { [wiki_page5, wiki_page4, wiki_page3, wiki_page2, wiki_page1] } it 'pages are displayed in reversed order' do
pages.reverse_each.with_index do |page_title, index|
it_behaves_like 'correctly_sorted_pages' expect(page_title.text).to eq(pages_ordered_by_created_at[index].title)
end
end
end end
end end
end end
...@@ -3,27 +3,27 @@ import { setHTMLFixture } from './helpers/fixtures'; ...@@ -3,27 +3,27 @@ import { setHTMLFixture } from './helpers/fixtures';
describe('Wikis', () => { describe('Wikis', () => {
describe('setting the commit message when the title changes', () => { describe('setting the commit message when the title changes', () => {
const editFormHtmlFixture = args => `<form class="wiki-form ${
args.newPage ? 'js-new-wiki-page' : ''
}">
<input type="text" id="wiki_title" value="My title" />
<input type="text" id="wiki_message" />
</form>`;
let wikis; let wikis;
let titleInput; let titleInput;
let messageInput; let messageInput;
const CREATE = true;
const UPDATE = false;
const editFormHtmlFixture = newPage => describe('when the wiki page is being created', () => {
`<form class="wiki-form ${newPage ? 'js-new-wiki-page' : ''}"> const formHtmlFixture = editFormHtmlFixture({ newPage: true });
<input type="text" id="wiki_page_title" value="My title" />
<input type="text" id="wiki_page_message" />
</form>`;
const init = newPage => { beforeEach(() => {
setHTMLFixture(editFormHtmlFixture(newPage)); setHTMLFixture(formHtmlFixture);
titleInput = document.getElementById('wiki_page_title');
messageInput = document.getElementById('wiki_page_message');
wikis = new Wikis();
};
describe('when the wiki page is being created', () => { titleInput = document.getElementById('wiki_title');
beforeEach(() => init(CREATE)); messageInput = document.getElementById('wiki_message');
wikis = new Wikis();
});
it('binds an event listener to the title input', () => { it('binds an event listener to the title input', () => {
wikis.handleWikiTitleChange = jest.fn(); wikis.handleWikiTitleChange = jest.fn();
...@@ -51,7 +51,15 @@ describe('Wikis', () => { ...@@ -51,7 +51,15 @@ describe('Wikis', () => {
}); });
describe('when the wiki page is being updated', () => { describe('when the wiki page is being updated', () => {
beforeEach(() => init(UPDATE)); const formHtmlFixture = editFormHtmlFixture({ newPage: false });
beforeEach(() => {
setHTMLFixture(formHtmlFixture);
titleInput = document.getElementById('wiki_title');
messageInput = document.getElementById('wiki_message');
wikis = new Wikis();
});
it('sets the commit message when title changes, prefixing with "Update"', () => { it('sets the commit message when title changes, prefixing with "Update"', () => {
titleInput.value = 'My title'; titleInput.value = 'My title';
......
...@@ -23,13 +23,8 @@ describe WikiHelper do ...@@ -23,13 +23,8 @@ describe WikiHelper do
describe '#wiki_sort_controls' do describe '#wiki_sort_controls' do
let(:project) { create(:project) } let(:project) { create(:project) }
let(:classes) { described_class::WIKI_SORT_CSS_CLASSES } let(:wiki_link) { helper.wiki_sort_controls(project, sort, direction) }
let(:classes) { "btn btn-default has-tooltip reverse-sort-btn qa-reverse-sort rspec-reverse-sort" }
subject(:wiki_link) do
helper.wiki_sort_controls(sort: sort, direction: direction) do |opts|
project_wikis_pages_path(project, opts)
end
end
def expected_link(sort, direction, icon_class) def expected_link(sort, direction, icon_class)
path = "/#{project.full_path}/wikis/pages?direction=#{direction}&sort=#{sort}" path = "/#{project.full_path}/wikis/pages?direction=#{direction}&sort=#{sort}"
...@@ -67,18 +62,6 @@ describe WikiHelper do ...@@ -67,18 +62,6 @@ describe WikiHelper do
end end
end end
describe '#wiki_show_children_icon' do
ProjectWiki::NESTINGS.each do |nesting|
context "When the nesting parameter is `#{nesting}`" do
let(:element) { helper.wiki_show_children_icon(nesting) }
it 'produces something that contains an SVG' do
expect(element).to match(/svg/)
end
end
end
end
describe '#wiki_sort_title' do describe '#wiki_sort_title' do
it 'returns a title corresponding to a key' do it 'returns a title corresponding to a key' do
expect(helper.wiki_sort_title('created_at')).to eq('Created date') expect(helper.wiki_sort_title('created_at')).to eq('Created date')
......
...@@ -11,10 +11,6 @@ describe Banzai::Filter::WikiLinkFilter do ...@@ -11,10 +11,6 @@ describe Banzai::Filter::WikiLinkFilter do
let(:wiki) { ProjectWiki.new(project, user) } let(:wiki) { ProjectWiki.new(project, user) }
let(:repository_upload_folder) { Wikis::CreateAttachmentService::ATTACHMENT_PATH } let(:repository_upload_folder) { Wikis::CreateAttachmentService::ATTACHMENT_PATH }
def upload_href(file_name)
::File.join(wiki.wiki_page_path, repository_upload_folder, file_name)
end
it "doesn't rewrite absolute links" do it "doesn't rewrite absolute links" do
filtered_link = filter("<a href='http://example.com:8000/'>Link</a>", project_wiki: wiki).children[0] filtered_link = filter("<a href='http://example.com:8000/'>Link</a>", project_wiki: wiki).children[0]
...@@ -32,12 +28,12 @@ describe Banzai::Filter::WikiLinkFilter do ...@@ -32,12 +28,12 @@ describe Banzai::Filter::WikiLinkFilter do
it 'rewrites links' do it 'rewrites links' do
filtered_link = filter("<a href='#{repository_upload_folder}/a.test'>Link</a>", project_wiki: wiki).children[0] filtered_link = filter("<a href='#{repository_upload_folder}/a.test'>Link</a>", project_wiki: wiki).children[0]
expect(filtered_link.attribute('href').value).to eq(upload_href "a.test") expect(filtered_link.attribute('href').value).to eq("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.test")
end end
end end
context 'with "img" html tag' do context 'with "img" html tag' do
let(:path) { upload_href "a.jpg" } let(:path) { "#{wiki.wiki_base_path}/#{repository_upload_folder}/a.jpg" }
context 'inside an "a" html tag' do context 'inside an "a" html tag' do
it 'rewrites links' do it 'rewrites links' do
...@@ -61,7 +57,7 @@ describe Banzai::Filter::WikiLinkFilter do ...@@ -61,7 +57,7 @@ describe Banzai::Filter::WikiLinkFilter do
it 'rewrites links' do it 'rewrites links' do
filtered_link = filter("<video src='#{repository_upload_folder}/a.mp4'></video>", project_wiki: wiki).children[0] filtered_link = filter("<video src='#{repository_upload_folder}/a.mp4'></video>", project_wiki: wiki).children[0]
expect(filtered_link.attribute('src').value).to eq(upload_href "a.mp4") expect(filtered_link.attribute('src').value).to eq("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.mp4")
end end
end end
...@@ -69,8 +65,7 @@ describe Banzai::Filter::WikiLinkFilter do ...@@ -69,8 +65,7 @@ describe Banzai::Filter::WikiLinkFilter do
it 'rewrites links' do it 'rewrites links' do
filtered_link = filter("<audio src='#{repository_upload_folder}/a.wav'></audio>", project_wiki: wiki).children[0] filtered_link = filter("<audio src='#{repository_upload_folder}/a.wav'></audio>", project_wiki: wiki).children[0]
# expect(filtered_link.attribute('src').value).to eq("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.wav") expect(filtered_link.attribute('src').value).to eq("#{wiki.wiki_base_path}/#{repository_upload_folder}/a.wav")
expect(filtered_link.attribute('src').value).to eq(upload_href "a.wav")
end end
end end
end end
......
...@@ -9,6 +9,7 @@ describe Gitlab::Tracking do ...@@ -9,6 +9,7 @@ describe Gitlab::Tracking do
stub_application_setting(snowplow_collector_hostname: 'gitfoo.com') stub_application_setting(snowplow_collector_hostname: 'gitfoo.com')
stub_application_setting(snowplow_cookie_domain: '.gitfoo.com') stub_application_setting(snowplow_cookie_domain: '.gitfoo.com')
stub_application_setting(snowplow_site_id: '_abc123_') stub_application_setting(snowplow_site_id: '_abc123_')
stub_application_setting(snowplow_iglu_registry_url: 'https://example.org')
end end
describe '.snowplow_options' do describe '.snowplow_options' do
...@@ -19,7 +20,8 @@ describe Gitlab::Tracking do ...@@ -19,7 +20,8 @@ describe Gitlab::Tracking do
cookieDomain: '.gitfoo.com', cookieDomain: '.gitfoo.com',
appId: '_abc123_', appId: '_abc123_',
formTracking: true, formTracking: true,
linkClickTracking: true linkClickTracking: true,
igluRegistryUrl: 'https://example.org'
} }
expect(subject.snowplow_options(nil)).to match(expected_fields) expect(subject.snowplow_options(nil)).to match(expected_fields)
......
...@@ -150,7 +150,7 @@ describe Gitlab::UrlBuilder do ...@@ -150,7 +150,7 @@ describe Gitlab::UrlBuilder do
wiki_page = build(:wiki_page) wiki_page = build(:wiki_page)
url = described_class.build(wiki_page) url = described_class.build(wiki_page)
expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_page_path}/#{wiki_page.slug}" expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}"
end end
end end
end end
......
...@@ -19,7 +19,11 @@ describe Gitlab::UsageData do ...@@ -19,7 +19,11 @@ describe Gitlab::UsageData do
create(:service, project: projects[2], type: 'SlackService', active: true) create(:service, project: projects[2], type: 'SlackService', active: true)
create(:project_error_tracking_setting, project: projects[0]) create(:project_error_tracking_setting, project: projects[0])
create(:project_error_tracking_setting, project: projects[1], enabled: false) create(:project_error_tracking_setting, project: projects[1], enabled: false)
create_list(:issue, 4, project: projects[0])
create(:zoom_meeting, project: projects[0], issue: projects[0].issues[0], issue_status: :added)
create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[1], issue_status: :removed)
create(:zoom_meeting, project: projects[0], issue: projects[0].issues[2], issue_status: :added)
create_list(:zoom_meeting, 2, project: projects[0], issue: projects[0].issues[2], issue_status: :removed)
gcp_cluster = create(:cluster, :provided_by_gcp) gcp_cluster = create(:cluster, :provided_by_gcp)
create(:cluster, :provided_by_user) create(:cluster, :provided_by_user)
create(:cluster, :provided_by_user, :disabled) create(:cluster, :provided_by_user, :disabled)
...@@ -125,6 +129,8 @@ describe Gitlab::UsageData do ...@@ -125,6 +129,8 @@ describe Gitlab::UsageData do
in_review_folder in_review_folder
groups groups
issues issues
issues_with_associated_zoom_link
issues_using_zoom_quick_actions
keys keys
label_lists label_lists
labels labels
...@@ -176,6 +182,8 @@ describe Gitlab::UsageData do ...@@ -176,6 +182,8 @@ describe Gitlab::UsageData do
expect(count_data[:projects_slack_slash_active]).to eq(1) expect(count_data[:projects_slack_slash_active]).to eq(1)
expect(count_data[:projects_with_repositories_enabled]).to eq(3) expect(count_data[:projects_with_repositories_enabled]).to eq(3)
expect(count_data[:projects_with_error_tracking_enabled]).to eq(1) expect(count_data[:projects_with_error_tracking_enabled]).to eq(1)
expect(count_data[:issues_with_associated_zoom_link]).to eq(2)
expect(count_data[:issues_using_zoom_quick_actions]).to eq(3)
expect(count_data[:clusters_enabled]).to eq(7) expect(count_data[:clusters_enabled]).to eq(7)
expect(count_data[:project_clusters_enabled]).to eq(6) expect(count_data[:project_clusters_enabled]).to eq(6)
......
...@@ -252,41 +252,4 @@ describe Gitlab::Utils do ...@@ -252,41 +252,4 @@ describe Gitlab::Utils do
expect(described_class.string_to_ip_object('1:0:0:0:0:0:0:0/124')).to eq(IPAddr.new('1:0:0:0:0:0:0:0/124')) expect(described_class.string_to_ip_object('1:0:0:0:0:0:0:0/124')).to eq(IPAddr.new('1:0:0:0:0:0:0:0/124'))
end end
end end
describe '.allow_hash_values' do
it 'removes keys that do not pass the inclusion filters' do
symbols = %i[x y z]
ints = (0..100)
strings = %w[foo bar baz].to_set
hash = {
a: :x,
b: 100,
c: 'foo',
d: :irrelevant,
aa: :w,
bb: 200,
cc: 'food',
dd: :totally_irrelevant
}
allowed = {
a: symbols,
b: ints,
c: strings,
aa: symbols,
bb: ints,
cc: strings
}
described_class.allow_hash_values(hash, allowed)
expect(hash).to eq({
a: :x,
b: 100,
c: 'foo',
d: :irrelevant,
dd: :totally_irrelevant
})
end
end
end end
...@@ -72,10 +72,14 @@ describe ApplicationSetting do ...@@ -72,10 +72,14 @@ describe ApplicationSetting do
it { is_expected.not_to allow_value(nil).for(:snowplow_collector_hostname) } it { is_expected.not_to allow_value(nil).for(:snowplow_collector_hostname) }
it { is_expected.to allow_value("snowplow.gitlab.com").for(:snowplow_collector_hostname) } it { is_expected.to allow_value("snowplow.gitlab.com").for(:snowplow_collector_hostname) }
it { is_expected.not_to allow_value('/example').for(:snowplow_collector_hostname) } it { is_expected.not_to allow_value('/example').for(:snowplow_collector_hostname) }
it { is_expected.to allow_value('https://example.org').for(:snowplow_iglu_registry_url) }
it { is_expected.not_to allow_value('not-a-url').for(:snowplow_iglu_registry_url) }
it { is_expected.to allow_value(nil).for(:snowplow_iglu_registry_url) }
end end
context 'when snowplow is not enabled' do context 'when snowplow is not enabled' do
it { is_expected.to allow_value(nil).for(:snowplow_collector_hostname) } it { is_expected.to allow_value(nil).for(:snowplow_collector_hostname) }
it { is_expected.to allow_value(nil).for(:snowplow_iglu_registry_url) }
end end
context 'when pendo is enabled' do context 'when pendo is enabled' do
......
...@@ -515,6 +515,48 @@ describe Environment, :use_clean_rails_memory_store_caching do ...@@ -515,6 +515,48 @@ describe Environment, :use_clean_rails_memory_store_caching do
end end
end end
describe '#last_visible_deployment' do
subject { environment.last_visible_deployment }
before do
allow_any_instance_of(Deployment).to receive(:create_ref)
end
context 'when there is an old deployment record' do
let!(:previous_deployment) { create(:deployment, :success, environment: environment) }
context 'when there is a deployment record with created status' do
let!(:deployment) { create(:deployment, environment: environment) }
it { is_expected.to eq(previous_deployment) }
end
context 'when there is a deployment record with running status' do
let!(:deployment) { create(:deployment, :running, environment: environment) }
it { is_expected.to eq(deployment) }
end
context 'when there is a deployment record with success status' do
let!(:deployment) { create(:deployment, :success, environment: environment) }
it { is_expected.to eq(deployment) }
end
context 'when there is a deployment record with failed status' do
let!(:deployment) { create(:deployment, :failed, environment: environment) }
it { is_expected.to eq(deployment) }
end
context 'when there is a deployment record with canceled status' do
let!(:deployment) { create(:deployment, :canceled, environment: environment) }
it { is_expected.to eq(deployment) }
end
end
end
describe '#has_terminals?' do describe '#has_terminals?' do
subject { environment.has_terminals? } subject { environment.has_terminals? }
......
...@@ -28,9 +28,7 @@ describe ProjectWiki do ...@@ -28,9 +28,7 @@ describe ProjectWiki do
describe '#web_url' do describe '#web_url' do
it 'returns the full web URL to the wiki' do it 'returns the full web URL to the wiki' do
home_url = Gitlab::Routing.url_helpers.project_wiki_url(project, :home) expect(subject.web_url).to eq("#{Gitlab.config.gitlab.url}/#{project.full_path}/wikis/home")
expect(subject.web_url).to eq(home_url)
end end
end end
...@@ -73,23 +71,9 @@ describe ProjectWiki do ...@@ -73,23 +71,9 @@ describe ProjectWiki do
describe "#wiki_base_path" do describe "#wiki_base_path" do
it "returns the wiki base path" do it "returns the wiki base path" do
wiki_path = Gitlab::Routing.url_helpers.project_wikis_path(project) wiki_base_path = "#{Gitlab.config.gitlab.relative_url_root}/#{project.full_path}/wikis"
expect(subject.wiki_base_path).to eq(wiki_path)
end
end
describe "#wiki_page_path" do expect(subject.wiki_base_path).to eq(wiki_base_path)
let(:page) { create(:wiki_page, wiki: project_wiki) }
describe 'suffixed with /:page_slug' do
subject { "#{project_wiki.wiki_page_path}/#{page.slug}" }
it "equals the project_wiki_path" do
path = Gitlab::Routing.url_helpers.project_wiki_path(project, page)
expect(subject).to eq(path)
end
end end
end end
......
# frozen_string_literal: true # frozen_string_literal: true
require 'spec_helper' require 'spec_helper'
require 'set'
RSpec.describe WikiDirectory do RSpec.describe WikiDirectory do
include GitHelpers
let(:project) { create(:project, :wiki_repo) }
let(:user) { project.owner }
let(:wiki) { ProjectWiki.new(project, user) }
describe 'validations' do describe 'validations' do
subject { build(:wiki_directory) } subject { build(:wiki_directory) }
it { is_expected.to validate_presence_of(:slug) } it { is_expected.to validate_presence_of(:slug) }
end end
describe '.group_by_directory' do
context 'when there are no pages' do
it 'returns an empty array' do
expect(described_class.group_by_directory(nil)).to eq([])
expect(described_class.group_by_directory([])).to eq([])
end
end
context 'when there are pages' do
before do
create_page('dir_1/dir_1_1/page_3', 'content')
create_page('page_1', 'content')
create_page('dir_1/page_2', 'content')
create_page('dir_2', 'page with dir name')
create_page('dir_2/page_5', 'content')
create_page('page_6', 'content')
create_page('dir_2/page_4', 'content')
end
let(:page_1) { wiki.find_page('page_1') }
let(:page_6) { wiki.find_page('page_6') }
let(:page_dir_2) { wiki.find_page('dir_2') }
let(:dir_1) do
described_class.new('dir_1', [wiki.find_page('dir_1/page_2')])
end
let(:dir_1_1) do
described_class.new('dir_1/dir_1_1', [wiki.find_page('dir_1/dir_1_1/page_3')])
end
let(:dir_2) do
pages = [wiki.find_page('dir_2/page_5'),
wiki.find_page('dir_2/page_4')]
described_class.new('dir_2', pages)
end
context "#list_pages" do
shared_examples "a correct grouping" do
let(:grouped_slugs) { grouped_entries.map(&method(:slugs)) }
let(:expected_slugs) { expected_grouped_entries.map(&method(:slugs)).map(&method(:match_array)) }
it 'returns an array with pages and directories' do
expect(grouped_slugs).to match_array(expected_slugs)
end
end
context 'sort by title' do
let(:grouped_entries) { described_class.group_by_directory(wiki.list_pages) }
let(:expected_grouped_entries) { [dir_1_1, dir_1, page_dir_2, dir_2, page_1, page_6] }
it_behaves_like "a correct grouping"
end
context 'sort by created_at' do
let(:grouped_entries) { described_class.group_by_directory(wiki.list_pages(sort: 'created_at')) }
let(:expected_grouped_entries) { [dir_1_1, page_1, dir_1, page_dir_2, dir_2, page_6] }
it_behaves_like "a correct grouping"
end
it 'returns an array with retained order with directories at the top' do
expected_order = ['dir_1/dir_1_1/page_3', 'dir_1/page_2', 'dir_2', 'dir_2/page_4', 'dir_2/page_5', 'page_1', 'page_6']
grouped_entries = described_class.group_by_directory(wiki.list_pages)
actual_order = grouped_entries.flat_map(&method(:slugs))
expect(actual_order).to eq(expected_order)
end
end
end
end
describe '#initialize' do describe '#initialize' do
context 'when there are pages' do context 'when there are pages' do
let(:pages) { [build(:wiki_page)] } let(:pages) { [build(:wiki_page)] }
...@@ -120,112 +40,7 @@ RSpec.describe WikiDirectory do ...@@ -120,112 +40,7 @@ RSpec.describe WikiDirectory do
it 'returns the relative path to the partial to be used' do it 'returns the relative path to the partial to be used' do
directory = build(:wiki_directory) directory = build(:wiki_directory)
expect(directory.to_partial_path).to eq('projects/wiki_directories/wiki_directory') expect(directory.to_partial_path).to eq('projects/wikis/wiki_directory')
end
end
describe 'attributes' do
def page_path(index)
"dir-path/page-#{index}"
end
let(:page_paths) { (1..3).map { |n| page_path(n) } }
let(:pages) do
page_paths.map { |p| wiki.find_page(p) }
end end
subject { described_class.new('dir-path', pages) }
context 'there are no pages' do
let(:pages) { [] }
it { is_expected.to have_attributes(page_count: 0, last_version: be_nil) }
end
context 'there is one page' do
before do
create_page("dir-path/singleton", "Just this page")
end
let(:the_page) { wiki.find_page("dir-path/singleton") }
let(:pages) { [the_page] }
it { is_expected.to have_attributes(page_count: 1, last_version: the_page.last_version) }
end
context 'there are a few pages, each with a single version' do
before do
page_paths.each_with_index do |path, n|
Timecop.freeze(Time.local(1990) + n.minutes) do
create_page(path, "this is page #{n}")
end
end
end
let(:expected_last_version) { pages.last.last_version }
it { is_expected.to have_attributes(page_count: 3, last_version: expected_last_version) }
end
context 'there are a few pages, each with a few versions' do
before do
page_paths.each_with_index do |path, n|
t = Time.local(1990) + n.minutes
Timecop.freeze(t) do
create_page(path, "This is page #{n}")
(2..3).each do |v|
Timecop.freeze(t + v.seconds) do
update_page(path, "Now at version #{v}")
end
end
end
end
end
it { is_expected.to have_attributes(page_count: 3, last_version: pages.last.last_version) }
end
end
private
def create_page(name, content)
wiki.wiki.write_page(name, :markdown, content, commit_details)
set_time(name)
end
def update_page(name, content)
wiki.wiki.update_page(name, name, :markdown, content, update_commit_details)
set_time(name)
end
def set_time(name)
return unless Timecop.frozen?
new_date = Time.now
page = wiki.find_page(name).page
commit = page.version.commit
repo = commit.instance_variable_get(:@repository)
rug_commit = rugged_repo_at_path(repo.relative_path).lookup(commit.id)
rug_commit.amend(
message: rug_commit.message,
tree: rug_commit.tree,
author: rug_commit.author.merge(time: new_date),
committer: rug_commit.committer.merge(time: new_date),
update_ref: 'HEAD'
)
end
def commit_details
Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.email, "test commit")
end
def update_commit_details
Gitlab::Git::Wiki::CommitDetails.new(user.id, user.username, user.name, user.email, "test update")
end
def slugs(thing)
Array.wrap(thing.respond_to?(:pages) ? thing.pages.map(&:slug) : thing.slug)
end end
end end
...@@ -9,6 +9,87 @@ describe WikiPage do ...@@ -9,6 +9,87 @@ describe WikiPage do
subject { described_class.new(wiki) } subject { described_class.new(wiki) }
describe '.group_by_directory' do
context 'when there are no pages' do
it 'returns an empty array' do
expect(described_class.group_by_directory(nil)).to eq([])
expect(described_class.group_by_directory([])).to eq([])
end
end
context 'when there are pages' do
before do
create_page('dir_1/dir_1_1/page_3', 'content')
create_page('page_1', 'content')
create_page('dir_1/page_2', 'content')
create_page('dir_2', 'page with dir name')
create_page('dir_2/page_5', 'content')
create_page('page_6', 'content')
create_page('dir_2/page_4', 'content')
end
let(:page_1) { wiki.find_page('page_1') }
let(:page_6) { wiki.find_page('page_6') }
let(:page_dir_2) { wiki.find_page('dir_2') }
let(:dir_1) do
WikiDirectory.new('dir_1', [wiki.find_page('dir_1/page_2')])
end
let(:dir_1_1) do
WikiDirectory.new('dir_1/dir_1_1', [wiki.find_page('dir_1/dir_1_1/page_3')])
end
let(:dir_2) do
pages = [wiki.find_page('dir_2/page_5'),
wiki.find_page('dir_2/page_4')]
WikiDirectory.new('dir_2', pages)
end
context "#list_pages" do
context 'sort by title' do
let(:grouped_entries) { described_class.group_by_directory(wiki.list_pages) }
let(:expected_grouped_entries) { [dir_1_1, dir_1, page_dir_2, dir_2, page_1, page_6] }
it 'returns an array with pages and directories' do
grouped_entries.each_with_index do |page_or_dir, i|
expected_page_or_dir = expected_grouped_entries[i]
expected_slugs = get_slugs(expected_page_or_dir)
slugs = get_slugs(page_or_dir)
expect(slugs).to match_array(expected_slugs)
end
end
end
context 'sort by created_at' do
let(:grouped_entries) { described_class.group_by_directory(wiki.list_pages(sort: 'created_at')) }
let(:expected_grouped_entries) { [dir_1_1, page_1, dir_1, page_dir_2, dir_2, page_6] }
it 'returns an array with pages and directories' do
grouped_entries.each_with_index do |page_or_dir, i|
expected_page_or_dir = expected_grouped_entries[i]
expected_slugs = get_slugs(expected_page_or_dir)
slugs = get_slugs(page_or_dir)
expect(slugs).to match_array(expected_slugs)
end
end
end
it 'returns an array with retained order with directories at the top' do
expected_order = ['dir_1/dir_1_1/page_3', 'dir_1/page_2', 'dir_2', 'dir_2/page_4', 'dir_2/page_5', 'page_1', 'page_6']
grouped_entries = described_class.group_by_directory(wiki.list_pages)
actual_order =
grouped_entries.flat_map do |page_or_dir|
get_slugs(page_or_dir)
end
expect(actual_order).to eq(expected_order)
end
end
end
end
describe '.unhyphenize' do describe '.unhyphenize' do
it 'removes hyphens from a name' do it 'removes hyphens from a name' do
name = 'a-name--with-hyphens' name = 'a-name--with-hyphens'
...@@ -441,7 +522,7 @@ describe WikiPage do ...@@ -441,7 +522,7 @@ describe WikiPage do
it 'returns the relative path to the partial to be used' do it 'returns the relative path to the partial to be used' do
page = build(:wiki_page) page = build(:wiki_page)
expect(page.to_partial_path).to eq('projects/wiki_pages/wiki_page') expect(page.to_partial_path).to eq('projects/wikis/wiki_page')
end end
end end
...@@ -521,4 +602,12 @@ describe WikiPage do ...@@ -521,4 +602,12 @@ describe WikiPage do
page = wiki.wiki.page(title: title, dir: dir) page = wiki.wiki.page(title: title, dir: dir)
wiki.delete_page(page, "test commit") wiki.delete_page(page, "test commit")
end end
def get_slugs(page_or_dir)
if page_or_dir.is_a? WikiPage
[page_or_dir.slug]
else
page_or_dir.pages.present? ? page_or_dir.pages.map(&:slug) : []
end
end
end end
...@@ -178,7 +178,8 @@ describe API::Settings, 'Settings' do ...@@ -178,7 +178,8 @@ describe API::Settings, 'Settings' do
snowplow_collector_hostname: "snowplow.example.com", snowplow_collector_hostname: "snowplow.example.com",
snowplow_cookie_domain: ".example.com", snowplow_cookie_domain: ".example.com",
snowplow_enabled: true, snowplow_enabled: true,
snowplow_site_id: "site_id" snowplow_site_id: "site_id",
snowplow_iglu_registry_url: 'https://example.com'
} }
end end
......
...@@ -5,12 +5,9 @@ require 'spec_helper' ...@@ -5,12 +5,9 @@ require 'spec_helper'
describe 'project routing' do describe 'project routing' do
before do before do
allow(Project).to receive(:find_by_full_path).and_return(false) allow(Project).to receive(:find_by_full_path).and_return(false)
allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq', any_args).and_return(project) allow(Project).to receive(:find_by_full_path).with('gitlab/gitlabhq', any_args).and_return(true)
end end
set(:namespace) { create(:namespace, name: 'gitlab') }
set(:project) { create(:project, namespace: namespace, name: 'gitlabhq') }
# Shared examples for a resource inside a Project # Shared examples for a resource inside a Project
# #
# By default it tests all the default REST actions: index, create, new, edit, # By default it tests all the default REST actions: index, create, new, edit,
...@@ -150,39 +147,24 @@ describe 'project routing' do ...@@ -150,39 +147,24 @@ describe 'project routing' do
it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/autocomplete_sources/labels", "/gitlab/gitlabhq/-/autocomplete_sources/labels" it_behaves_like 'redirecting a legacy project path', "/gitlab/gitlabhq/autocomplete_sources/labels", "/gitlab/gitlabhq/-/autocomplete_sources/labels"
end end
# GET /:project_id/wikis/pages(.:format) projects/wikis#pages # pages_project_wikis GET /:project_id/wikis/pages(.:format) projects/wikis#pages
# GET /:project_id/-/wiki_pages/:id/history(.:format) projects/wiki_pages#history # history_project_wiki GET /:project_id/wikis/:id/history(.:format) projects/wikis#history
# POST /:project_id/-/wiki_pages(.:format) projects/wiki_pages#create # project_wikis POST /:project_id/wikis(.:format) projects/wikis#create
# GET /:project_id/-/wiki_pages/:id/edit(.:format) projects/wiki_pages#edit # edit_project_wiki GET /:project_id/wikis/:id/edit(.:format) projects/wikis#edit
# GET /:project_id/-/wiki_pages/:id(.:format) projects/wiki_pages#show # project_wiki GET /:project_id/wikis/:id(.:format) projects/wikis#show
# DELETE /:project_id/-/wiki_pages/:id(.:format) projects/wiki_pages#destroy # DELETE /:project_id/wikis/:id(.:format) projects/wikis#destroy
describe Projects::WikisController, 'routing' do describe Projects::WikisController, 'routing' do
let(:wiki) { ProjectWiki.new(project, project.owner) } it 'to #pages' do
let(:wiki_page) { create(:wiki_page, wiki: wiki) } expect(get('/gitlab/gitlabhq/wikis/pages')).to route_to('projects/wikis#pages', namespace_id: 'gitlab', project_id: 'gitlabhq')
it '#pages' do
expect(get('/gitlab/gitlabhq/wikis/pages'))
.to route_to('projects/wikis#pages',
namespace_id: 'gitlab',
project_id: 'gitlabhq')
end end
describe '#history' do it 'to #history' do
let(:history_path) { project_wiki_history_path(project, wiki_page) } expect(get('/gitlab/gitlabhq/wikis/1/history')).to route_to('projects/wikis#history', namespace_id: 'gitlab', project_id: 'gitlabhq', id: '1')
it 'routes to history' do
expect(get(history_path))
.to route_to('projects/wiki_pages#history',
namespace_id: namespace.path,
project_id: project.name,
id: wiki_page.slug)
end
end end
it_behaves_like 'RESTful project resources' do it_behaves_like 'RESTful project resources' do
let(:actions) { [:create, :edit, :show, :destroy] } let(:actions) { [:create, :edit, :show, :destroy] }
let(:controller) { 'wiki_pages' } let(:controller) { 'wikis' }
let(:controller_path) { '-/wiki_pages' }
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
# We build URIs to wiki pages manually in various places (most notably
# in markdown generation). To ensure these do not get out of sync, these
# tests verify that our path generation assumptions are sound.
describe 'Wiki path generation assumptions' do
set(:project) { create(:project, :public, :repository) }
let(:project_wiki) { ProjectWiki.new(project, project.owner) }
let(:some_page_name) { 'some-wiki-page' }
let(:wiki_page) do
create(:wiki_page, wiki: project_wiki, attrs: { title: some_page_name })
end
describe 'WikiProject#wiki_page_path', 'routing' do
it 'is consistent with routing to wiki#show' do
uri = URI.parse(project_wiki.wiki_page_path)
path = ::File.join(uri.path, some_page_name)
expect(get('/' + path)).to route_to('projects/wiki_pages#show',
id: some_page_name,
namespace_id: project.namespace.to_param,
project_id: project.to_param)
end
end
describe 'project_wiki_path', 'routing' do
describe 'GET' do
it 'routes to the :show action' do
path = project_wiki_path(project, wiki_page)
expect(get('/' + path)).to route_to('projects/wiki_pages#show',
id: wiki_page.slug,
namespace_id: project.namespace.to_param,
project_id: project.to_param)
end
end
end
describe 'project_wiki_pages_new_path', 'routing' do
describe 'GET' do
it 'routes to the :new action' do
path = project_wiki_pages_new_path(project)
expect(get('/' + path)).to route_to('projects/wiki_pages#new',
namespace_id: project.namespace.to_param,
project_id: project.to_param)
end
end
end
# Early versions of the wiki paths routed all wiki pages at
# /wikis/:id - this test exists to guarantee that we support
# old URIs that may be out there, saved in bookmarks, on other wikis, etc.
describe 'legacy route support', type: 'request' do
let(:path) { ::File.join(project_wikis_path(project), some_page_name) }
before do
get(path)
end
it 'routes to new wiki paths' do
dest = project_wiki_path(project, wiki_page)
expect(response).to redirect_to(dest)
end
context 'the page is nested in a directory' do
let(:some_page_name) { 'some-dir/some-deep-dir/some-page' }
let(:path) { ::File.join(project_wikis_path(project), some_page_name) }
it 'still routes correctly' do
dest = project_wiki_path(project, wiki_page)
expect(response).to redirect_to(dest)
end
end
context 'the user requested the old history path' do
let(:some_page_name) { 'some-dir/some-deep-dir/some-page' }
let(:path) { ::File.join(project_wikis_path(project), some_page_name, 'history') }
it 'redirects to the new history path' do
dest = project_wiki_history_path(project, wiki_page)
expect(response).to redirect_to(dest)
end
end
context 'the user requested the old edit path' do
let(:some_page_name) { 'some-dir/some-deep-dir/some-page' }
let(:path) { ::File.join(project_wikis_path(project), some_page_name, 'edit') }
it 'redirects to the new history path' do
dest = project_wiki_edit_path(project, wiki_page)
expect(response).to redirect_to(dest)
end
end
end
end
# frozen_string_literal: true
def forbid_controller_ability!(ability)
allow(controller).to receive(:can?).and_call_original
allow(controller).to receive(:can?).with(anything, ability, any_args).and_return(false)
end
...@@ -46,14 +46,4 @@ module CapybaraHelpers ...@@ -46,14 +46,4 @@ module CapybaraHelpers
def javascript_test? def javascript_test?
Capybara.current_driver == Capybara.javascript_driver Capybara.current_driver == Capybara.javascript_driver
end end
def scroll_to(element)
raise 'JS not available' unless javascript_test?
script = <<-JS
arguments[0].scrollIntoView(true);
JS
page.driver.browser.execute_script(script, element.native)
end
end end
...@@ -14,8 +14,6 @@ module DropzoneHelper ...@@ -14,8 +14,6 @@ module DropzoneHelper
# If it's 'false', then the helper will NOT wait for backend response # If it's 'false', then the helper will NOT wait for backend response
# It lets to test behaviors while AJAX is processing. # It lets to test behaviors while AJAX is processing.
def dropzone_file(files, max_file_size = 0, wait_for_queuecomplete = true) def dropzone_file(files, max_file_size = 0, wait_for_queuecomplete = true)
# Assert that there is a dropzone to use (waiting until it is ready)
expect(page).to have_css('.div-dropzone')
# Generate a fake file input that Capybara can attach to # Generate a fake file input that Capybara can attach to
page.execute_script <<-JS.strip_heredoc page.execute_script <<-JS.strip_heredoc
$('#fakeFileInput').remove(); $('#fakeFileInput').remove();
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment