mirror of
https://github.com/wahyd4/gitlabhq.git
synced 2026-08-15 15:46:08 +10:00
Merge branch 'master' of gitlab.com:gitlab-org/gitlab-ce into feature/project-export-ui-experimental
This commit is contained in:
@@ -115,6 +115,11 @@ bundler:audit:
|
||||
script:
|
||||
- "bundle exec bundle-audit check --update --ignore OSVDB-115941"
|
||||
|
||||
db-migrate-reset:
|
||||
stage: test
|
||||
script:
|
||||
- RAILS_ENV=test bundle exec rake db:migrate:reset
|
||||
|
||||
# Ruby 2.2 jobs
|
||||
|
||||
spec:feature:ruby22:
|
||||
|
||||
@@ -3,6 +3,8 @@ Please view this file on the master branch, on stable branches it's out of date.
|
||||
v 8.8.0 (unreleased)
|
||||
|
||||
v 8.7.0 (unreleased)
|
||||
- Gitlab::GitAccess and Gitlab::GitAccessWiki are now instrumented
|
||||
- Fix vulnerability that made it possible to gain access to private labels and milestones
|
||||
- The number of InfluxDB points stored per UDP packet can now be configured
|
||||
- Fix error when cross-project label reference used with non-existent project
|
||||
- Transactions for /internal/allowed now have an "action" tag set
|
||||
@@ -53,7 +55,7 @@ v 8.7.0 (unreleased)
|
||||
- Add links to CI setup documentation from project settings and builds pages
|
||||
- Display project members page to all members
|
||||
- Handle nil descriptions in Slack issue messages (Stan Hu)
|
||||
- Add automated repository integrity checks
|
||||
- Add automated repository integrity checks (OFF by default)
|
||||
- API: Expose open_issues_count, closed_issues_count, open_merge_requests_count for labels (Robert Schilling)
|
||||
- API: Ability to star and unstar a project (Robert Schilling)
|
||||
- Add default scope to projects to exclude projects pending deletion
|
||||
@@ -80,6 +82,7 @@ v 8.7.0 (unreleased)
|
||||
- Remove "Congratulations!" tweet button on newly-created project. (Connor Shea)
|
||||
- Fix admin/projects when using visibility levels on search (PotHix)
|
||||
- Build status notifications
|
||||
- Update email confirmation interface
|
||||
- API: Expose user location (Robert Schilling)
|
||||
- API: Do not leak group existence via return code (Robert Schilling)
|
||||
- ClosingIssueExtractor regex now also works with colons. e.g. "Fixes: #1234" !3591
|
||||
@@ -107,6 +110,7 @@ v 8.7.0 (unreleased)
|
||||
- Updated print style for issues
|
||||
- Use GitHub Issue/PR number as iid to keep references
|
||||
- Import GitHub labels
|
||||
- Add option to filter by "Owned projects" on dashboard page
|
||||
- Import GitHub milestones
|
||||
- Fix emoji catgories in the emoji picker
|
||||
- Execute system web hooks on push to the project
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class @CommitsList
|
||||
@timer = null
|
||||
|
||||
@init: (ref, limit) ->
|
||||
@init: (limit) ->
|
||||
$("body").on "click", ".day-commits-table li.commit", (event) ->
|
||||
if event.target.nodeName != "A"
|
||||
location.href = $(this).attr("url")
|
||||
|
||||
@@ -221,6 +221,9 @@ class GitLabDropdown
|
||||
|
||||
menu.toggleClass PAGE_TWO_CLASS
|
||||
|
||||
# Focus first visible input on active page
|
||||
@dropdown.find('[class^="dropdown-page-"]:visible :text:visible:first').focus()
|
||||
|
||||
parseData: (data) ->
|
||||
@renderedData = data
|
||||
|
||||
@@ -240,7 +243,8 @@ class GitLabDropdown
|
||||
shouldPropagate: (e) =>
|
||||
if @options.multiSelect
|
||||
$target = $(e.target)
|
||||
if not $target.hasClass('dropdown-menu-close') and not $target.hasClass('dropdown-menu-close-icon')
|
||||
|
||||
if not $target.hasClass('dropdown-menu-close') and not $target.hasClass('dropdown-menu-close-icon') and not $target.data('is-link')
|
||||
e.stopPropagation()
|
||||
return false
|
||||
else
|
||||
@@ -375,7 +379,6 @@ class GitLabDropdown
|
||||
selectedObject = @renderedData[selectedIndex]
|
||||
value = if @options.id then @options.id(selectedObject, el) else selectedObject.id
|
||||
field = @dropdown.parent().find("input[name='#{fieldName}'][value='#{value}']")
|
||||
|
||||
if el.hasClass(ACTIVE_CLASS)
|
||||
el.removeClass(ACTIVE_CLASS)
|
||||
field.remove()
|
||||
|
||||
@@ -19,23 +19,19 @@ class @LabelsSelect
|
||||
$form = $dropdown.closest('form')
|
||||
$sidebarCollapsedValue = $block.find('.sidebar-collapsed-icon span')
|
||||
$value = $block.find('.value')
|
||||
$newLabelError = $('.js-label-error')
|
||||
$colorPreview = $('.js-dropdown-label-color-preview')
|
||||
$newLabelCreateButton = $('.js-new-label-btn')
|
||||
|
||||
$newLabelError.hide()
|
||||
$loading = $block.find('.block-loading').fadeOut()
|
||||
|
||||
if newLabelField.length
|
||||
$newLabelCreateButton = $('.js-new-label-btn')
|
||||
$colorPreview = $('.js-dropdown-label-color-preview')
|
||||
$newLabelError = $dropdown.parent().find('.js-label-error')
|
||||
$newLabelError.hide()
|
||||
|
||||
# Suggested colors in the dropdown to chose from pre-chosen colors
|
||||
$('.suggest-colors-dropdown a').on 'click', (e) ->
|
||||
|
||||
issueURLSplit = issueUpdateURL.split('/') if issueUpdateURL?
|
||||
if issueUpdateURL
|
||||
labelHTMLTemplate = _.template(
|
||||
'<% _.each(labels, function(label){ %>
|
||||
<a href="<%= ["",issueURLSplit[1], issueURLSplit[2],""].join("/") %>issues?label_name=<%= _.escape(label.title) %>">
|
||||
<span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>;">
|
||||
<span class="label has-tooltip color-label" title="<%= _.escape(label.description) %>" style="background-color: <%= label.color %>; color: <%= label.text_color %>;">
|
||||
<%= _.escape(label.title) %>
|
||||
</span>
|
||||
</a>
|
||||
@@ -43,7 +39,9 @@ class @LabelsSelect
|
||||
)
|
||||
labelNoneHTMLTemplate = _.template('<div class="light">None</div>')
|
||||
|
||||
if newLabelField.length and $dropdown.hasClass 'js-extra-options'
|
||||
if newLabelField.length
|
||||
|
||||
# Suggested colors in the dropdown to chose from pre-chosen colors
|
||||
$('.suggest-colors-dropdown a').on "click", (e) ->
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
@@ -82,26 +80,25 @@ class @LabelsSelect
|
||||
enableLabelCreateButton = ->
|
||||
if newLabelField.val() isnt '' and newColorField.val() isnt ''
|
||||
$newLabelError.hide()
|
||||
$('.js-new-label-btn').disable()
|
||||
|
||||
# Create new label with API
|
||||
Api.newLabel projectId, {
|
||||
name: newLabelField.val()
|
||||
color: newColorField.val()
|
||||
}, (label) ->
|
||||
$('.js-new-label-btn').enable()
|
||||
|
||||
if label.message?
|
||||
$newLabelError
|
||||
.text label.message
|
||||
.show()
|
||||
else
|
||||
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
|
||||
|
||||
$newLabelCreateButton.enable()
|
||||
else
|
||||
$newLabelCreateButton.disable()
|
||||
|
||||
saveLabel = ->
|
||||
# Create new label with API
|
||||
Api.newLabel projectId, {
|
||||
name: newLabelField.val()
|
||||
color: newColorField.val()
|
||||
}, (label) ->
|
||||
$newLabelCreateButton.enable()
|
||||
|
||||
if label.message?
|
||||
$newLabelError
|
||||
.text label.message
|
||||
.show()
|
||||
else
|
||||
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
|
||||
|
||||
newLabelField.on 'keyup change', enableLabelCreateButton
|
||||
|
||||
newColorField.on 'keyup change', enableLabelCreateButton
|
||||
@@ -112,24 +109,7 @@ class @LabelsSelect
|
||||
.on 'click', (e) ->
|
||||
e.preventDefault()
|
||||
e.stopPropagation()
|
||||
|
||||
if newLabelField.val() isnt '' and newColorField.val() isnt ''
|
||||
$newLabelError.hide()
|
||||
$('.js-new-label-btn').disable()
|
||||
|
||||
# Create new label with API
|
||||
Api.newLabel projectId, {
|
||||
name: newLabelField.val()
|
||||
color: newColorField.val()
|
||||
}, (label) ->
|
||||
$('.js-new-label-btn').enable()
|
||||
|
||||
if label.message?
|
||||
$newLabelError
|
||||
.text label.message
|
||||
.show()
|
||||
else
|
||||
$('.dropdown-menu-back', $dropdown.parent()).trigger 'click'
|
||||
saveLabel()
|
||||
|
||||
saveLabelData = ->
|
||||
selected = $dropdown
|
||||
|
||||
@@ -87,8 +87,8 @@ class @MergeRequestTabs
|
||||
if window.location.hash
|
||||
navBarHeight = $('.navbar-gitlab').outerHeight()
|
||||
|
||||
$el = $("#{container} #{window.location.hash}")
|
||||
$.scrollTo("#{container} #{window.location.hash}", offset: -navBarHeight) if $el.length
|
||||
$el = $("#{container} #{window.location.hash}:not(.match)")
|
||||
$.scrollTo("#{container} #{window.location.hash}:not(.match)", offset: -navBarHeight) if $el.length
|
||||
|
||||
# Activate a tab based on the current action
|
||||
activateTab: (action) ->
|
||||
@@ -176,12 +176,12 @@ class @MergeRequestTabs
|
||||
|
||||
if locationHash isnt ''
|
||||
hashClassString = ".#{locationHash.replace('#', '')}"
|
||||
$diffLine = $(locationHash)
|
||||
$diffLine = $("#{locationHash}:not(.match)", $('#diffs'))
|
||||
|
||||
if $diffLine.is ':not(tr)'
|
||||
$diffLine = $("td#{locationHash}, td#{hashClassString}")
|
||||
if not $diffLine.is 'tr'
|
||||
$diffLine = $('#diffs').find("td#{locationHash}, td#{hashClassString}")
|
||||
else
|
||||
$diffLine = $('td', $diffLine)
|
||||
$diffLine = $diffLine.find('td')
|
||||
|
||||
if $diffLine.length
|
||||
$diffLine.addClass 'hll'
|
||||
|
||||
@@ -144,6 +144,10 @@
|
||||
}
|
||||
}
|
||||
|
||||
.btn-lg {
|
||||
padding: 12px 20px;
|
||||
}
|
||||
|
||||
.btn-transparent {
|
||||
color: $btn-transparent-color;
|
||||
background-color: transparent;
|
||||
|
||||
@@ -320,7 +320,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
.dropdown-input-field {
|
||||
.dropdown-input-field, .default-dropdown-input {
|
||||
width: 100%;
|
||||
padding: 0 7px;
|
||||
color: $dropdown-input-color;
|
||||
|
||||
@@ -38,12 +38,14 @@
|
||||
|
||||
.filename {
|
||||
&.old {
|
||||
display: inline-block;
|
||||
span.idiff {
|
||||
background-color: #f8cbcb;
|
||||
}
|
||||
}
|
||||
|
||||
&.new {
|
||||
display: inline-block;
|
||||
span.idiff {
|
||||
background-color: #a6f3a6;
|
||||
}
|
||||
@@ -129,6 +131,11 @@
|
||||
td.line-numbers {
|
||||
float: none;
|
||||
border-left: 1px solid #ddd;
|
||||
|
||||
i {
|
||||
float: none;
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
td.lines {
|
||||
padding: 0;
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
.well-confirmation {
|
||||
margin-bottom: 20px;
|
||||
border-bottom: 1px solid #eee;
|
||||
|
||||
> h1 {
|
||||
font-weight: 400;
|
||||
}
|
||||
|
||||
.lead {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
.confirmation-content {
|
||||
a {
|
||||
color: $md-link-color;
|
||||
}
|
||||
}
|
||||
@@ -249,6 +249,10 @@
|
||||
background: $gray-dark;
|
||||
border: 1px solid $border-gray-dark;
|
||||
}
|
||||
|
||||
&.btn-primary {
|
||||
@extend .btn-primary
|
||||
}
|
||||
}
|
||||
|
||||
a:not(.issuable-pager) {
|
||||
|
||||
@@ -183,6 +183,9 @@ ul.notes {
|
||||
}
|
||||
}
|
||||
|
||||
.author_link {
|
||||
color: $gl-gray;
|
||||
}
|
||||
}
|
||||
|
||||
.note-headline-light,
|
||||
|
||||
@@ -10,6 +10,8 @@ module FilterProjects
|
||||
def filter_projects(projects)
|
||||
projects = projects.search(params[:filter_projects]) if params[:filter_projects].present?
|
||||
projects = projects.non_archived if params[:archived].blank?
|
||||
projects = projects.personal(current_user) if params[:personal].present? && current_user
|
||||
|
||||
projects
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,7 +1,16 @@
|
||||
class ConfirmationsController < Devise::ConfirmationsController
|
||||
|
||||
def almost_there
|
||||
flash[:notice] = nil
|
||||
render layout: "devise_empty"
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def after_resending_confirmation_instructions_path_for(resource)
|
||||
users_almost_there_path
|
||||
end
|
||||
|
||||
def after_confirmation_path_for(resource_name, resource)
|
||||
if signed_in?(resource_name)
|
||||
after_sign_in_path_for(resource)
|
||||
|
||||
@@ -83,8 +83,7 @@ class Projects::ApplicationController < ApplicationController
|
||||
end
|
||||
|
||||
def apply_diff_view_cookie!
|
||||
view = params[:view] || cookies[:diff_view]
|
||||
cookies.permanent[:diff_view] = params[:view] = view if view
|
||||
cookies.permanent[:diff_view] = params.delete(:view) if params[:view].present?
|
||||
end
|
||||
|
||||
def builds_enabled
|
||||
|
||||
@@ -101,7 +101,6 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
end
|
||||
|
||||
respond_to do |format|
|
||||
format.js
|
||||
format.html do
|
||||
if @issue.valid?
|
||||
redirect_to issue_path(@issue)
|
||||
@@ -110,7 +109,7 @@ class Projects::IssuesController < Projects::ApplicationController
|
||||
end
|
||||
end
|
||||
format.json do
|
||||
render json: @issue.to_json(include: [:milestone, :labels, assignee: { methods: :avatar_url }])
|
||||
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -149,13 +149,12 @@ class Projects::MergeRequestsController < Projects::ApplicationController
|
||||
|
||||
if @merge_request.valid?
|
||||
respond_to do |format|
|
||||
format.js
|
||||
format.html do
|
||||
redirect_to([@merge_request.target_project.namespace.becomes(Namespace),
|
||||
@merge_request.target_project, @merge_request])
|
||||
end
|
||||
format.json do
|
||||
render json: @merge_request.to_json(include: [:milestone, :labels, assignee: { methods: :avatar_url }])
|
||||
render json: @merge_request.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
|
||||
end
|
||||
end
|
||||
else
|
||||
|
||||
@@ -31,11 +31,11 @@ class RegistrationsController < Devise::RegistrationsController
|
||||
end
|
||||
|
||||
def after_sign_up_path_for(_resource)
|
||||
new_user_session_path
|
||||
users_almost_there_path
|
||||
end
|
||||
|
||||
def after_inactive_sign_up_path_for(_resource)
|
||||
new_user_session_path
|
||||
users_almost_there_path
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
@@ -278,9 +278,7 @@ class IssuableFinder
|
||||
end
|
||||
end
|
||||
|
||||
# When filtering by multiple labels we may end up duplicating issues (if one
|
||||
# has multiple labels). This ensures we only return unique issues.
|
||||
items.distinct
|
||||
items
|
||||
end
|
||||
|
||||
def by_due_date(items)
|
||||
|
||||
@@ -9,7 +9,13 @@ module DiffHelper
|
||||
end
|
||||
|
||||
def diff_view
|
||||
params[:view] == 'parallel' ? 'parallel' : 'inline'
|
||||
diff_views = %w(inline parallel)
|
||||
|
||||
if diff_views.include?(cookies[:diff_view])
|
||||
cookies[:diff_view]
|
||||
else
|
||||
diff_views.first
|
||||
end
|
||||
end
|
||||
|
||||
def diff_hard_limit_enabled?
|
||||
|
||||
@@ -37,8 +37,9 @@ class IssuableBaseService < BaseService
|
||||
end
|
||||
|
||||
def filter_params(issuable_ability_name = :issue)
|
||||
params[:assignee_id] = "" if params[:assignee_id] == IssuableFinder::NONE
|
||||
params[:milestone_id] = "" if params[:milestone_id] == IssuableFinder::NONE
|
||||
filter_assignee
|
||||
filter_milestone
|
||||
filter_labels
|
||||
|
||||
ability = :"admin_#{issuable_ability_name}"
|
||||
|
||||
@@ -49,6 +50,29 @@ class IssuableBaseService < BaseService
|
||||
end
|
||||
end
|
||||
|
||||
def filter_assignee
|
||||
if params[:assignee_id] == IssuableFinder::NONE
|
||||
params[:assignee_id] = ''
|
||||
end
|
||||
end
|
||||
|
||||
def filter_milestone
|
||||
milestone_id = params[:milestone_id]
|
||||
return unless milestone_id
|
||||
|
||||
if milestone_id == IssuableFinder::NONE ||
|
||||
project.milestones.find_by(id: milestone_id).nil?
|
||||
params[:milestone_id] = ''
|
||||
end
|
||||
end
|
||||
|
||||
def filter_labels
|
||||
return if params[:label_ids].to_a.empty?
|
||||
|
||||
params[:label_ids] =
|
||||
project.labels.where(id: params[:label_ids]).pluck(:id)
|
||||
end
|
||||
|
||||
def update(issuable)
|
||||
change_state(issuable)
|
||||
filter_params
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
.well-confirmation.text-center
|
||||
%h1.prepend-top-0
|
||||
Almost there...
|
||||
%p.lead
|
||||
Please check your email to confirm your account
|
||||
%p.confirmation-content.text-center
|
||||
No confirmation email received? Please check your spam folder or
|
||||
.append-bottom-20.prepend-top-20.text-center
|
||||
%a.btn.btn-lg.btn-success{ href: new_user_confirmation_path }
|
||||
Request new confirmation email
|
||||
@@ -0,0 +1,17 @@
|
||||
!!! 5
|
||||
%html{ lang: "en"}
|
||||
= render "layouts/head"
|
||||
%body.ui_charcoal.login-page.application.navless
|
||||
= render "layouts/header/empty"
|
||||
= render "layouts/broadcast"
|
||||
.container.navless-container
|
||||
.content
|
||||
= render "layouts/flash"
|
||||
= yield
|
||||
|
||||
%hr
|
||||
.container
|
||||
.footer-links
|
||||
= link_to "Explore", explore_root_path
|
||||
= link_to "Help", help_path
|
||||
= link_to "About GitLab", "https://about.gitlab.com/"
|
||||
@@ -1,17 +1,17 @@
|
||||
- if @lines.present?
|
||||
- if @form.unfold? && @form.since != 1 && !@form.bottom?
|
||||
%tr.line_holder{ id: @form.since }
|
||||
%tr.line_holder
|
||||
= render "projects/diffs/match_line", { line: @match_line,
|
||||
line_old: @form.since, line_new: @form.since, bottom: false, new_file: false }
|
||||
|
||||
- @lines.each_with_index do |line, index|
|
||||
- line_new = index + @form.since
|
||||
- line_old = line_new - @form.offset
|
||||
%tr.line_holder
|
||||
%tr.line_holder{ id: line_old }
|
||||
%td.old_line.diff-line-num{ data: { linenumber: line_old } }
|
||||
= link_to raw(line_old), "#"
|
||||
= link_to raw(line_old), "##{line_old}"
|
||||
%td.new_line.diff-line-num{ data: { linenumber: line_old } }
|
||||
= link_to raw(line_new) , "#"
|
||||
= link_to raw(line_new) , "##{line_old}"
|
||||
%td.line_content.noteable_line==#{' ' * @form.indent}#{line}
|
||||
|
||||
- if @form.unfold? && @form.bottom? && @form.to < @blob.loc
|
||||
|
||||
@@ -39,4 +39,4 @@
|
||||
= spinner
|
||||
|
||||
:javascript
|
||||
CommitsList.init("#{@ref}", #{@limit});
|
||||
CommitsList.init(#{@limit});
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
- page_card_attributes @merge_request.card_attributes
|
||||
- header_title project_title(@project, "Merge Requests", namespace_project_merge_requests_path(@project.namespace, @project))
|
||||
|
||||
- if params[:view] == 'parallel'
|
||||
- if diff_view == 'parallel'
|
||||
- fluid_layout true
|
||||
|
||||
.merge-request{'data-url' => merge_request_path(@merge_request)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
- page_title "#{@merge_request.title} (#{merge_request.to_reference}", "Merge Requests"
|
||||
- page_title "#{@merge_request.title} (#{@merge_request.to_reference}", "Merge Requests"
|
||||
= render "header_title"
|
||||
|
||||
.merge-request
|
||||
|
||||
@@ -8,39 +8,7 @@
|
||||
= h(multi_label_name(params[:label_name], "Label"))
|
||||
= icon('chevron-down')
|
||||
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
|
||||
.dropdown-page-one
|
||||
= dropdown_title("Filter by label")
|
||||
= dropdown_filter("Search labels")
|
||||
= dropdown_content
|
||||
- if @project
|
||||
= dropdown_footer do
|
||||
%ul.dropdown-footer-list
|
||||
- if can? current_user, :admin_label, @project
|
||||
%li
|
||||
%a.dropdown-toggle-page{href: "#"}
|
||||
Create new
|
||||
%li
|
||||
= link_to namespace_project_labels_path(@project.namespace, @project) do
|
||||
- if can? current_user, :admin_label, @project
|
||||
Manage labels
|
||||
- else
|
||||
View labels
|
||||
= render partial: "shared/issuable/label_page_default", locals: { title: "Filter by label" }
|
||||
- if can? current_user, :admin_label, @project and @project
|
||||
.dropdown-page-two.dropdown-new-label
|
||||
= dropdown_title("Create new label", back: true)
|
||||
= dropdown_content do
|
||||
.dropdown-labels-error.js-label-error
|
||||
%input#new_label_name.dropdown-input-field{type: "text", placeholder: "Name new label"}
|
||||
.suggest-colors.suggest-colors-dropdown
|
||||
- suggested_colors.each do |color|
|
||||
= link_to '#', style: "background-color: #{color}", data: { color: color } do
|
||||
 
|
||||
.dropdown-label-color-input
|
||||
.dropdown-label-color-preview.js-dropdown-label-color-preview
|
||||
%input#new_label_color.dropdown-input-field{ type: "text" }
|
||||
.clearfix
|
||||
%button.btn.btn-primary.pull-left.js-new-label-btn{type: "button"}
|
||||
Create
|
||||
%button.btn.btn-default.pull-right.js-cancel-label-btn{type: "button"}
|
||||
Cancel
|
||||
= render partial: "shared/issuable/label_page_create"
|
||||
= dropdown_loading
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
.dropdown-page-two.dropdown-new-label
|
||||
= dropdown_title("Create new label", back: true)
|
||||
= dropdown_content do
|
||||
.dropdown-labels-error.js-label-error
|
||||
%input#new_label_name.default-dropdown-input{ type: "text", placeholder: "Name new label" }
|
||||
.suggest-colors.suggest-colors-dropdown
|
||||
- suggested_colors.each do |color|
|
||||
= link_to '#', style: "background-color: #{color}", data: { color: color } do
|
||||
 
|
||||
.dropdown-label-color-input
|
||||
.dropdown-label-color-preview.js-dropdown-label-color-preview
|
||||
%input#new_label_color.default-dropdown-input{ type: "text" }
|
||||
.clearfix
|
||||
%button.btn.btn-primary.pull-left.js-new-label-btn{ type: "button" }
|
||||
Create
|
||||
%button.btn.btn-default.pull-right.js-cancel-label-btn{ type: "button" }
|
||||
Cancel
|
||||
@@ -0,0 +1,20 @@
|
||||
- title = local_assigns.fetch(:title, 'Assign labels')
|
||||
- filter_placeholder = local_assigns.fetch(:filter_placeholder, 'Search labels')
|
||||
.dropdown-page-one
|
||||
= dropdown_title(title)
|
||||
= dropdown_filter(filter_placeholder)
|
||||
= dropdown_content
|
||||
- if @project
|
||||
= dropdown_footer do
|
||||
%ul.dropdown-footer-list
|
||||
- if can? current_user, :admin_label, @project
|
||||
%li
|
||||
%a.dropdown-toggle-page{href: "#"}
|
||||
Create new
|
||||
%li
|
||||
= link_to namespace_project_labels_path(@project.namespace, @project), :"data-is-link" => true do
|
||||
- if can? current_user, :admin_label, @project
|
||||
Manage labels
|
||||
- else
|
||||
View labels
|
||||
= dropdown_loading
|
||||
@@ -20,7 +20,7 @@
|
||||
%a.btn.btn-default.issuable-pager.disabled{href: '#'}
|
||||
Next
|
||||
|
||||
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
|
||||
= form_for [@project.namespace.becomes(Namespace), @project, issuable], remote: true, format: :json, html: {class: 'issuable-context-form inline-update js-issuable-update'} do |f|
|
||||
.block.assignee
|
||||
.sidebar-collapsed-icon.sidebar-collapsed-user{data: {toggle: "tooltip", placement: "left", container: "body"}, title: (issuable.assignee.to_reference if issuable.assignee)}
|
||||
- if issuable.assignee
|
||||
@@ -129,24 +129,9 @@
|
||||
Label
|
||||
= icon('chevron-down')
|
||||
.dropdown-menu.dropdown-select.dropdown-menu-paging.dropdown-menu-labels.dropdown-menu-selectable
|
||||
.dropdown-page-one
|
||||
= dropdown_title("Assign labels")
|
||||
= dropdown_filter("Search labels")
|
||||
= dropdown_content
|
||||
- if @project
|
||||
= dropdown_footer do
|
||||
%ul.dropdown-footer-list
|
||||
- if can? current_user, :admin_label, @project
|
||||
%li
|
||||
%a.dropdown-toggle-page{href: "#"}
|
||||
Create new
|
||||
%li
|
||||
= link_to namespace_project_labels_path(@project.namespace, @project) do
|
||||
- if can? current_user, :admin_label, @project
|
||||
Manage labels
|
||||
- else
|
||||
View labels
|
||||
= dropdown_loading
|
||||
= render partial: "shared/issuable/label_page_default"
|
||||
- if can? current_user, :admin_label, @project and @project
|
||||
= render partial: "shared/issuable/label_page_create"
|
||||
|
||||
= render "shared/issuable/participants", participants: issuable.participants(current_user)
|
||||
- if current_user
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
- @sort ||= sort_value_recently_updated
|
||||
- personal = params[:personal]
|
||||
- archived = params[:archived]
|
||||
.dropdown.inline
|
||||
%button.dropdown-toggle.btn{type: 'button', 'data-toggle' => 'dropdown'}
|
||||
@@ -10,7 +11,7 @@
|
||||
Sort by
|
||||
- projects_sort_options_hash.each do |value, title|
|
||||
%li
|
||||
= link_to filter_projects_path(sort: value, archived: archived), class: ("is-active" if @sort == value) do
|
||||
= link_to filter_projects_path(sort: value, archived: archived, personal: personal), class: ("is-active" if @sort == value) do
|
||||
= title
|
||||
|
||||
%li.divider
|
||||
@@ -20,3 +21,11 @@
|
||||
%li
|
||||
= link_to filter_projects_path(sort: @sort, archived: true), class: ("is-active" if params[:archived].present?) do
|
||||
Show archived projects
|
||||
- if current_user
|
||||
%li.divider
|
||||
%li
|
||||
= link_to filter_projects_path(sort: @sort, personal: nil), class: ("is-active" unless personal) do
|
||||
Owned by anyone
|
||||
%li
|
||||
= link_to filter_projects_path(sort: @sort, personal: true), class: ("is-active" if personal) do
|
||||
Owned by me
|
||||
|
||||
@@ -107,6 +107,10 @@ if Gitlab::Metrics.enabled?
|
||||
config.instrument_methods(const)
|
||||
config.instrument_instance_methods(const)
|
||||
end
|
||||
|
||||
# Instrument the classes used for checking if somebody has push access.
|
||||
config.instrument_instance_methods(Gitlab::GitAccess)
|
||||
config.instrument_instance_methods(Gitlab::GitAccessWiki)
|
||||
end
|
||||
|
||||
GC::Profiler.enable
|
||||
|
||||
@@ -418,6 +418,7 @@ Rails.application.routes.draw do
|
||||
|
||||
devise_scope :user do
|
||||
get '/users/auth/:provider/omniauth_error' => 'omniauth_callbacks#omniauth_error', as: :omniauth_error
|
||||
get '/users/almost_there' => 'confirmations#almost_there'
|
||||
end
|
||||
|
||||
root to: "root#index"
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
class DisableRepositoryChecks < ActiveRecord::Migration
|
||||
def up
|
||||
change_column_default :application_settings, :repository_checks_enabled, false
|
||||
execute 'UPDATE application_settings SET repository_checks_enabled = false'
|
||||
end
|
||||
|
||||
def down
|
||||
change_column_default :application_settings, :repository_checks_enabled, true
|
||||
execute 'UPDATE application_settings SET repository_checks_enabled = true'
|
||||
end
|
||||
end
|
||||
+2
-2
@@ -11,7 +11,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20160419120017) do
|
||||
ActiveRecord::Schema.define(version: 20160421130527) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
@@ -77,7 +77,7 @@ ActiveRecord::Schema.define(version: 20160419120017) do
|
||||
t.string "akismet_api_key"
|
||||
t.boolean "email_author_in_body", default: false
|
||||
t.integer "default_group_visibility"
|
||||
t.boolean "repository_checks_enabled", default: true
|
||||
t.boolean "repository_checks_enabled", default: false
|
||||
t.integer "metrics_packet_size", default: 1
|
||||
t.text "shared_runners_text"
|
||||
end
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Repository checks
|
||||
|
||||
>**Note:**
|
||||
This feature was [introduced][ce-3232] in GitLab 8.7.
|
||||
This feature was [introduced][ce-3232] in GitLab 8.7. It is OFF by
|
||||
default because it still causes too many false alarms.
|
||||
|
||||
Git has a built-in mechanism, [git fsck][git-fsck], to verify the
|
||||
integrity of all data commited to a repository. GitLab administrators
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
# Downgrading from EE to CE
|
||||
|
||||
If you ever decide to downgrade your Enterprise Edition back to the Community
|
||||
Edition, there are a few steps you need take before installing the CE package
|
||||
on top of the current EE package, or, if you are in an installation from source,
|
||||
before you change remotes and fetch the latest CE code.
|
||||
|
||||
## Disable Enterprise-only features
|
||||
|
||||
First thing to do is to disable the following features.
|
||||
|
||||
### Authentication mechanisms
|
||||
|
||||
Kerberos and Atlassian Crowd are only available on the Enterprise Edition, so
|
||||
you should disable these mechanisms before downgrading and you should provide
|
||||
alternative authentication methods to your users.
|
||||
|
||||
### Git Annex
|
||||
|
||||
Git Annex is also only available on the Enterprise Edition. This means that if
|
||||
you have repositories that use Git Annex to store large files, these files will
|
||||
no longer be easily available via Git. You should consider migrating these
|
||||
repositories to use Git LFS before downgrading to the Community Edition.
|
||||
|
||||
### Remove Jenkins CI Service entries from the database
|
||||
|
||||
The `JenkinsService` class is only available on the Enterprise Edition codebase,
|
||||
so if you downgrade to the Community Edition, you'll come across the following
|
||||
error:
|
||||
|
||||
```
|
||||
Completed 500 Internal Server Error in 497ms (ActiveRecord: 32.2ms)
|
||||
|
||||
ActionView::Template::Error (The single-table inheritance mechanism failed to locate the subclass: 'JenkinsService'. This
|
||||
error is raised because the column 'type' is reserved for storing the class in case of inheritance. Please rename this
|
||||
column if you didn't intend it to be used for storing the inheritance class or overwrite Service.inheritance_column to
|
||||
use another column for that information.)
|
||||
```
|
||||
|
||||
All services are created automatically for every project you have, so in order
|
||||
to avoid getting this error, you need to remove all instances of the
|
||||
`JenkinsService` from your database:
|
||||
|
||||
**Omnibus Installation**
|
||||
|
||||
```
|
||||
$ sudo gitlab-rails runner "Service.where(type: 'JenkinsService').delete_all"
|
||||
```
|
||||
|
||||
**Source Installation**
|
||||
|
||||
```
|
||||
$ bundle exec rails runner "Service.where(type: 'JenkinsService').delete_all" production
|
||||
```
|
||||
|
||||
## Downgrade to CE
|
||||
|
||||
After performing the above mentioned steps, you are now ready to downgrade your
|
||||
GitLab installation to the Community Edition.
|
||||
|
||||
**Omnibus Installation**
|
||||
|
||||
To downgrade an Omnibus installation, it is sufficient to install the Community
|
||||
Edition package on top of the currently installed one. You can do this manually,
|
||||
by directly [downloading the package](https://packages.gitlab.com/gitlab/gitlab-ce)
|
||||
you need, or by adding our CE package repository and following the
|
||||
[CE installation instructions](https://about.gitlab.com/downloads/).
|
||||
|
||||
**Source Installation**
|
||||
|
||||
To downgrade a source installation, you need to replace the current remote of
|
||||
your GitLab installation with the Community Edition's remote, fetch the latest
|
||||
changes, and checkout the latest stable branch:
|
||||
|
||||
```
|
||||
$ git remote set-url origin git@gitlab.com:gitlab-org/gitlab-ce.git
|
||||
$ git fetch --all
|
||||
$ git checkout 8-x-stable
|
||||
```
|
||||
|
||||
Remember to follow the correct [update guides](../update/README.md) to make
|
||||
sure all dependencies are up to date.
|
||||
+85
-8
@@ -1,18 +1,95 @@
|
||||
Depending on the installation method and your GitLab version, there are multiple update guides. Choose one that fits your needs.
|
||||
# Updating GitLab
|
||||
|
||||
Depending on the installation method and your GitLab version, there are multiple
|
||||
update guides.
|
||||
|
||||
There are currently 3 official ways to install GitLab:
|
||||
|
||||
- Omnibus packages
|
||||
- Source installation
|
||||
- Docker installation
|
||||
|
||||
Based on your installation, choose a section below that fits your needs.
|
||||
|
||||
---
|
||||
|
||||
<!-- START doctoc generated TOC please keep comment here to allow auto update -->
|
||||
<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
|
||||
**Table of Contents** *generated with [DocToc](https://github.com/thlorenz/doctoc)*
|
||||
|
||||
- [Omnibus Packages](#omnibus-packages)
|
||||
- [Installation from source](#installation-from-source)
|
||||
- [Installation using Docker](#installation-using-docker)
|
||||
- [Upgrading between editions](#upgrading-between-editions)
|
||||
- [Community to Enterprise Edition](#community-to-enterprise-edition)
|
||||
- [Enterprise to Community Edition](#enterprise-to-community-edition)
|
||||
- [Miscellaneous](#miscellaneous)
|
||||
|
||||
<!-- END doctoc generated TOC please keep comment here to allow auto update -->
|
||||
|
||||
## Omnibus Packages
|
||||
|
||||
- [Omnibus update guide](https://gitlab.com/gitlab-org/omnibus-gitlab/blob/master/doc/update.md) contains the steps needed to update a GitLab [package](https://about.gitlab.com/downloads/).
|
||||
- The [Omnibus update guide](http://doc.gitlab.com/omnibus/update/README.html)
|
||||
contains the steps needed to update an Omnibus GitLab package.
|
||||
|
||||
## Installation from source
|
||||
|
||||
- [The individual upgrade guides](https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update) are for those who have installed GitLab from source.
|
||||
- [The CE to EE update guides](https://gitlab.com/subscribers/gitlab-ee/tree/master/doc/update) are for subscribers of the Enterprise Edition only. The steps are very similar to a version upgrade: stop the server, get the code, update config files for the new functionality, install libs and do migrations, update the init script, start the application and check the application status.
|
||||
- [Upgrader](upgrader.md) is an automatic ruby script that performs the update for installations from source.
|
||||
- [Patch versions](patch_versions.md) guide includes the steps needed for a patch version, eg. 6.2.0 to 6.2.1.
|
||||
- [Upgrading Community Edition from source][source-ce] - The individual
|
||||
upgrade guides are for those who have installed GitLab CE from source.
|
||||
- [Upgrading Enterprise Edition from source][source-ee] - The individual
|
||||
upgrade guides are for those who have installed GitLab EE from source.
|
||||
- [Patch versions](patch_versions.md) guide includes the steps needed for a
|
||||
patch version, eg. 6.2.0 to 6.2.1, and apply to both Community and Enterprise
|
||||
Editions.
|
||||
|
||||
## Installation using Docker
|
||||
|
||||
GitLab provides official Docker images for both Community and Enterprise
|
||||
editions. They are based on the Omnibus package and instructions on how to
|
||||
update them are in [a separate document][omnidocker].
|
||||
|
||||
## Upgrading between editions
|
||||
|
||||
GitLab comes in two flavors: [Community Edition][ce] which is MIT licensed,
|
||||
and [Enterprise Edition][ee] which builds on top of the Community Edition and
|
||||
includes extra features mainly aimed at organizations with more than 100 users.
|
||||
|
||||
Below you can find some guides to help you change editions easily.
|
||||
|
||||
### Community to Enterprise Edition
|
||||
|
||||
>**Note:**
|
||||
The following guides are for subscribers of the Enterprise Edition only.
|
||||
|
||||
If you wish to upgrade your GitLab installation from Community to Enterprise
|
||||
Edition, follow the guides below based on the installation method:
|
||||
|
||||
- [Source CE to EE update guides][source-ee] - Find your version, and follow the
|
||||
`-ce-to-ee.md` guide. The steps are very similar to a version upgrade: stop
|
||||
the server, get the code, update config files for the new functionality,
|
||||
install libraries and do migrations, update the init script, start the
|
||||
application and check its status.
|
||||
- [Omnibus CE to EE][omni-ce-ee] - Follow this guide to update your Omnibus
|
||||
GitLab Community Edition to the Enterprise Edition.
|
||||
|
||||
### Enterprise to Community Edition
|
||||
|
||||
If you need to downgrade your Enterprise Edition installation back to Community
|
||||
Edition, you can follow [this guide][ee-ce] to make the process as smooth as
|
||||
possible.
|
||||
|
||||
## Miscellaneous
|
||||
|
||||
- [MySQL to PostgreSQL](mysql_to_postgresql.md) guides you through migrating your database from MySQL to PostgreSQL.
|
||||
- [MySQL installation guide](https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/install/database_mysql.md) contains additional information about configuring GitLab to work with a MySQL database.
|
||||
- [MySQL to PostgreSQL](mysql_to_postgresql.md) guides you through migrating
|
||||
your database from MySQL to PostgreSQL.
|
||||
- [MySQL installation guide](../install/database_mysql.md) contains additional
|
||||
information about configuring GitLab to work with a MySQL database.
|
||||
- [Restoring from backup after a failed upgrade](restore_after_failure.md)
|
||||
|
||||
[omnidocker]: http://doc.gitlab.com/omnibus/docker/README.html
|
||||
[source-ee]: https://gitlab.com/gitlab-org/gitlab-ee/tree/master/doc/update
|
||||
[source-ce]: https://gitlab.com/gitlab-org/gitlab-ce/tree/master/doc/update
|
||||
[ee-ce]: ../downgrade_ee_to_ce/README.md
|
||||
[ce]: https://about.gitlab.com/features/#community
|
||||
[ee]: https://about.gitlab.com/features/#enterprise
|
||||
[omni-ce-ee]: http://doc.gitlab.com/omnibus/update/README.html#from-community-edition-to-enterprise-edition
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# Cherry-pick changes
|
||||
|
||||
_**Note:** This feature was [introduced][ce-3514] in GitLab 8.7._
|
||||
>**Note:**
|
||||
This feature was [introduced][ce-3514] in GitLab 8.7.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
# Import your project from GitHub to GitLab
|
||||
|
||||
_**Note:** In order to enable the GitHub import setting, you should first
|
||||
enable the [GitHub integration][gh-import] in your GitLab instance._
|
||||
>**Note:**
|
||||
In order to enable the GitHub import setting, you should first
|
||||
enable the [GitHub integration][gh-import] in your GitLab instance.
|
||||
|
||||
At its current state, GitHub importer can import:
|
||||
|
||||
@@ -10,10 +11,13 @@ At its current state, GitHub importer can import:
|
||||
- the issues (introduced in GitLab 7.7)
|
||||
- the pull requests (introduced in GitLab 8.4)
|
||||
- the wiki pages (introduced in GitLab 8.4)
|
||||
- the milestones (introduced in GitLab 8.7)
|
||||
- the labels (introduced in GitLab 8.7)
|
||||
|
||||
It is not yet possible to import your labels, milestones and cross-repository
|
||||
pull requests (those from forks). We are working on improving this in the near
|
||||
future.
|
||||
With GitLab 8.7+, references to pull requests and issues are preserved.
|
||||
|
||||
It is not yet possible to import your cross-repository pull requests (those from
|
||||
forks). We are working on improving this in the near future.
|
||||
|
||||
The importer page is visible when you [create a new project][new-project].
|
||||
Click on the **GitHub** link and you will be redirected to GitHub for
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
module Banzai
|
||||
module Filter
|
||||
# HTML Filter to add a `rel="nofollow"` attribute to external links
|
||||
#
|
||||
# HTML Filter to modify the attributes of external links
|
||||
class ExternalLinkFilter < HTML::Pipeline::Filter
|
||||
def call
|
||||
doc.search('a').each do |node|
|
||||
@@ -15,7 +14,7 @@ module Banzai
|
||||
# Skip internal links
|
||||
next if link.start_with?(internal_url)
|
||||
|
||||
node.set_attribute('rel', 'nofollow')
|
||||
node.set_attribute('rel', 'nofollow noreferrer')
|
||||
end
|
||||
|
||||
doc
|
||||
|
||||
@@ -300,14 +300,6 @@ describe Projects::MergeRequestsController do
|
||||
|
||||
expect(response.cookies['diff_view']).to eq('parallel')
|
||||
end
|
||||
|
||||
it 'assigns :view param based on cookie' do
|
||||
request.cookies['diff_view'] = 'parallel'
|
||||
|
||||
go
|
||||
|
||||
expect(controller.params[:view]).to eq 'parallel'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'GET commits' do
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe "Dashboard projects filters", feature: true, js: true do
|
||||
|
||||
context 'filtering personal projects' do
|
||||
before do
|
||||
user = create(:user)
|
||||
project = create(:project, name: "Victorialand", namespace: user.namespace)
|
||||
project.team << [user, :master]
|
||||
|
||||
login_as(user)
|
||||
visit dashboard_projects_path
|
||||
|
||||
open_filter_dropdown
|
||||
click_link "Owned by me"
|
||||
end
|
||||
|
||||
it 'filters by projects "Owned by me"' do
|
||||
sleep 1
|
||||
open_filter_dropdown
|
||||
page.within('ul.dropdown-menu.dropdown-menu-align-right') do
|
||||
expect(page).to have_css('.is-active', text: 'Owned by me')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def open_filter_dropdown
|
||||
find('button.dropdown-toggle.btn').click
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,79 @@
|
||||
require 'rails_helper'
|
||||
|
||||
feature 'Issue Sidebar', feature: true do
|
||||
let(:project) { create(:project) }
|
||||
let(:issue) { create(:issue, project: project) }
|
||||
let!(:user) { create(:user)}
|
||||
|
||||
before do
|
||||
create(:label, project: project, title: 'bug')
|
||||
login_as(user)
|
||||
end
|
||||
|
||||
context 'as a allowed user' do
|
||||
before do
|
||||
project.team << [user, :developer]
|
||||
visit_issue(project, issue)
|
||||
end
|
||||
|
||||
describe 'when clicking on edit labels', js: true do
|
||||
it 'dropdown has an option to create a new label' do
|
||||
find('.block.labels .edit-link').click
|
||||
|
||||
page.within('.block.labels') do
|
||||
expect(page).to have_content 'Create new'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'creating a new label', js: true do
|
||||
it 'option to crate a new label is present' do
|
||||
page.within('.block.labels') do
|
||||
find('.edit-link').click
|
||||
|
||||
expect(page).to have_content 'Create new'
|
||||
end
|
||||
end
|
||||
|
||||
it 'dropdown switches to "create label" section' do
|
||||
page.within('.block.labels') do
|
||||
find('.edit-link').click
|
||||
click_link 'Create new'
|
||||
|
||||
expect(page).to have_content 'Create new label'
|
||||
end
|
||||
end
|
||||
|
||||
it 'new label is added' do
|
||||
page.within('.block.labels') do
|
||||
find('.edit-link').click
|
||||
sleep 1
|
||||
click_link 'Create new'
|
||||
|
||||
fill_in 'new_label_name', with: 'wontfix'
|
||||
page.find(".suggest-colors a", match: :first).click
|
||||
click_button 'Create'
|
||||
|
||||
page.within('.dropdown-page-one') do
|
||||
expect(page).to have_content 'wontfix'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'as a guest' do
|
||||
before do
|
||||
project.team << [user, :guest]
|
||||
visit_issue(project, issue)
|
||||
end
|
||||
|
||||
it 'does not have a option to edit labels' do
|
||||
expect(page).not_to have_selector('.block.labels .edit-link')
|
||||
end
|
||||
end
|
||||
|
||||
def visit_issue(project, issue)
|
||||
visit namespace_project_issue_path(project.namespace, project, issue)
|
||||
end
|
||||
end
|
||||
@@ -178,6 +178,19 @@ describe 'Issues', feature: true do
|
||||
|
||||
expect(first_issue).to include('foo')
|
||||
end
|
||||
|
||||
context 'with a filter on labels' do
|
||||
let(:label) { create(:label, project: project) }
|
||||
before { create(:label_link, label: label, target: foo) }
|
||||
|
||||
it 'sorts by least recently due date by excluding nil due dates' do
|
||||
bar.update(due_date: nil)
|
||||
|
||||
visit namespace_project_issues_path(project.namespace, project, label_names: [label.name], sort: sort_value_due_date_later)
|
||||
|
||||
expect(first_issue).to include('foo')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'filtering by due date' do
|
||||
@@ -304,6 +317,27 @@ describe 'Issues', feature: true do
|
||||
|
||||
expect(issue.reload.assignee).to be_nil
|
||||
end
|
||||
|
||||
it 'allows user to select an assignee', js: true do
|
||||
issue2 = create(:issue, project: project, author: @user)
|
||||
visit namespace_project_issue_path(project.namespace, project, issue2)
|
||||
|
||||
page.within('.assignee') do
|
||||
expect(page).to have_content "No assignee"
|
||||
end
|
||||
|
||||
page.within '.assignee' do
|
||||
click_link 'Edit'
|
||||
end
|
||||
|
||||
page.within '.dropdown-menu-user' do
|
||||
click_link @user.name
|
||||
end
|
||||
|
||||
page.within('.assignee') do
|
||||
expect(page).to have_content @user.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'by unauthorized user' do
|
||||
|
||||
@@ -165,7 +165,12 @@ describe 'GitLab Markdown', feature: true do
|
||||
describe 'ExternalLinkFilter' do
|
||||
it 'adds nofollow to external link' do
|
||||
link = doc.at_css('a:contains("Google")')
|
||||
expect(link.attr('rel')).to match 'nofollow'
|
||||
expect(link.attr('rel')).to include('nofollow')
|
||||
end
|
||||
|
||||
it 'adds noreferrer to external link' do
|
||||
link = doc.at_css('a:contains("Google")')
|
||||
expect(link.attr('rel')).to include('noreferrer')
|
||||
end
|
||||
|
||||
it 'ignores internal link' do
|
||||
|
||||
@@ -13,8 +13,8 @@ feature 'Signup', feature: true do
|
||||
fill_in 'user_password_sign_up', with: user.password
|
||||
click_button "Sign up"
|
||||
|
||||
expect(current_path).to eq user_session_path
|
||||
expect(page).to have_content("A message with a confirmation link has been sent to your email address.")
|
||||
expect(current_path).to eq users_almost_there_path
|
||||
expect(page).to have_content("Please check your email to confirm your account")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -11,6 +11,26 @@ describe DiffHelper do
|
||||
let(:diff_refs) { [commit.parent, commit] }
|
||||
let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs) }
|
||||
|
||||
describe 'diff_view' do
|
||||
it 'returns a valid value when cookie is set' do
|
||||
helper.request.cookies[:diff_view] = 'parallel'
|
||||
|
||||
expect(helper.diff_view).to eq 'parallel'
|
||||
end
|
||||
|
||||
it 'returns a default value when cookie is invalid' do
|
||||
helper.request.cookies[:diff_view] = 'invalid'
|
||||
|
||||
expect(helper.diff_view).to eq 'inline'
|
||||
end
|
||||
|
||||
it 'returns a default value when cookie is nil' do
|
||||
expect(helper.request.cookies).to be_empty
|
||||
|
||||
expect(helper.diff_view).to eq 'inline'
|
||||
end
|
||||
end
|
||||
|
||||
describe 'diff_hard_limit_enabled?' do
|
||||
it 'should return true if param is provided' do
|
||||
allow(controller).to receive(:params) { { force_show_diff: true } }
|
||||
|
||||
@@ -24,6 +24,14 @@ describe Banzai::Filter::ExternalLinkFilter, lib: true do
|
||||
doc = filter(act)
|
||||
|
||||
expect(doc.at_css('a')).to have_attribute('rel')
|
||||
expect(doc.at_css('a')['rel']).to eq 'nofollow'
|
||||
expect(doc.at_css('a')['rel']).to include 'nofollow'
|
||||
end
|
||||
|
||||
it 'adds rel="noreferrer" to external links' do
|
||||
act = %q(<a href="https://google.com/">Google</a>)
|
||||
doc = filter(act)
|
||||
|
||||
expect(doc.at_css('a')).to have_attribute('rel')
|
||||
expect(doc.at_css('a')['rel']).to include 'noreferrer'
|
||||
end
|
||||
end
|
||||
|
||||
@@ -100,7 +100,7 @@ describe Issues::BulkUpdateService, services: true do
|
||||
describe :update_milestone do
|
||||
|
||||
before do
|
||||
@milestone = create :milestone
|
||||
@milestone = create(:milestone, project: @project)
|
||||
@params = {
|
||||
issues_ids: [issue.id],
|
||||
milestone_id: @milestone.id
|
||||
|
||||
@@ -3,40 +3,75 @@ require 'spec_helper'
|
||||
describe Issues::CreateService, services: true do
|
||||
let(:project) { create(:empty_project) }
|
||||
let(:user) { create(:user) }
|
||||
let(:assignee) { create(:user) }
|
||||
|
||||
describe :execute do
|
||||
context 'valid params' do
|
||||
describe '#execute' do
|
||||
let(:issue) { described_class.new(project, user, opts).execute }
|
||||
|
||||
context 'when params are valid' do
|
||||
let(:assignee) { create(:user) }
|
||||
let(:milestone) { create(:milestone, project: project) }
|
||||
let(:labels) { create_pair(:label, project: project) }
|
||||
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
project.team << [assignee, :master]
|
||||
|
||||
opts = {
|
||||
title: 'Awesome issue',
|
||||
description: 'please fix',
|
||||
assignee: assignee
|
||||
}
|
||||
|
||||
@issue = Issues::CreateService.new(project, user, opts).execute
|
||||
end
|
||||
|
||||
it { expect(@issue).to be_valid }
|
||||
it { expect(@issue.title).to eq('Awesome issue') }
|
||||
it { expect(@issue.assignee).to eq assignee }
|
||||
let(:opts) do
|
||||
{ title: 'Awesome issue',
|
||||
description: 'please fix',
|
||||
assignee: assignee,
|
||||
label_ids: labels.map(&:id),
|
||||
milestone_id: milestone.id }
|
||||
end
|
||||
|
||||
it { expect(issue).to be_valid }
|
||||
it { expect(issue.title).to eq('Awesome issue') }
|
||||
it { expect(issue.assignee).to eq assignee }
|
||||
it { expect(issue.labels).to match_array labels }
|
||||
it { expect(issue.milestone).to eq milestone }
|
||||
|
||||
it 'creates a pending todo for new assignee' do
|
||||
attributes = {
|
||||
project: project,
|
||||
author: user,
|
||||
user: assignee,
|
||||
target_id: @issue.id,
|
||||
target_type: @issue.class.name,
|
||||
target_id: issue.id,
|
||||
target_type: issue.class.name,
|
||||
action: Todo::ASSIGNED,
|
||||
state: :pending
|
||||
}
|
||||
|
||||
expect(Todo.where(attributes).count).to eq 1
|
||||
end
|
||||
|
||||
context 'when label belongs to different project' do
|
||||
let(:label) { create(:label) }
|
||||
|
||||
let(:opts) do
|
||||
{ title: 'Title',
|
||||
description: 'Description',
|
||||
label_ids: [label.id] }
|
||||
end
|
||||
|
||||
it 'does not assign label'do
|
||||
expect(issue.labels).to_not include label
|
||||
end
|
||||
end
|
||||
|
||||
context 'when milestone belongs to different project' do
|
||||
let(:milestone) { create(:milestone) }
|
||||
|
||||
let(:opts) do
|
||||
{ title: 'Title',
|
||||
description: 'Description',
|
||||
milestone_id: milestone.id }
|
||||
end
|
||||
|
||||
it 'does not assign milestone' do
|
||||
expect(issue.milestone).to_not eq milestone
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -4,10 +4,15 @@ describe Issues::UpdateService, services: true do
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
let(:user3) { create(:user) }
|
||||
let(:issue) { create(:issue, title: 'Old title', assignee_id: user3.id) }
|
||||
let(:label) { create(:label) }
|
||||
let(:project) { create(:empty_project) }
|
||||
let(:label) { create(:label, project: project) }
|
||||
let(:label2) { create(:label) }
|
||||
let(:project) { issue.project }
|
||||
|
||||
let(:issue) do
|
||||
create(:issue, title: 'Old title',
|
||||
assignee_id: user3.id,
|
||||
project: project)
|
||||
end
|
||||
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
|
||||
@@ -1,14 +1,19 @@
|
||||
require 'spec_helper'
|
||||
|
||||
describe MergeRequests::UpdateService, services: true do
|
||||
let(:project) { create(:project) }
|
||||
let(:user) { create(:user) }
|
||||
let(:user2) { create(:user) }
|
||||
let(:user3) { create(:user) }
|
||||
let(:merge_request) { create(:merge_request, :simple, title: 'Old title', assignee_id: user3.id) }
|
||||
let(:project) { merge_request.project }
|
||||
let(:label) { create(:label) }
|
||||
let(:label) { create(:label, project: project) }
|
||||
let(:label2) { create(:label) }
|
||||
|
||||
let(:merge_request) do
|
||||
create(:merge_request, :simple, title: 'Old title',
|
||||
assignee_id: user3.id,
|
||||
source_project: project)
|
||||
end
|
||||
|
||||
before do
|
||||
project.team << [user, :master]
|
||||
project.team << [user2, :developer]
|
||||
|
||||
Reference in New Issue
Block a user