Commit 1ed0673c authored by Phil Hughes's avatar Phil Hughes

Merge branch 'master' into refactor-realtime-issue

parents 584ea586 4067dd4f
......@@ -494,7 +494,13 @@ Style/TrailingBlankLines:
# This cop checks for trailing comma in array and hash literals.
Style/TrailingCommaInLiteral:
Enabled: false
Enabled: true
EnforcedStyleForMultiline: no_comma
# This cop checks for trailing comma in argument lists.
Style/TrailingCommaInArguments:
Enabled: true
EnforcedStyleForMultiline: no_comma
# Checks for %W when interpolation is not needed.
Style/UnneededCapitalW:
......
......@@ -369,13 +369,6 @@ Style/SymbolProc:
Style/TernaryParentheses:
Enabled: false
# Offense count: 53
# Cop supports --auto-correct.
# Configuration parameters: EnforcedStyleForMultiline, SupportedStylesForMultiline.
# SupportedStylesForMultiline: comma, consistent_comma, no_comma
Style/TrailingCommaInArguments:
Enabled: false
# Offense count: 18
# Cop supports --auto-correct.
# Configuration parameters: AllowNamedUnderscoreVariables.
......
......@@ -8,7 +8,10 @@ export default class BlobViewer {
this.richViewer = document.querySelector('.blob-viewer[data-type="rich"]');
this.$fileHolder = $('.file-holder');
let initialViewerName = document.querySelector('.blob-viewer:not(.hidden)').getAttribute('data-type');
const initialViewer = document.querySelector('.blob-viewer:not(.hidden)');
if (!initialViewer) return;
let initialViewerName = initialViewer.getAttribute('data-type');
this.initBindings();
......
......@@ -36,6 +36,9 @@ gl.issueBoards.BoardSidebar = Vue.extend({
},
assigneeId() {
return this.issue.assignee ? this.issue.assignee.id : 0;
},
milestoneTitle() {
return this.issue.milestone ? this.issue.milestone.title : 'No Milestone';
}
},
watch: {
......
......@@ -246,6 +246,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
new NotificationsForm();
if ($('#tree-slider').length) {
new TreeView();
new BlobViewer();
}
break;
case 'projects:pipelines:builds':
......@@ -300,6 +301,7 @@ const ShortcutsBlob = require('./shortcuts_blob');
case 'projects:tree:show':
shortcut_handler = new ShortcutsNavigation();
new TreeView();
new BlobViewer();
gl.TargetBranchDropDown.bootstrap();
break;
case 'projects:find_file:show':
......
const AjaxCache = {
internalStorage: { },
class AjaxCache {
constructor() {
this.internalStorage = { };
this.pendingRequests = { };
}
get(endpoint) {
return this.internalStorage[endpoint];
},
}
hasData(endpoint) {
return Object.prototype.hasOwnProperty.call(this.internalStorage, endpoint);
},
purge(endpoint) {
}
remove(endpoint) {
delete this.internalStorage[endpoint];
},
}
retrieve(endpoint) {
if (AjaxCache.hasData(endpoint)) {
return Promise.resolve(AjaxCache.get(endpoint));
if (this.hasData(endpoint)) {
return Promise.resolve(this.get(endpoint));
}
return new Promise((resolve, reject) => {
let pendingRequest = this.pendingRequests[endpoint];
if (!pendingRequest) {
pendingRequest = new Promise((resolve, reject) => {
// jQuery 2 is not Promises/A+ compatible (missing catch)
$.ajax(endpoint) // eslint-disable-line promise/catch-or-return
.then(data => resolve(data),
(jqXHR, textStatus, errorThrown) => {
......@@ -24,9 +35,20 @@ const AjaxCache = {
},
);
})
.then((data) => { this.internalStorage[endpoint] = data; })
.then(() => AjaxCache.get(endpoint));
},
};
.then((data) => {
this.internalStorage[endpoint] = data;
delete this.pendingRequests[endpoint];
})
.catch((error) => {
delete this.pendingRequests[endpoint];
throw error;
});
this.pendingRequests[endpoint] = pendingRequest;
}
return pendingRequest.then(() => this.get(endpoint));
}
}
export default AjaxCache;
export default new AjaxCache();
......@@ -18,12 +18,11 @@
}
$els.each(function(i, dropdown) {
var $block, $dropdown, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, collapsedSidebarLabelTemplate, defaultLabel, issuableId, issueUpdateURL, milestoneLinkNoneTemplate, milestoneLinkTemplate, milestonesUrl, projectId, selectedMilestone, showAny, showNo, showUpcoming, showStarted, useId, showMenuAbove;
var $block, $dropdown, $loading, $selectbox, $sidebarCollapsedValue, $value, abilityName, collapsedSidebarLabelTemplate, defaultLabel, defaultNo, issuableId, issueUpdateURL, milestoneLinkNoneTemplate, milestoneLinkTemplate, milestonesUrl, projectId, selectedMilestone, selectedMilestoneDefault, showAny, showNo, showUpcoming, showStarted, useId, showMenuAbove;
$dropdown = $(dropdown);
projectId = $dropdown.data('project-id');
milestonesUrl = $dropdown.data('milestones');
issueUpdateURL = $dropdown.data('issueUpdate');
selectedMilestone = $dropdown.data('selected');
showNo = $dropdown.data('show-no');
showAny = $dropdown.data('show-any');
showMenuAbove = $dropdown.data('showMenuAbove');
......@@ -31,6 +30,7 @@
showStarted = $dropdown.data('show-started');
useId = $dropdown.data('use-id');
defaultLabel = $dropdown.data('default-label');
defaultNo = $dropdown.data('default-no');
issuableId = $dropdown.data('issuable-id');
abilityName = $dropdown.data('ability-name');
$selectbox = $dropdown.closest('.selectbox');
......@@ -38,6 +38,9 @@
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon');
$value = $block.find('.value');
$loading = $block.find('.block-loading').fadeOut();
selectedMilestoneDefault = (showAny ? '' : null);
selectedMilestoneDefault = (showNo && defaultNo ? 'No Milestone' : selectedMilestoneDefault);
selectedMilestone = $dropdown.data('selected') || selectedMilestoneDefault;
if (issueUpdateURL) {
milestoneLinkTemplate = _.template('<a href="/<%- full_path %>/milestones/<%- iid %>" class="bold has-tooltip" data-container="body" title="<%- remaining %>"><%- title %></a>');
milestoneLinkNoneTemplate = '<span class="no-value">None</span>';
......@@ -86,8 +89,18 @@
if (showMenuAbove) {
$dropdown.data('glDropdown').positionMenuAbove();
}
$(`[data-milestone-id="${selectedMilestone}"] > a`).addClass('is-active');
});
},
renderRow: function(milestone) {
return `
<li data-milestone-id="${milestone.name}">
<a href='#' class='dropdown-menu-milestone-link'>
${_.escape(milestone.title)}
</a>
</li>
`;
},
filterable: true,
search: {
fields: ['title']
......@@ -120,15 +133,24 @@
// display:block overrides the hide-collapse rule
return $value.css('display', '');
},
opened: function(e) {
const $el = $(e.currentTarget);
if ($dropdown.hasClass('js-issue-board-sidebar')) {
selectedMilestone = $dropdown[0].dataset.selected || selectedMilestoneDefault;
}
$('a.is-active', $el).removeClass('is-active');
$(`[data-milestone-id="${selectedMilestone}"] > a`, $el).addClass('is-active');
},
vue: $dropdown.hasClass('js-issue-board-sidebar'),
clicked: function(options) {
const { $el, e } = options;
let selected = options.selectedObj;
var data, isIssueIndex, isMRIndex, page, boardsStore;
var data, isIssueIndex, isMRIndex, isSelecting, page, boardsStore;
page = $('body').data('page');
isIssueIndex = page === 'projects:issues:index';
isMRIndex = (page === page && page === 'projects:merge_requests:index');
isSelecting = (selected.name !== selectedMilestone);
selectedMilestone = isSelecting ? selected.name : selectedMilestoneDefault;
if ($dropdown.hasClass('js-filter-bulk-update') || $dropdown.hasClass('js-issuable-form-dropdown')) {
e.preventDefault();
return;
......@@ -142,16 +164,11 @@
boardsStore[$dropdown.data('field-name')] = selected.name;
e.preventDefault();
} else if ($dropdown.hasClass('js-filter-submit') && (isIssueIndex || isMRIndex)) {
if (selected.name != null) {
selectedMilestone = selected.name;
} else {
selectedMilestone = '';
}
return Issuable.filterResults($dropdown.closest('form'));
} else if ($dropdown.hasClass('js-filter-submit')) {
return $dropdown.closest('form').submit();
} else if ($dropdown.hasClass('js-issue-board-sidebar')) {
if (selected.id !== -1) {
if (selected.id !== -1 && isSelecting) {
gl.issueBoards.boardStoreIssueSet('milestone', new ListMilestone({
id: selected.id,
title: selected.name
......
......@@ -312,7 +312,7 @@
}
.empty-state {
margin: 100px 0 0;
margin: 5% auto 0;
.text-content {
max-width: 460px;
......@@ -335,27 +335,12 @@
}
.btn {
margin: $btn-side-margin $btn-side-margin 0 0;
}
margin: $btn-side-margin 5px;
@media(max-width: $screen-xs-max) {
margin-top: 50px;
text-align: center;
.btn {
width: 100%;
}
}
@media(min-width: $screen-xs-max) {
&.merge-requests .text-content {
margin-top: 40px;
}
&.labels .text-content {
margin-top: 70px;
}
}
}
.flex-container-block {
......
......@@ -51,6 +51,7 @@ ul.related-merge-requests > li {
display: -ms-flexbox;
display: -webkit-flex;
display: flex;
align-items: center;
.merge-request-id {
flex-shrink: 0;
......@@ -59,6 +60,14 @@ ul.related-merge-requests > li {
.merge-request-info {
margin-left: 5px;
}
.row_title {
vertical-align: bottom;
}
gl-emoji {
font-size: 1em;
}
}
.merge-requests-title,
......@@ -114,7 +123,6 @@ ul.related-merge-requests > li {
.related-merge-requests {
.ci-status-link {
display: block;
margin-top: 3px;
margin-right: 5px;
}
......
......@@ -8,10 +8,6 @@
white-space: nowrap;
}
.empty-state {
margin: 5% auto 0;
}
.table-holder {
width: 100%;
......
......@@ -41,7 +41,7 @@ class AutocompleteController < ApplicationController
no_project = {
id: 0,
name_with_namespace: 'No project',
name_with_namespace: 'No project'
}
projects.unshift(no_project) unless params[:offset_id].present?
......
......@@ -23,7 +23,7 @@ module LfsRequest
render(
json: {
message: 'Git LFS is not enabled on this GitLab server, contact your admin.',
documentation_url: help_url,
documentation_url: help_url
},
status: 501
)
......@@ -48,7 +48,7 @@ module LfsRequest
render(
json: {
message: 'Access forbidden. Check your access level.',
documentation_url: help_url,
documentation_url: help_url
},
content_type: "application/vnd.git-lfs+json",
status: 403
......@@ -59,7 +59,7 @@ module LfsRequest
render(
json: {
message: 'Not found.',
documentation_url: help_url,
documentation_url: help_url
},
content_type: "application/vnd.git-lfs+json",
status: 404
......
class Dashboard::SnippetsController < Dashboard::ApplicationController
def index
@snippets = SnippetsFinder.new.execute(
@snippets = SnippetsFinder.new(
current_user,
filter: :by_user,
user: current_user,
author: current_user,
scope: params[:scope]
)
).execute
@snippets = @snippets.page(params[:page])
end
end
class Explore::GroupsController < Explore::ApplicationController
def index
@groups = GroupsFinder.new.execute(current_user)
@groups = GroupsFinder.new(current_user).execute
@groups = @groups.search(params[:filter_groups]) if params[:filter_groups].present?
@groups = @groups.sort(@sort = params[:sort])
@groups = @groups.page(params[:page])
......
class Explore::SnippetsController < Explore::ApplicationController
def index
@snippets = SnippetsFinder.new.execute(current_user, filter: :all)
@snippets = SnippetsFinder.new(current_user).execute
@snippets = @snippets.page(params[:page])
end
end
......@@ -64,7 +64,7 @@ class GroupsController < Groups::ApplicationController
end
def subgroups
@nested_groups = group.children
@nested_groups = GroupsFinder.new(current_user, parent: group).execute
@nested_groups = @nested_groups.search(params[:filter_groups]) if params[:filter_groups].present?
end
......
......@@ -5,7 +5,7 @@ class HealthController < ActionController::Base
CHECKS = [
Gitlab::HealthChecks::DbCheck,
Gitlab::HealthChecks::RedisCheck,
Gitlab::HealthChecks::FsShardsCheck,
Gitlab::HealthChecks::FsShardsCheck
].freeze
def readiness
......
......@@ -4,7 +4,7 @@ class JwtController < ApplicationController
before_action :authenticate_project_or_user
SERVICES = {
Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService,
Auth::ContainerRegistryAuthenticationService::AUDIENCE => Auth::ContainerRegistryAuthenticationService
}.freeze
def auth
......
......@@ -33,7 +33,7 @@ class Profiles::PreferencesController < Profiles::ApplicationController
:color_scheme_id,
:layout,
:dashboard,
:project_view,
:project_view
)
end
end
......@@ -6,7 +6,7 @@ class Projects::DeploymentsController < Projects::ApplicationController
deployments = environment.deployments.reorder(created_at: :desc)
deployments = deployments.where('created_at > ?', params[:after].to_time) if params[:after]&.to_time
render json: { deployments: DeploymentSerializer.new(user: @current_user, project: project)
render json: { deployments: DeploymentSerializer.new(project: project)
.represent_concise(deployments) }
end
......
......@@ -208,7 +208,7 @@ class Projects::IssuesController < Projects::ApplicationController
description: view_context.markdown_field(@issue, :description),
description_text: @issue.description,
task_status: @issue.task_status,
updated_at: @issue.updated_at,
updated_at: @issue.updated_at
}
end
......@@ -268,7 +268,7 @@ class Projects::IssuesController < Projects::ApplicationController
def issue_params
params.require(:issue).permit(
:title, :assignee_id, :position, :description, :confidential,
:milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: [], assignee_ids: [],
:milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: [], assignee_ids: []
)
end
......
......@@ -22,7 +22,7 @@ class Projects::LfsApiController < Projects::GitHttpClientController
render(
json: {
message: 'Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.',
documentation_url: "#{Gitlab.config.gitlab.url}/help",
documentation_url: "#{Gitlab.config.gitlab.url}/help"
},
status: 501
)
......@@ -55,7 +55,7 @@ class Projects::LfsApiController < Projects::GitHttpClientController
else
object[:error] = {
code: 404,
message: "Object does not exist on the server or you don't have permissions to access it",
message: "Object does not exist on the server or you don't have permissions to access it"
}
end
end
......
......@@ -44,7 +44,7 @@ class Projects::PipelinesController < Projects::ApplicationController
all: @pipelines_count,
running: @running_count,
pending: @pending_count,
finished: @finished_count,
finished: @finished_count
}
}
end
......
......@@ -23,12 +23,11 @@ class Projects::SnippetsController < Projects::ApplicationController
respond_to :html
def index
@snippets = SnippetsFinder.new.execute(
@snippets = SnippetsFinder.new(
current_user,
filter: :by_project,
project: @project,
scope: params[:scope]
)
).execute
@snippets = @snippets.page(params[:page])
if @snippets.out_of_range? && @snippets.total_pages != 0
redirect_to namespace_project_snippets_path(page: @snippets.total_pages)
......
......@@ -38,6 +38,8 @@ class Projects::TagsController < Projects::ApplicationController
redirect_to namespace_project_tag_path(@project.namespace, @project, @tag.name)
else
@error = result[:message]
@message = params[:message]
@release_description = params[:release_description]
render action: 'new'
end
end
......
......@@ -48,7 +48,7 @@ class Projects::TreeController < Projects::ApplicationController
@dir_name = File.join(@path, params[:dir_name])
@commit_params = {
file_path: @dir_name,
commit_message: params[:commit_message],
commit_message: params[:commit_message]
}
end
end
......@@ -220,7 +220,7 @@ class ProjectsController < Projects::ApplicationController
branches = BranchesFinder.new(@repository, params).execute.map(&:name)
options = {
'Branches' => branches.take(100),
'Branches' => branches.take(100)
}
unless @repository.tag_count.zero?
......
......@@ -27,12 +27,8 @@ class SnippetsController < ApplicationController
return render_404 unless @user
@snippets = SnippetsFinder.new.execute(current_user, {
filter: :by_user,
user: @user,
scope: params[:scope]
})
.page(params[:page])
@snippets = SnippetsFinder.new(current_user, author: @user, scope: params[:scope])
.execute.page(params[:page])
render 'index'
else
......@@ -103,20 +99,20 @@ class SnippetsController < ApplicationController
protected
def snippet
@snippet ||= if current_user
PersonalSnippet.where("author_id = ? OR visibility_level IN (?)",
current_user.id,
[Snippet::PUBLIC, Snippet::INTERNAL]).
find(params[:id])
else
PersonalSnippet.find(params[:id])
end
@snippet ||= PersonalSnippet.find_by(id: params[:id])
end
alias_method :awardable, :snippet
alias_method :spammable, :snippet
def authorize_read_snippet!
authenticate_user! unless can?(current_user, :read_personal_snippet, @snippet)
return if can?(current_user, :read_personal_snippet, @snippet)
if current_user
render_404
else
authenticate_user!
end
end
def authorize_update_snippet!
......
......@@ -128,12 +128,11 @@ class UsersController < ApplicationController
end
def load_snippets
@snippets = SnippetsFinder.new.execute(
@snippets = SnippetsFinder.new(
current_user,
filter: :by_user,
user: user,
author: user,
scope: params[:scope]
).page(params[:page])
).execute.page(params[:page])
end
def projects_for_current_user
......
class GroupsFinder < UnionFinder
def execute(current_user = nil)
segments = all_groups(current_user)
def initialize(current_user = nil, params = {})
@current_user = current_user
@params = params
end
find_union(segments, Group).with_route.order_id_desc
def execute
groups = find_union(all_groups, Group).with_route.order_id_desc
by_parent(groups)
end
private
def all_groups(current_user)
attr_reader :current_user, :params
def all_groups
groups = []
groups << current_user.authorized_groups if current_user
......@@ -15,4 +21,10 @@ class GroupsFinder < UnionFinder
groups
end
def by_parent(groups)
return groups unless params[:parent]
groups.where(parent: params[:parent])
end
end
......@@ -67,7 +67,7 @@ class NotesFinder
when "merge_request"
MergeRequestsFinder.new(@current_user, project_id: @project.id).execute
when "snippet", "project_snippet"
SnippetsFinder.new.execute(@current_user, filter: :by_project, project: @project)
SnippetsFinder.new(@current_user, project: @project).execute
when "personal_snippet"
PersonalSnippet.all
else
......
class SnippetsFinder
def execute(current_user, params = {})
filter = params[:filter]
user = params.fetch(:user, current_user)
case filter
when :all then
snippets(current_user).fresh
when :public then
Snippet.are_public.fresh
when :by_user then
by_user(current_user, user, params[:scope])
when :by_project
by_project(current_user, params[:project], params[:scope])
class SnippetsFinder < UnionFinder
attr_accessor :current_user, :params
def initialize(current_user, params = {})
@current_user = current_user
@params = params
end
def execute
items = init_collection
items = by_project(items)
items = by_author(items)
items = by_visibility(items)
items.fresh
end
private
def snippets(current_user)
if current_user
Snippet.public_and_internal
else
# Not authenticated
#
# Return only:
# public snippets
Snippet.are_public
end
def init_collection
items = Snippet.all
accessible(items)
end
def by_user(current_user, user, scope)
snippets = user.snippets.fresh
def accessible(items)
segments = []
segments << items.public_to_user(current_user)
segments << authorized_to_user(items) if current_user
if current_user
include_private = user == current_user
by_scope(snippets, scope, include_private)
else
snippets.are_public
find_union(segments, Snippet)
end
def authorized_to_user(items)
items.where(
'author_id = :author_id
OR project_id IN (:project_ids)',
author_id: current_user.id,
project_ids: current_user.authorized_projects.select(:id))
end
def by_project(current_user, project, scope)
snippets = project.snippets.fresh
def by_visibility(items)
visibility = params[:visibility] || visibility_from_scope
if current_user
include_private = project.team.member?(current_user) || current_user.admin?
by_scope(snippets, scope, include_private)
else
snippets.are_public
return items unless visibility
items.where(visibility_level: visibility)
end
def by_author(items)
return items unless params[:author]
items.where(author_id: params[:author].id)
end
def by_project(items)
return items unless params[:project]
items.where(project_id: params[:project].id)
end
def by_scope(snippets, scope = nil, include_private = false)
case scope.to_s
def visibility_from_scope
case params[:scope].to_s
when 'are_private'
include_private ? snippets.are_private : Snippet.none
Snippet::PRIVATE
when 'are_internal'
snippets.are_internal
Snippet::INTERNAL
when 'are_public'
snippets.are_public
Snippet::PUBLIC
else
include_private ? snippets : snippets.public_and_internal
nil
end
end
end
......@@ -98,7 +98,7 @@ module DiffHelper
[
content_tag(:span, link_to(truncate(blob.name, length: 40), tree)),
'@',
content_tag(:span, commit_id, class: 'monospace'),
content_tag(:span, commit_id, class: 'monospace')
].join(' ').html_safe
end
......
......@@ -12,7 +12,7 @@ module EmailsHelper
"action" => {
"@type" => "ViewAction",
"name" => name,
"url" => url,
"url" => url
}
}
......
......@@ -41,7 +41,7 @@ module EventsHelper
link_opts = {
class: "event-filter-link",
id: "#{key}_event_filter",
title: "Filter by #{tooltip.downcase}",
title: "Filter by #{tooltip.downcase}"
}
content_tag :li, class: active do
......
......@@ -10,7 +10,7 @@ module ExploreHelper
personal: params[:personal],
archived: params[:archived],
shared: params[:shared],
namespace_id: params[:namespace_id],
namespace_id: params[:namespace_id]
}
options = exist_opts.merge(options).delete_if { |key, value| value.blank? }
......
......@@ -32,7 +32,7 @@ module MarkupHelper
context = {
project: @project,
current_user: (current_user if defined?(current_user)),
pipeline: :single_line,
pipeline: :single_line
}
gfm_body = Banzai.render(body, context)
......@@ -116,13 +116,13 @@ module MarkupHelper
if gitlab_markdown?(file_name)
markdown_unsafe(text, context)
elsif asciidoc?(file_name)
asciidoc_unsafe(text)
asciidoc_unsafe(text, context)
elsif plain?(file_name)
content_tag :pre, class: 'plain-readme' do
text
end
else
other_markup_unsafe(file_name, text)
other_markup_unsafe(file_name, text, context)
end
rescue RuntimeError
simple_format(text)
......@@ -217,12 +217,12 @@ module MarkupHelper
Banzai.render(text, context)
end
def asciidoc_unsafe(text)
Gitlab::Asciidoc.render(text)
def asciidoc_unsafe(text, context = {})
Gitlab::Asciidoc.render(text, context)
end
def other_markup_unsafe(file_name, text)
Gitlab::OtherMarkup.render(file_name, text)
def other_markup_unsafe(file_name, text, context = {})
Gitlab::OtherMarkup.render(file_name, text, context)
end
def prepare_for_rendering(html, context = {})
......
......@@ -54,7 +54,7 @@ module MergeRequestsHelper
source_project_id: merge_request.source_project_id,
target_project_id: merge_request.target_project_id,
source_branch: merge_request.source_branch,
target_branch: merge_request.target_branch,
target_branch: merge_request.target_branch
},
change_branches: true
)
......
......@@ -19,7 +19,7 @@ module NotesHelper
id: noteable.id,
class: noteable.class.name,
resources: noteable.class.table_name,
project_id: noteable.project.id,
project_id: noteable.project.id
}.to_json
end
......@@ -34,7 +34,7 @@ module NotesHelper
data = {
line_code: line_code,
line_type: line_type,
line_type: line_type
}
if @use_legacy_diff_notes
......
......@@ -42,7 +42,7 @@ module SearchHelper
{ category: "Settings", label: "User settings", url: profile_path },
{ category: "Settings", label: "SSH Keys", url: profile_keys_path },
{ category: "Settings", label: "Dashboard", url: root_path },
{ category: "Settings", label: "Admin Section", url: admin_root_path },
{ category: "Settings", label: "Admin Section", url: admin_root_path }
]
end
......@@ -57,7 +57,7 @@ module SearchHelper
{ category: "Help", label: "SSH Keys Help", url: help_page_path("ssh/README") },
{ category: "Help", label: "System Hooks Help", url: help_page_path("system_hooks/system_hooks") },
{ category: "Help", label: "Webhooks Help", url: help_page_path("user/project/integrations/webhooks") },
{ category: "Help", label: "Workflow Help", url: help_page_path("workflow/README") },
{ category: "Help", label: "Workflow Help", url: help_page_path("workflow/README") }
]
end
......@@ -76,7 +76,7 @@ module SearchHelper
{ category: "Current Project", label: "Milestones", url: namespace_project_milestones_path(@project.namespace, @project) },
{ category: "Current Project", label: "Snippets", url: namespace_project_snippets_path(@project.namespace, @project) },
{ category: "Current Project", label: "Members", url: namespace_project_settings_members_path(@project.namespace, @project) },
{ category: "Current Project", label: "Wiki", url: namespace_project_wikis_path(@project.namespace, @project) },
{ category: "Current Project", label: "Wiki", url: namespace_project_wikis_path(@project.namespace, @project) }
]
else
[]
......
......@@ -67,7 +67,7 @@ module SelectsHelper
current_user: opts[:current_user] || false,
"push-code-to-protected-branches" => opts[:push_code_to_protected_branches],
author_id: opts[:author_id] || '',
skip_users: opts[:skip_users] ? opts[:skip_users].map(&:id) : nil,
skip_users: opts[:skip_users] ? opts[:skip_users].map(&:id) : nil
}
end
end
......@@ -58,7 +58,7 @@ module SortingHelper
sort_value_due_date_soon => sort_title_due_date_soon,
sort_value_due_date_later => sort_title_due_date_later,
sort_value_start_date_soon => sort_title_start_date_soon,
sort_value_start_date_later => sort_title_start_date_later,
sort_value_start_date_later => sort_title_start_date_later
}
end
......
module SubmoduleHelper
include Gitlab::ShellAdapter
VALID_SUBMODULE_PROTOCOLS = %w[http https git ssh].freeze
# links to files listing for submodule if submodule is a project on this server
def submodule_links(submodule_item, ref = nil, repository = @repository)
url = repository.submodule_url_for(ref, submodule_item.path)
return url, nil unless url =~ /([^\/:]+)\/([^\/]+(?:\.git)?)\Z/
namespace = $1
project = $2
project.chomp!('.git')
if url =~ /([^\/:]+)\/([^\/]+(?:\.git)?)\Z/
namespace, project = $1, $2
project.sub!(/\.git\z/, '')
if self_url?(url, namespace, project)
return namespace_project_path(namespace, project),
namespace_project_tree_path(namespace, project,
submodule_item.id)
[namespace_project_path(namespace, project),
namespace_project_tree_path(namespace, project, submodule_item.id)]
elsif relative_self_url?(url)
relative_self_links(url, submodule_item.id)
elsif github_dot_com_url?(url)
......@@ -22,7 +21,10 @@ module SubmoduleHelper
elsif gitlab_dot_com_url?(url)
standard_links('gitlab.com', namespace, project, submodule_item.id)
else
return url, nil
[sanitize_submodule_url(url), nil]
end
else
[sanitize_submodule_url(url), nil]
end
end
......@@ -73,4 +75,16 @@ module SubmoduleHelper
namespace_project_tree_path(namespace, base, commit)
]
end
def sanitize_submodule_url(url)
uri = URI.parse(url)
if uri.scheme.in?(VALID_SUBMODULE_PROTOCOLS)
uri.to_s
else
nil
end
rescue URI::InvalidURIError
nil
end
end
......@@ -63,7 +63,7 @@ module TodosHelper
project_id: params[:project_id],
author_id: params[:author_id],
type: params[:type],
action_id: params[:action_id],
action_id: params[:action_id]
}
end
......
......@@ -33,7 +33,7 @@ class Blob < SimpleDelegator
BlobViewer::PDF,
BlobViewer::BinarySTL,
BlobViewer::TextSTL,
BlobViewer::TextSTL
].freeze
BINARY_VIEWERS = RICH_VIEWERS.select(&:binary?).freeze
......
......@@ -124,8 +124,8 @@ module Ci
success? || failed? || canceled?
end
def retried?
!self.pipeline.statuses.latest.include?(self)
def latest?
!retried?
end
def expanded_environment_name
......
......@@ -49,7 +49,7 @@ class Commit
def max_diff_options
{
max_files: DIFF_HARD_LIMIT_FILES,
max_lines: DIFF_HARD_LIMIT_LINES,
max_lines: DIFF_HARD_LIMIT_LINES
}
end
......
......@@ -19,12 +19,6 @@ class CommitStatus < ActiveRecord::Base
alias_attribute :author, :user
scope :latest, -> do
max_id = unscope(:select).select("max(#{quoted_table_name}.id)")
where(id: max_id.group(:name, :commit_id))
end
scope :failed_but_allowed, -> do
where(allow_failure: true, status: [:failed, :canceled])
end
......@@ -37,7 +31,8 @@ class CommitStatus < ActiveRecord::Base
false, all_state_names - [:failed, :canceled, :manual])
end
scope :retried, -> { where.not(id: latest) }
scope :latest, -> { where(retried: [false, nil]) }
scope :retried, -> { where(retried: true) }
scope :ordered, -> { order(:name) }
scope :latest_ordered, -> { latest.ordered.includes(project: :namespace) }
scope :retried_ordered, -> { retried.ordered.includes(project: :namespace) }
......
......@@ -39,7 +39,7 @@ class DiffDiscussion < Discussion
def reply_attributes
super.merge(
original_position: original_position.to_json,
position: position.to_json,
position: position.to_json
)
end
end
......@@ -62,7 +62,7 @@ class Environment < ActiveRecord::Base
def predefined_variables
[
{ key: 'CI_ENVIRONMENT_NAME', value: name, public: true },
{ key: 'CI_ENVIRONMENT_SLUG', value: slug, public: true },
{ key: 'CI_ENVIRONMENT_SLUG', value: slug, public: true }
]
end
......
......@@ -31,7 +31,7 @@ class WebHook < ActiveRecord::Base
post_url = url.gsub("#{parsed_url.userinfo}@", '')
auth = {
username: CGI.unescape(parsed_url.user),
password: CGI.unescape(parsed_url.password),
password: CGI.unescape(parsed_url.password)
}
response = WebHook.post(post_url,
body: data.to_json,
......
......@@ -74,7 +74,7 @@ class Key < ActiveRecord::Base
GitlabShellWorker.perform_async(
:remove_key,
shell_id,
key,
key
)
end
......
......@@ -56,7 +56,7 @@ class Namespace < ActiveRecord::Base
'COALESCE(SUM(ps.storage_size), 0) AS storage_size',
'COALESCE(SUM(ps.repository_size), 0) AS repository_size',
'COALESCE(SUM(ps.lfs_objects_size), 0) AS lfs_objects_size',
'COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size',
'COALESCE(SUM(ps.build_artifacts_size), 0) AS build_artifacts_size'
)
end
......
......@@ -967,7 +967,7 @@ class Project < ActiveRecord::Base
namespace: namespace.name,
visibility_level: visibility_level,
path_with_namespace: path_with_namespace,
default_branch: default_branch,
default_branch: default_branch
}
# Backward compatibility
......
......@@ -52,7 +52,7 @@ class BambooService < CiService
placeholder: 'Bamboo build plan key like KEY' },
{ type: 'text', name: 'username',
placeholder: 'A user with API access, if applicable' },
{ type: 'password', name: 'password' },
{ type: 'password', name: 'password' }
]
end
......
......@@ -39,7 +39,7 @@ class ChatNotificationService < Service
{ type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
{ type: 'text', name: 'username', placeholder: 'e.g. GitLab' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
{ type: 'checkbox', name: 'notify_only_default_branch' },
{ type: 'checkbox', name: 'notify_only_default_branch' }
]
end
......
......@@ -47,7 +47,7 @@ class EmailsOnPushService < Service
help: "Send notifications from the committer's email address if the domain is part of the domain GitLab is running on (e.g. #{domains})." },
{ type: 'checkbox', name: 'disable_diffs', title: "Disable code diffs",
help: "Don't include possibly sensitive code diffs in notification body." },
{ type: 'textarea', name: 'recipients', placeholder: 'Emails separated by whitespace' },
{ type: 'textarea', name: 'recipients', placeholder: 'Emails separated by whitespace' }
]
end
end
......@@ -19,7 +19,7 @@ class ExternalWikiService < Service
def fields
[
{ type: 'text', name: 'external_wiki_url', placeholder: 'The URL of the external Wiki' },
{ type: 'text', name: 'external_wiki_url', placeholder: 'The URL of the external Wiki' }
]
end
......
......@@ -37,7 +37,7 @@ class FlowdockService < Service
repo: project.repository.path_to_repo,
repo_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}",
commit_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/commit/%s",
diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s",
diff_url: "#{Gitlab.config.gitlab.url}/#{project.path_with_namespace}/compare/%s...%s"
)
end
end
......@@ -41,7 +41,7 @@ class HipchatService < Service
placeholder: 'Leave blank for default (v2)' },
{ type: 'text', name: 'server',
placeholder: 'Leave blank for default. https://hipchat.example.com' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' }
]
end
......
......@@ -58,7 +58,7 @@ class IrkerService < Service
' want to use a password, you have to omit the "#" on the channel). If you ' \
' specify a default IRC URI to prepend before each recipient, you can just ' \
' give a channel name.' },
{ type: 'checkbox', name: 'colorize_messages' },
{ type: 'checkbox', name: 'colorize_messages' }
]
end
......
......@@ -149,7 +149,7 @@ class JiraService < IssueTrackerService
data = {
user: {
name: author.name,
url: resource_url(user_path(author)),
url: resource_url(user_path(author))
},
project: {
name: self.project.path_with_namespace,
......
......@@ -73,7 +73,7 @@ class KubernetesService < DeploymentService
{ type: 'textarea',
name: 'ca_pem',
title: 'Custom CA bundle',
placeholder: 'Certificate Authority bundle (PEM format)' },
placeholder: 'Certificate Authority bundle (PEM format)' }
]
end
......
......@@ -35,7 +35,7 @@ class MicrosoftTeamsService < ChatNotificationService
[
{ type: 'text', name: 'webhook', placeholder: "e.g. #{webhook_placeholder}" },
{ type: 'checkbox', name: 'notify_only_broken_pipelines' },
{ type: 'checkbox', name: 'notify_only_default_branch' },
{ type: 'checkbox', name: 'notify_only_default_branch' }
]
end
......
......@@ -21,7 +21,7 @@ class MockCiService < CiService
[
{ type: 'text',
name: 'mock_service_url',
placeholder: 'http://localhost:4004' },
placeholder: 'http://localhost:4004' }
]
end
......
......@@ -55,7 +55,7 @@ class PipelinesEmailService < Service
name: 'recipients',
placeholder: 'Emails separated by comma' },
{ type: 'checkbox',
name: 'notify_only_broken_pipelines' },
name: 'notify_only_broken_pipelines' }
]
end
......
......@@ -55,7 +55,7 @@ class PushoverService < Service
['Pushover Echo (long)', 'echo'],
['Up Down (long)', 'updown'],
['None (silent)', 'none']
] },
] }
]
end
......
......@@ -55,7 +55,7 @@ class TeamcityService < CiService
placeholder: 'Build configuration ID' },
{ type: 'text', name: 'username',
placeholder: 'A user with permissions to trigger a manual build' },
{ type: 'password', name: 'password' },
{ type: 'password', name: 'password' }
]
end
......@@ -78,7 +78,7 @@ class TeamcityService < CiService
auth = {
username: username,
password: password,
password: password
}
branch = Gitlab::Git.ref_name(data[:ref])
......
class ReadmeBlob < SimpleDelegator
attr_reader :repository
def initialize(blob, repository)
@repository = repository
super(blob)
end
def rendered_markup
repository.rendered_readme
end
end
......@@ -518,7 +518,7 @@ class Repository
def readme
if head = tree(:head)
head.readme
ReadmeBlob.new(head.readme, self)
end
end
......@@ -833,7 +833,7 @@ class Repository
actual_options = options.merge(
parents: [our_commit, their_commit],
tree: merge_index.write_tree(rugged),
tree: merge_index.write_tree(rugged)
)
commit_id = create_commit(actual_options)
......
......@@ -39,7 +39,7 @@ class SentNotification < ActiveRecord::Base
noteable_type: noteable.class.name,
noteable_id: noteable_id,
commit_id: commit_id,
commit_id: commit_id
)
create(attrs)
......
......@@ -152,18 +152,5 @@ class Snippet < ActiveRecord::Base
where(table[:content].matches(pattern))
end
def accessible_to(user)
return are_public unless user.present?
return all if user.admin?
where(
'visibility_level IN (:visibility_levels)
OR author_id = :author_id
OR project_id IN (:project_ids)',
visibility_levels: [Snippet::PUBLIC, Snippet::INTERNAL],
author_id: user.id,
project_ids: user.authorized_projects.select(:id))
end
end
end
......@@ -40,10 +40,7 @@ class Tree
readme_path = path == '/' ? readme_tree.name : File.join(path, readme_tree.name)
git_repo = repository.raw_repository
@readme = Gitlab::Git::Blob.find(git_repo, sha, readme_path)
@readme.load_all_data!(git_repo)
@readme
@readme = repository.blob_at(sha, readme_path)
end
def trees
......
......@@ -13,7 +13,7 @@ class ProjectSnippetPolicy < BasePolicy
can! :read_project_snippet
end
if @subject.private? && @subject.project.team.member?(@user)
if @subject.project.team.member?(@user)
can! :read_project_snippet
end
end
......
......@@ -16,7 +16,7 @@ class AkismetService
created_at: DateTime.now,
author: owner.name,
author_email: owner.email,
referrer: options[:referrer],
referrer: options[:referrer]
}
begin
......
......@@ -8,7 +8,7 @@ class AuditEventService
with: @details[:with],
target_id: @author.id,
target_type: 'User',
target_details: @author.name,
target_details: @author.name
}
self
......
......@@ -38,7 +38,7 @@ module Boards
attrs.merge!(
add_label_ids: add_label_ids,
remove_label_ids: remove_label_ids,
state_event: issue_state,
state_event: issue_state
)
end
......
......@@ -5,6 +5,8 @@ module Ci
def execute(pipeline)
@pipeline = pipeline
update_retried
new_builds =
stage_indexes_of_created_builds.map do |index|
process_stage(index)
......@@ -71,5 +73,23 @@ module Ci
def created_builds
pipeline.builds.created
end
# This method is for compatibility and data consistency and should be removed with 9.3 version of GitLab
# This replicates what is db/post_migrate/20170416103934_upate_retried_for_ci_build.rb
# and ensures that functionality will not be broken before migration is run
# this updates only when there are data that needs to be updated, there are two groups with no retried flag
def update_retried
# find the latest builds for each name
latest_statuses = pipeline.statuses.latest
.group(:name)
.having('count(*) > 1')
.pluck('max(id)', 'name')
# mark builds that are retried
pipeline.statuses.latest
.where(name: latest_statuses.map(&:second))
.where.not(id: latest_statuses.map(&:first))
.update_all(retried: true) if latest_statuses.any?
end
end
end
......@@ -6,7 +6,7 @@ module Ci
description tag_list].freeze
def execute(build)
reprocess(build).tap do |new_build|
reprocess!(build).tap do |new_build|
build.pipeline.mark_as_processable_after_stage(build.stage_idx)
new_build.enqueue!
......@@ -17,7 +17,7 @@ module Ci
end
end
def reprocess(build)
def reprocess!(build)
unless can?(current_user, :update_build, build)
raise Gitlab::Access::AccessDeniedError
end
......@@ -28,7 +28,14 @@ module Ci
attributes.push([:user, current_user])
project.builds.create(Hash[attributes])
Ci::Build.transaction do
# mark all other builds of that name as retried
build.pipeline.builds.latest
.where(name: build.name)
.update_all(retried: true)
project.builds.create!(Hash[attributes])
end
end
end
end
......@@ -11,7 +11,7 @@ module Ci
next unless can?(current_user, :update_build, build)
Ci::RetryBuildService.new(project, current_user)
.reprocess(build)
.reprocess!(build)
end
pipeline.builds.latest.skipped.find_each do |skipped|
......
......@@ -298,7 +298,7 @@ class NotificationService
recipients ||= NotificationRecipientService.new(pipeline.project).build_pipeline_recipients(
pipeline,
pipeline.user,
action: pipeline.status,
action: pipeline.status
).map(&:notification_email)
if recipients.any?
......
......@@ -27,7 +27,7 @@ module Projects
{
domain: domain.domain,
certificate: domain.certificate,
key: domain.key,
key: domain.key
}
end
end
......
......@@ -7,7 +7,7 @@ module Search
end
def execute
snippets = Snippet.accessible_to(current_user)
snippets = SnippetsFinder.new(current_user).execute
Gitlab::SnippetSearchResults.new(snippets, params[:search])
end
......
......@@ -51,7 +51,7 @@ class SystemHooksService
path: model.path,
group_id: model.id,
owner_name: owner.respond_to?(:name) ? owner.name : nil,
owner_email: owner.respond_to?(:email) ? owner.email : nil,
owner_email: owner.respond_to?(:email) ? owner.email : nil
)
when GroupMember
data.merge!(group_member_data(model))
......@@ -113,7 +113,7 @@ class SystemHooksService
user_name: model.user.name,
user_email: model.user.email,
user_id: model.user.id,
group_access: model.human_access,
group_access: model.human_access
}
end
end
......@@ -3,7 +3,7 @@
.diff-file.file-holder
.js-file-title.file-title
= render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_file.content_commit, project: discussion.project, url: discussion_path(discussion)
= render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_file.content_commit, project: discussion.project, url: discussion_path(discussion), show_toggle: false
.diff-content.code.js-syntax-highlight
%table
......
......@@ -10,4 +10,4 @@
- else
:plain
job = $("tr#repo_#{@repo_id}")
job.find(".import-actions").html("<i class='fa fa-exclamation-circle'></i> Error saving project: #{escape_javascript(@project.errors.full_messages.join(','))}")
job.find(".import-actions").html("<i class='fa fa-exclamation-circle'></i> Error saving project: #{escape_javascript(h(@project.errors.full_messages.join(',')))}")
......@@ -2,9 +2,9 @@
%article.readme-holder
.pull-right
- if can?(current_user, :push_code, @project)
= link_to icon('pencil'), namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.name)), class: 'light edit-project-readme'
.file-content.wiki
= markup(readme.name, readme.data, rendered: @repository.rendered_readme)
= link_to icon('pencil'), namespace_project_edit_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.path)), class: 'light edit-project-readme'
= render 'projects/blob/viewer', viewer: readme.rich_viewer, viewer_url: namespace_project_blob_path(@project.namespace, @project, tree_join(@repository.root_ref, readme.path), viewer: :rich, format: :json)
- else
.row-content-block.second-block.center
%h3.page-title
......
- @gfm_form = true
- current_text ||= nil
- supports_slash_commands = local_assigns.fetch(:supports_slash_commands, false)
.zen-backdrop
- classes << ' js-gfm-input js-autosize markdown-area'
- if defined?(f) && f
= f.text_area attr, class: classes, placeholder: placeholder, data: { supports_slash_commands: supports_slash_commands }
- else
= text_area_tag attr, nil, class: classes, placeholder: placeholder
= text_area_tag attr, current_text, class: classes, placeholder: placeholder
%a.zen-control.zen-control-leave.js-zen-leave{ href: "#" }
= icon('compress')
......@@ -2,8 +2,8 @@
- render_error = viewer.render_error
- load_asynchronously = local_assigns.fetch(:load_asynchronously, viewer.server_side?) && render_error.nil?
- url = url_for(params.merge(viewer: viewer.type, format: :json)) if load_asynchronously
.blob-viewer{ data: { type: viewer.type, url: url }, class: ('hidden' if hidden) }
- viewer_url = local_assigns.fetch(:viewer_url) { url_for(params.merge(viewer: viewer.type, format: :json)) } if load_asynchronously
.blob-viewer{ data: { type: viewer.type, url: viewer_url }, class: ('hidden' if hidden) }
- if load_asynchronously
.text-center.prepend-top-default.append-bottom-default
= icon('spinner spin 2x', 'aria-hidden' => 'true', 'aria-label' => 'Loading content')
......
......@@ -16,7 +16,8 @@
name: "issue[milestone_id]",
"v-if" => "issue.milestone" }
.dropdown
%button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: "true" },
%button.dropdown-menu-toggle.js-milestone-select.js-issue-board-sidebar{ type: "button", data: { toggle: "dropdown", show_no: "true", field_name: "issue[milestone_id]", project_id: @project.id, milestones: namespace_project_milestones_path(@project.namespace, @project, :json), ability_name: "issue", use_id: "true", default_no: "true" },
":data-selected" => "milestoneTitle",
":data-issuable-id" => "issue.id",
":data-issue-update" => "'#{namespace_project_issues_path(@project.namespace, @project)}/' + issue.id + '.json'" }
Milestone
......
......@@ -51,7 +51,7 @@
%ul
%li.stage-header
%span.stage-name
{{ __('ProjectLifecycle|Stage') }}
{{ s__('ProjectLifecycle|Stage') }}
%i.has-tooltip.fa.fa-question-circle{ "data-placement" => "top", title: _("The phase of the development lifecycle."), "aria-hidden" => "true" }
%li.median-header
%span.stage-name
......
%i.fa.diff-toggle-caret.fa-fw
- show_toggle = local_assigns.fetch(:show_toggle, true)
- if show_toggle
%i.fa.diff-toggle-caret.fa-fw
- if defined?(blob) && blob && diff_file.submodule?
%span
= icon('archive fw')
......
......@@ -10,7 +10,7 @@
.panel-body
%pre
:preserve
#{sanitize_repo_path(@project, @project.import_error)}
#{h(sanitize_repo_path(@project, @project.import_error))}
= form_for @project, url: namespace_project_import_path(@project.namespace, @project), method: :post, html: { class: 'form-horizontal' } do |f|
= render "shared/import_form", f: f
......
......@@ -22,14 +22,14 @@
.form-group
= label_tag :message, nil, class: 'control-label'
.col-sm-10
= text_area_tag :message, nil, required: false, tabindex: 3, class: 'form-control', rows: 5
= text_area_tag :message, @message, required: false, tabindex: 3, class: 'form-control', rows: 5
.help-block Optionally, add a message to the tag.
%hr
.form-group
= label_tag :release_description, 'Release notes', class: 'control-label'
.col-sm-10
= render layout: 'projects/md_preview', locals: { url: preview_markdown_path(@project), referenced_users: true } do
= render 'projects/zen', attr: :release_description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here..."
= render 'projects/zen', attr: :release_description, classes: 'note-textarea', placeholder: "Write your release notes or drag files here...", current_text: @release_description
= render 'shared/notes/hints'
.help-block Optionally, add release notes to the tag. They will be stored in the GitLab database and displayed on the tags page.
.form-actions
......
%article.file-holder.readme-holder
.js-file-title.file-title
= blob_icon readme.mode, readme.name
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, @path, readme.name)) do
= link_to namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, readme.path)) do
%strong
= readme.name
.file-content.wiki
= markup(readme.name, readme.data)
= render 'projects/blob/viewer', viewer: readme.rich_viewer, viewer_url: namespace_project_blob_path(@project.namespace, @project, tree_join(@ref, readme.path), viewer: :rich, format: :json)
......@@ -28,7 +28,7 @@
%h3 Clone your wiki
%pre.dark
:preserve
git clone #{ content_tag(:span, default_url_to_repo(@project_wiki), class: 'clone')}
git clone #{ content_tag(:span, h(default_url_to_repo(@project_wiki)), class: 'clone')}
cd #{h @project_wiki.path}
%h3 Start Gollum and edit locally
......
......@@ -3,10 +3,10 @@
- has_button = button_path || project_select_button
.row.empty-state
.pull-right.col-xs-12{ class: "#{'col-sm-6' if has_button}" }
.col-xs-12
.svg-content
= render 'shared/empty_states/icons/issues.svg'
.col-xs-12{ class: "#{'col-sm-6' if has_button}" }
.col-xs-12.text-center
.text-content
- if has_button && current_user
%h4
......
.row.empty-state.labels
.pull-right.col-xs-12.col-sm-6
.col-xs-12
.svg-content
= render 'shared/empty_states/icons/labels.svg'
.col-xs-12.col-sm-6
.col-xs-12.text-center
.text-content
%h4 Labels can be applied to issues and merge requests to categorize them.
%p You can also star a label to make it a priority label.
......
......@@ -3,10 +3,10 @@
- has_button = button_path || project_select_button
.row.empty-state.merge-requests
.col-xs-12{ class: "#{'col-sm-6 pull-right' if has_button}" }
.col-xs-12
.svg-content
= render 'shared/empty_states/icons/merge_requests.svg'
.col-xs-12{ class: "#{'col-sm-6' if has_button}" }
.col-xs-12.text-center
.text-content
- if has_button
%h4
......
......@@ -6,7 +6,7 @@
- if selected.present? || params[:milestone_title].present?
= hidden_field_tag(name, name == :milestone_title ? selected_text : selected.id)
= dropdown_tag(milestone_dropdown_label(selected_text), options: { title: dropdown_title, toggle_class: "js-milestone-select js-filter-submit #{extra_class}", filter: true, dropdown_class: "dropdown-menu-selectable dropdown-menu-milestone",
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, show_started: show_started, field_name: name, selected: selected.try(:title), project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
placeholder: "Search milestones", footer_content: project.present?, data: { show_no: true, show_menu_above: show_menu_above, show_any: show_any, show_upcoming: show_upcoming, show_started: show_started, field_name: name, selected: selected_text, project_id: project.try(:id), milestones: milestones_filter_dropdown_path, default_label: "Milestone" } }) do
- if project
%ul.dropdown-footer-list
- if can? current_user, :admin_milestone, project
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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