Commit cb4a5880 authored by Rémy Coutable's avatar Rémy Coutable

Merge remote-tracking branch 'origin/master' into 8-10-stable

parents 4d0a6f17 9ca633eb
# Prefer single quotes
StringLiterals:
EnforcedStyle: single_quotes
Enabled: true
stage:
before:
- cp config/gitlab.teatro.yml config/gitlab.yml
- mkdir /apps/gitlab-satellites
- mkdir /apps/repositories
database:
- RAILS_ENV=development force=yes bundle exec rake db:create gitlab:setup
\ No newline at end of file
Please view this file on the master branch, on stable branches it's out of date. Please view this file on the master branch, on stable branches it's out of date.
v 8.10.0 (unreleased) v 8.10.0 (unreleased)
- Expose {should,force}_remove_source_branch (Ben Boeckel)
- Fix commit builds API, return all builds for all pipelines for given commit. !4849 - Fix commit builds API, return all builds for all pipelines for given commit. !4849
- Replace Haml with Hamlit to make view rendering faster. !3666 - Replace Haml with Hamlit to make view rendering faster. !3666
- Expire the branch cache after `git gc` runs
- Refactor repository paths handling to allow multiple git mount points - Refactor repository paths handling to allow multiple git mount points
- Optimize system note visibility checking by memoizing the visible reference count !5070
- Add Application Setting to configure default Repository Path for new projects - Add Application Setting to configure default Repository Path for new projects
- Delete award emoji when deleting a user
- Remove pinTo from Flash and make inline flash messages look nicer !4854 (winniehell)
- Wrap code blocks on Activies and Todos page. !4783 (winniehell) - Wrap code blocks on Activies and Todos page. !4783 (winniehell)
- Align flash messages with left side of page content !4959 (winniehell) - Align flash messages with left side of page content !4959 (winniehell)
- Display tooltip for "Copy to Clipboard" button !5164 (winniehell)
- Display last commit of deleted branch in push events !4699 (winniehell) - Display last commit of deleted branch in push events !4699 (winniehell)
- Escape file extension when parsing search results !5141 (winniehell) - Escape file extension when parsing search results !5141 (winniehell)
- Apply the trusted_proxies config to the rack request object for use with rack_attack - Apply the trusted_proxies config to the rack request object for use with rack_attack
- Add Sidekiq queue duration to transaction metrics. - Add Sidekiq queue duration to transaction metrics.
- Add a new column `artifacts_size` to table `ci_builds` !4964 - Add a new column `artifacts_size` to table `ci_builds` !4964
- Let Workhorse serve format-patch diffs - Let Workhorse serve format-patch diffs
- Added day name to contribution calendar tooltips
- Make images fit to the size of the viewport !4810 - Make images fit to the size of the viewport !4810
- Fix check for New Branch button on Issue page !4630 (winniehell) - Fix check for New Branch button on Issue page !4630 (winniehell)
- Fix MR-auto-close text added to description. !4836 - Fix MR-auto-close text added to description. !4836
...@@ -26,26 +33,36 @@ v 8.10.0 (unreleased) ...@@ -26,26 +33,36 @@ v 8.10.0 (unreleased)
- Exclude email check from the standard health check - Exclude email check from the standard health check
- Updated layout for Projects, Groups, Users on Admin area !4424 - Updated layout for Projects, Groups, Users on Admin area !4424
- Fix changing issue state columns in milestone view - Fix changing issue state columns in milestone view
- Update health_check gem to version 2.1.0
- Add notification settings dropdown for groups - Add notification settings dropdown for groups
- Render inline diffs for multiple changed lines following eachother
- Wildcards for protected branches. !4665 - Wildcards for protected branches. !4665
- Allow importing from Github using Personal Access Tokens. (Eric K Idema) - Allow importing from Github using Personal Access Tokens. (Eric K Idema)
- API: Todos !3188 (Robert Schilling) - API: Todos !3188 (Robert Schilling)
- API: Expose shared groups for projects and shared projects for groups !5050 (Robert Schilling) - API: Expose shared groups for projects and shared projects for groups !5050 (Robert Schilling)
- Add "Enabled Git access protocols" to Application Settings - Add "Enabled Git access protocols" to Application Settings
- Diffs will create button/diff form on demand no on server side
- Reduce size of HTML used by diff comment forms
- Protected branches have a "Developers can Merge" setting. !4892 (original implementation by Mathias Vestergaard)
- Fix user creation with stronger minimum password requirements !4054 (nathan-pmt) - Fix user creation with stronger minimum password requirements !4054 (nathan-pmt)
- Only show New Snippet button to users that can create snippets. - Only show New Snippet button to users that can create snippets.
- PipelinesFinder uses git cache data - PipelinesFinder uses git cache data
- Throttle the update of `project.pushes_since_gc` to 1 minute. - Throttle the update of `project.pushes_since_gc` to 1 minute.
- Allow expanding and collapsing files in diff view (!4990)
- Collapse large diffs by default (!4990)
- Check for conflicts with existing Project's wiki path when creating a new project. - Check for conflicts with existing Project's wiki path when creating a new project.
- Show last push widget in upstream after push to fork - Show last push widget in upstream after push to fork
- Cache todos pending/done dashboard query counts.
- Don't instantiate a git tree on Projects show default view - Don't instantiate a git tree on Projects show default view
- Bump Rinku to 2.0.0 - Bump Rinku to 2.0.0
- Remove unused front-end variable -> default_issues_tracker - Remove unused front-end variable -> default_issues_tracker
- ObjectRenderer retrieve renderer content using Rails.cache.read_multi
- Better caching of git calls on ProjectsController#show. - Better caching of git calls on ProjectsController#show.
- Avoid to retrieve MR closes_issues as much as possible. - Avoid to retrieve MR closes_issues as much as possible.
- Add API endpoint for a group issues !4520 (mahcsig) - Add API endpoint for a group issues !4520 (mahcsig)
- Add Bugzilla integration !4930 (iamtjg) - Add Bugzilla integration !4930 (iamtjg)
- Instrument Rinku usage - Instrument Rinku usage
- Be explicit to define merge request discussion variables
- Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab - Metrics for Rouge::Plugins::Redcarpet and Rouge::Formatters::HTMLGitlab
- RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info. - RailsCache metris now includes fetch_hit/fetch_miss and read_hit/read_miss info.
- Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w) - Allow [ci skip] to be in any case and allow [skip ci]. !4785 (simon_w)
...@@ -60,9 +77,27 @@ v 8.10.0 (unreleased) ...@@ -60,9 +77,27 @@ v 8.10.0 (unreleased)
- Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests - Fix importer for GitHub Pull Requests when a branch was reused across Pull Requests
- Add date when user joined the team on the member page - Add date when user joined the team on the member page
- Fix 404 redirect after validation fails importing a GitLab project - Fix 404 redirect after validation fails importing a GitLab project
- Fix 404 redirect after validation fails importing a GitLab project
- Added setting to set new users by default as external !4545 (Dravere) - Added setting to set new users by default as external !4545 (Dravere)
- Add min value for project limit field on user's form !3622 (jastkand) - Add min value for project limit field on user's form !3622 (jastkand)
- Reset project pushes_since_gc when we enqueue the git gc call
- Add reminder to not paste private SSH keys !4399 (Ingo Blechschmidt) - Add reminder to not paste private SSH keys !4399 (Ingo Blechschmidt)
- Remove duplicate `description` field in `MergeRequest` entities (Ben Boeckel)
- Style of import project buttons were fixed in the new project page. !5183 (rdemirbay)
- Fix GitHub client requests when rate limit is disabled
- Optimistic locking for Issues and Merge Requests (Title and description overriding prevention)
- Redesign Builds and Pipelines pages
- Change status color and icon for running builds
v 8.9.6
- Fix importing of events under notes for GitLab projects. !5154
- Fix log statements in import/export. !5129
- Fix commit avatar alignment in compare view. !5128
- Fix broken migration in MySQL. !5005
- Overwrite Host and X-Forwarded-Host headers in NGINX !5213
v 8.9.6 (unreleased)
- Fix importing of events under notes for GitLab projects
v 8.9.5 v 8.9.5
- Add more debug info to import/export and memory killer. !5108 - Add more debug info to import/export and memory killer. !5108
...@@ -2611,13 +2646,13 @@ v 6.5.0 ...@@ -2611,13 +2646,13 @@ v 6.5.0
- Files API supports base64 encoded content (sponsored by O'Reilly Media) - Files API supports base64 encoded content (sponsored by O'Reilly Media)
- Added support for Go's repository retrieval (Bruno Albuquerque) - Added support for Go's repository retrieval (Bruno Albuquerque)
v6.4.3 v 6.4.3
- Don't use unicorn worker killer if PhusionPassenger is defined - Don't use unicorn worker killer if PhusionPassenger is defined
v6.4.2 v 6.4.2
- Fixed wrong behaviour of script/upgrade.rb - Fixed wrong behaviour of script/upgrade.rb
v6.4.1 v 6.4.1
- Fixed bug with repository rename - Fixed bug with repository rename
- Fixed bug with project transfer - Fixed bug with project transfer
......
...@@ -344,7 +344,7 @@ gem 'oauth2', '~> 1.2.0' ...@@ -344,7 +344,7 @@ gem 'oauth2', '~> 1.2.0'
gem 'paranoia', '~> 2.0' gem 'paranoia', '~> 2.0'
# Health check # Health check
gem 'health_check', '~> 1.5.1' gem 'health_check', '~> 2.1.0'
# System information # System information
gem 'vmstat', '~> 2.1.0' gem 'vmstat', '~> 2.1.0'
......
...@@ -322,8 +322,8 @@ GEM ...@@ -322,8 +322,8 @@ GEM
thor thor
tilt tilt
hashie (3.4.3) hashie (3.4.3)
health_check (1.5.1) health_check (2.1.0)
rails (>= 2.3.0) rails (>= 4.0)
hipchat (1.5.2) hipchat (1.5.2)
httparty httparty
mimemagic mimemagic
...@@ -870,7 +870,7 @@ DEPENDENCIES ...@@ -870,7 +870,7 @@ DEPENDENCIES
grape (~> 0.13.0) grape (~> 0.13.0)
grape-entity (~> 0.4.2) grape-entity (~> 0.4.2)
hamlit (~> 2.5) hamlit (~> 2.5)
health_check (~> 1.5.1) health_check (~> 2.1.0)
hipchat (~> 1.5.0) hipchat (~> 1.5.0)
html-pipeline (~> 1.11.0) html-pipeline (~> 1.11.0)
httparty (~> 0.13.3) httparty (~> 0.13.3)
......
...@@ -47,7 +47,6 @@ ...@@ -47,7 +47,6 @@
#= require date.format #= require date.format
#= require_directory ./behaviors #= require_directory ./behaviors
#= require_directory ./blob #= require_directory ./blob
#= require_directory ./ci
#= require_directory ./commit #= require_directory ./commit
#= require_directory ./extensions #= require_directory ./extensions
#= require_directory ./lib/utils #= require_directory ./lib/utils
......
class @CiBuild class @Build
@interval: null @interval: null
@state: null @state: null
constructor: (@page_url, @build_url, @build_status, @state) -> constructor: (@page_url, @build_url, @build_status, @state) ->
clearInterval(CiBuild.interval) clearInterval(Build.interval)
# Init breakpoint checker # Init breakpoint checker
@bp = Breakpoints.get() @bp = Breakpoints.get()
...@@ -40,7 +40,7 @@ class @CiBuild ...@@ -40,7 +40,7 @@ class @CiBuild
# Check for new build output if user still watching build page # Check for new build output if user still watching build page
# Only valid for runnig build when output changes during time # Only valid for runnig build when output changes during time
# #
CiBuild.interval = setInterval => Build.interval = setInterval =>
if window.location.href.split("#").first() is @page_url if window.location.href.split("#").first() is @page_url
@getBuildTrace() @getBuildTrace()
, 4000 , 4000
......
#= require pager
#= require jquery_nested_form
#= require_tree .
$(document).on 'click', '.assign-all-runner', ->
$(this).replaceWith('<i class="fa fa-refresh fa-spin"></i> Assign in progress..')
window.unbindEvents = ->
$(document).unbind('scroll')
$(document).off('scroll')
document.addEventListener("page:fetch", unbindEvents)
$(document).on 'click', '.badge-codes-toggle', ->
$('.badge-codes-block').toggleClass("hide")
return false
class @Diff class @Diff
UNFOLD_COUNT = 20 UNFOLD_COUNT = 20
constructor: -> constructor: ->
$('.files .diff-file').singleFileDiff()
@filesCommentButton = $('.files .diff-file').filesCommentButton()
$(document).off('click', '.js-unfold') $(document).off('click', '.js-unfold')
$(document).on('click', '.js-unfold', (event) => $(document).on('click', '.js-unfold', (event) =>
target = $(event.target) target = $(event.target)
...@@ -36,7 +39,7 @@ class @Diff ...@@ -36,7 +39,7 @@ class @Diff
# see https://gitlab.com/gitlab-org/gitlab-ce/issues/707 # see https://gitlab.com/gitlab-org/gitlab-ce/issues/707
indent: 1 indent: 1
$.get(link, params, (response) => $.get(link, params, (response) ->
target.parent().replaceWith(response) target.parent().replaceWith(response)
) )
) )
......
class @FilesCommentButton
COMMENT_BUTTON_CLASS = '.add-diff-note'
COMMENT_BUTTON_TEMPLATE = _.template '<button name="button" type="submit" class="btn <%- COMMENT_BUTTON_CLASS %> js-add-diff-note-button" title="Add a comment to this line"><i class="fa fa-comment-o"></i></button>'
LINE_HOLDER_CLASS = '.line_holder'
LINE_NUMBER_CLASS = 'diff-line-num'
LINE_CONTENT_CLASS = 'line_content'
UNFOLDABLE_LINE_CLASS = 'js-unfold'
EMPTY_CELL_CLASS = 'empty-cell'
OLD_LINE_CLASS = 'old_line'
NEW_CLASS = 'new'
LINE_COLUMN_CLASSES = ".#{LINE_NUMBER_CLASS}, .line_content"
TEXT_FILE_SELECTOR = '.text-file'
DEBOUNCE_TIMEOUT_DURATION = 100
constructor: (@filesContainerElement) ->
@VIEW_TYPE = $('input#view[type=hidden]').val()
debounce = _.debounce @render, DEBOUNCE_TIMEOUT_DURATION
$(document)
.on 'mouseover', LINE_COLUMN_CLASSES, debounce
.on 'mouseleave', LINE_COLUMN_CLASSES, @destroy
render: (e) =>
$currentTarget = $(e.currentTarget)
buttonParentElement = @getButtonParent $currentTarget
return unless @shouldRender e, buttonParentElement
textFileElement = @getTextFileElement $currentTarget
lineContentElement = @getLineContent $currentTarget
buttonParentElement.append @buildButton
noteableType: textFileElement.attr 'data-noteable-type'
noteableID: textFileElement.attr 'data-noteable-id'
commitID: textFileElement.attr 'data-commit-id'
noteType: lineContentElement.attr 'data-note-type'
position: lineContentElement.attr 'data-position'
lineType: lineContentElement.attr 'data-line-type'
discussionID: lineContentElement.attr 'data-discussion-id'
lineCode: lineContentElement.attr 'data-line-code'
return
destroy: (e) =>
return if @isMovingToSameType e
$(COMMENT_BUTTON_CLASS, @getButtonParent $(e.currentTarget)).remove()
return
buildButton: (buttonAttributes) ->
initializedButtonTemplate = COMMENT_BUTTON_TEMPLATE
COMMENT_BUTTON_CLASS: COMMENT_BUTTON_CLASS.substr 1
$(initializedButtonTemplate).attr
'data-noteable-type': buttonAttributes.noteableType
'data-noteable-id': buttonAttributes.noteableID
'data-commit-id': buttonAttributes.commitID
'data-note-type': buttonAttributes.noteType
'data-line-code': buttonAttributes.lineCode
'data-position': buttonAttributes.position
'data-discussion-id': buttonAttributes.discussionID
'data-line-type': buttonAttributes.lineType
getTextFileElement: (hoveredElement) ->
$(hoveredElement.closest TEXT_FILE_SELECTOR)
getLineContent: (hoveredElement) ->
return hoveredElement if hoveredElement.hasClass LINE_CONTENT_CLASS
$(".#{LINE_CONTENT_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
getButtonParent: (hoveredElement) ->
if @VIEW_TYPE is 'inline'
return hoveredElement if hoveredElement.hasClass OLD_LINE_CLASS
$(".#{OLD_LINE_CLASS}", hoveredElement.parent())
else
return hoveredElement if hoveredElement.hasClass LINE_NUMBER_CLASS
$(".#{LINE_NUMBER_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent())
diffTypeClass: (hoveredElement) ->
if hoveredElement.hasClass(NEW_CLASS) then '.new' else '.old'
isMovingToSameType: (e) ->
newButtonParent = @getButtonParent $(e.toElement)
return false unless newButtonParent
newButtonParent.is @getButtonParent $(e.currentTarget)
shouldRender: (e, buttonParentElement) ->
(not buttonParentElement.hasClass(EMPTY_CELL_CLASS) and \
not buttonParentElement.hasClass(UNFOLDABLE_LINE_CLASS) and \
$(COMMENT_BUTTON_CLASS, buttonParentElement).length is 0)
$.fn.filesCommentButton = ->
return unless this and @parent().data('can-create-note')?
@each ->
unless $.data this, 'filesCommentButton'
$.data this, 'filesCommentButton', new FilesCommentButton $(this)
class @Flash class @Flash
constructor: (message, type = 'alert')-> hideFlash = -> $(@).fadeOut()
@flash = $(".flash-container")
@flash.html("")
innerDiv = $('<div/>', constructor: (message, type = 'alert', parent = null)->
if parent
@flashContainer = parent.find('.flash-container')
else
@flashContainer = $('.flash-container-page')
@flashContainer.html('')
flash = $('<div/>',
class: "flash-#{type}" class: "flash-#{type}"
) )
innerDiv.appendTo(".flash-container") flash.on 'click', hideFlash
textDiv = $("<div/>", textDiv = $('<div/>',
class: "flash-text", class: 'flash-text',
text: message text: message
) )
textDiv.appendTo(innerDiv) textDiv.appendTo(flash)
if @flash.parent().hasClass('content-wrapper') if @flashContainer.parent().hasClass('content-wrapper')
textDiv.addClass('container-fluid container-limited') textDiv.addClass('container-fluid container-limited')
@flash.click -> $(@).fadeOut() flash.appendTo(@flashContainer)
@flash.show() @flashContainer.show()
pinTo: (selector) ->
@flash.detach().appendTo(selector)
...@@ -2,10 +2,14 @@ ...@@ -2,10 +2,14 @@
w.gl ?= {} w.gl ?= {}
w.gl.utils ?= {} w.gl.utils ?= {}
w.gl.utils.days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday']
w.gl.utils.formatDate = (datetime) -> w.gl.utils.formatDate = (datetime) ->
dateFormat(datetime, 'mmm d, yyyy h:MMtt Z') dateFormat(datetime, 'mmm d, yyyy h:MMtt Z')
w.gl.utils.getDayName = (date) ->
this.days[date.getDay()]
w.gl.utils.localTimeAgo = ($timeagoEls, setTimeago = true) -> w.gl.utils.localTimeAgo = ($timeagoEls, setTimeago = true) ->
$timeagoEls.each( -> $timeagoEls.each( ->
$el = $(@) $el = $(@)
......
...@@ -153,17 +153,18 @@ class @MergeRequestTabs ...@@ -153,17 +153,18 @@ class @MergeRequestTabs
loadDiff: (source) -> loadDiff: (source) ->
return if @diffsLoaded return if @diffsLoaded
@_get @_get
url: "#{source}.json" + @_location.search url: "#{source}.json" + @_location.search
success: (data) => success: (data) =>
$('#diffs').html data.html $('#diffs').html data.html
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs')) gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'))
$('#diffs .js-syntax-highlight').syntaxHighlight() $('#diffs .js-syntax-highlight').syntaxHighlight()
$('#diffs .diff-file').singleFileDiff()
@expandViewContainer() if @diffViewType() is 'parallel' @expandViewContainer() if @diffViewType() is 'parallel'
@diffsLoaded = true @diffsLoaded = true
@scrollToElement("#diffs") @scrollToElement("#diffs")
@highlighSelectedLine() @highlighSelectedLine()
@filesCommentButton = $('.files .diff-file').filesCommentButton()
$(document) $(document)
.off 'click', '.diff-line-num a' .off 'click', '.diff-line-num a'
......
...@@ -194,8 +194,7 @@ class @Notes ...@@ -194,8 +194,7 @@ class @Notes
renderNote: (note) -> renderNote: (note) ->
unless note.valid unless note.valid
if note.award if note.award
flash = new Flash('You have already awarded this emoji!', 'alert') new Flash('You have already awarded this emoji!', 'alert')
flash.pinTo('.header-content')
return return
if note.award if note.award
...@@ -325,6 +324,8 @@ class @Notes ...@@ -325,6 +324,8 @@ class @Notes
form.find("#note_position").remove() form.find("#note_position").remove()
form.find("#note_type").remove() form.find("#note_type").remove()
@parentTimeline = form.parents('.timeline')
### ###
General note form setup. General note form setup.
...@@ -357,8 +358,7 @@ class @Notes ...@@ -357,8 +358,7 @@ class @Notes
@renderNote(note) @renderNote(note)
addNoteError: (xhr, note, status) => addNoteError: (xhr, note, status) =>
flash = new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert') new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', @parentTimeline)
flash.pinTo('.md-area')
### ###
Called in response to the new note form being submitted Called in response to the new note form being submitted
......
$ -> $ ->
$(".protected-branches-list :checkbox").change (e) -> $(".protected-branches-list :checkbox").change (e) ->
name = $(this).attr("name") name = $(this).attr("name")
if name == "developers_can_push" if name == "developers_can_push" || name == "developers_can_merge"
id = $(this).val() id = $(this).val()
checked = $(this).is(":checked") can_push = $(this).is(":checked")
url = $(this).data("url") url = $(this).data("url")
$.ajax $.ajax
type: "PUT" type: "PATCH"
url: url url: url
dataType: "json" dataType: "json"
data: data:
id: id id: id
protected_branch: protected_branch:
developers_can_push: checked "#{name}": can_push
success: -> success: ->
row = $(e.target) row = $(e.target)
......
class @SingleFileDiff
WRAPPER = '<div class="diff-content diff-wrap-lines"></div>'
LOADING_HTML = '<i class="fa fa-spinner fa-spin"></i>'
ERROR_HTML = '<div class="nothing-here-block"><i class="fa fa-warning"></i> Could not load diff</div>'
COLLAPSED_HTML = '<div class="nothing-here-block diff-collapsed">This diff is collapsed. Click to expand it.</div>'
constructor: (@file) ->
@content = $('.diff-content', @file)
@diffForPath = @content.find('[data-diff-for-path]').data 'diff-for-path'
@isOpen = !@diffForPath
if @diffForPath
@collapsedContent = @content
@loadingContent = $(WRAPPER).addClass('loading').html(LOADING_HTML).hide()
@content = null
@collapsedContent.after(@loadingContent)
else
@collapsedContent = $(WRAPPER).html(COLLAPSED_HTML).hide()
@content.after(@collapsedContent)
@collapsedContent.on 'click', @toggleDiff
$('.file-title > a', @file).on 'click', @toggleDiff
toggleDiff: (e) =>
@isOpen = !@isOpen
if not @isOpen and not @hasError
@content.hide()
@collapsedContent.show()
else if @content
@collapsedContent.hide()
@content.show()
else
@getContentHTML()
getContentHTML: ->
@collapsedContent.hide()
@loadingContent.show()
$.get @diffForPath, (data) =>
@loadingContent.hide()
if data.html
@content = $(data.html)
@content.syntaxHighlight()
else
@hasError = true
@content = $(ERROR_HTML)
@collapsedContent.after(@content)
return
$.fn.singleFileDiff = ->
return @each ->
if not $.data this, 'singleFileDiff'
$.data this, 'singleFileDiff', new SingleFileDiff this
...@@ -87,14 +87,15 @@ class @Calendar ...@@ -87,14 +87,15 @@ class @Calendar
.attr 'width', @daySize .attr 'width', @daySize
.attr 'height', @daySize .attr 'height', @daySize
.attr 'title', (stamp) => .attr 'title', (stamp) =>
date = new Date(stamp.date)
contribText = 'No contributions' contribText = 'No contributions'
if stamp.count > 0 if stamp.count > 0
contribText = "#{stamp.count} contribution#{if stamp.count > 1 then 's' else ''}" contribText = "#{stamp.count} contribution#{if stamp.count > 1 then 's' else ''}"
date = dateFormat(stamp.date, 'mmm d, yyyy') dateText = dateFormat(date, 'mmm d, yyyy')
"#{contribText}<br />#{date}" "#{contribText}<br />#{gl.utils.getDayName(date)} #{dateText}"
.attr 'class', 'user-contrib-cell js-tooltip' .attr 'class', 'user-contrib-cell js-tooltip'
.attr 'fill', (stamp) => .attr 'fill', (stamp) =>
if stamp.count isnt 0 if stamp.count isnt 0
......
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
} }
&.s16 { width: 16px; height: 16px; margin-right: 6px; } &.s16 { width: 16px; height: 16px; margin-right: 6px; }
&.s20 { width: 20px; height: 20px; margin-right: 7px; }
&.s24 { width: 24px; height: 24px; margin-right: 8px; } &.s24 { width: 24px; height: 24px; margin-right: 8px; }
&.s26 { width: 26px; height: 26px; margin-right: 8px; } &.s26 { width: 26px; height: 26px; margin-right: 8px; }
&.s32 { width: 32px; height: 32px; margin-right: 10px; } &.s32 { width: 32px; height: 32px; margin-right: 10px; }
......
...@@ -16,6 +16,9 @@ ...@@ -16,6 +16,9 @@
font-weight: normal; font-weight: normal;
font-size: 16px; font-size: 16px;
line-height: 36px; line-height: 36px;
&.diff-collapsed {
cursor: pointer;
}
} }
.row-content-block { .row-content-block {
......
.flash-container { .flash-container {
cursor: pointer; cursor: pointer;
margin: 0; margin: 0;
margin-bottom: $gl-padding;
font-size: 14px; font-size: 14px;
width: 100%;
z-index: 100; z-index: 100;
.flash-notice { .flash-notice {
...@@ -18,9 +18,27 @@ ...@@ -18,9 +18,27 @@
} }
.flash-notice, .flash-alert { .flash-notice, .flash-alert {
.container-fluid.flash-text { border-radius: $border-radius-default;
.container-fluid.container-limited.flash-text {
background: transparent; background: transparent;
} }
} }
&.flash-container-page {
margin-bottom: 0;
.flash-notice, .flash-alert {
border-radius: 0;
}
}
}
@media (max-width: $screen-md-min) {
ul.notes {
.flash-container.timeline-content {
margin-left: 0;
}
}
} }
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
.toggle-nav-collapse, .toggle-nav-collapse,
.pin-nav-btn { .pin-nav-btn {
color: $color-light; color: $color-light;
background: $color;
&:hover { &:hover {
color: $white-light; color: $white-light;
......
...@@ -3,6 +3,12 @@ ...@@ -3,6 +3,12 @@
padding-bottom: 25px; padding-bottom: 25px;
transition: padding $sidebar-transition-duration; transition: padding $sidebar-transition-duration;
&.page-sidebar-pinned {
.sidebar-wrapper {
@include box-shadow(none);
}
}
.sidebar-wrapper { .sidebar-wrapper {
position: fixed; position: fixed;
top: 0; top: 0;
...@@ -11,6 +17,7 @@ ...@@ -11,6 +17,7 @@
height: 100%; height: 100%;
overflow: hidden; overflow: hidden;
transition: width $sidebar-transition-duration; transition: width $sidebar-transition-duration;
@include box-shadow(2px 0 16px 0 $black-transparent);
} }
} }
...@@ -69,7 +76,7 @@ ...@@ -69,7 +76,7 @@
} }
a { a {
padding: 7px 15px 7px 12px; padding: 7px 16px;
font-size: $gl-font-size; font-size: $gl-font-size;
line-height: 24px; line-height: 24px;
display: block; display: block;
...@@ -101,7 +108,7 @@ ...@@ -101,7 +108,7 @@
} }
} }
.toggle-nav-collapse { .sidebar-action-buttons {
width: $sidebar_width; width: $sidebar_width;
position: absolute; position: absolute;
top: 0; top: 0;
...@@ -110,28 +117,14 @@ ...@@ -110,28 +117,14 @@
padding: 5px 0; padding: 5px 0;
font-size: 18px; font-size: 18px;
line-height: 30px; line-height: 30px;
}
.nav-header-btn {
padding: 10px 5px;
color: inherit;
transition-duration: .3s;
&:hover, .toggle-nav-collapse {
&:focus { left: 0;
color: $white-light;
text-decoration: none;
} }
}
.pin-nav-btn { .pin-nav-btn {
right: 0;
display: none; display: none;
position: absolute;
left: 0;
bottom: 0;
height: 50px;
width: $sidebar_width;
line-height: 30px;
@media (min-width: $sidebar-breakpoint) { @media (min-width: $sidebar-breakpoint) {
display: block; display: block;
...@@ -146,6 +139,21 @@ ...@@ -146,6 +139,21 @@
transform: rotate(90deg); transform: rotate(90deg);
} }
} }
}
}
.nav-header-btn {
padding: 10px 16px;
color: inherit;
transition-duration: .3s;
position: absolute;
top: 0;
&:hover,
&:focus {
color: $white-light;
text-decoration: none;
}
} }
.page-sidebar-collapsed { .page-sidebar-collapsed {
......
...@@ -17,6 +17,7 @@ $focus-border-color: #3aabf0; ...@@ -17,6 +17,7 @@ $focus-border-color: #3aabf0;
$table-border-color: #f0f0f0; $table-border-color: #f0f0f0;
$background-color: #fafafa; $background-color: #fafafa;
$dark-background-color: #f7f7f7; $dark-background-color: #f7f7f7;
$table-text-gray: #8f8f8f;
/* /*
* Text * Text
......
...@@ -83,14 +83,6 @@ ...@@ -83,14 +83,6 @@
} }
} }
table.builds {
.build-link {
a {
color: $gl-dark-link-color;
}
}
}
.build-trace { .build-trace {
background: $ci-output-bg; background: $ci-output-bg;
color: $ci-text-color; color: $ci-text-color;
......
...@@ -61,7 +61,7 @@ ...@@ -61,7 +61,7 @@
font-size: 0; font-size: 0;
} }
.btn-transparent { .btn-clipboard, .btn-transparent {
padding-left: 0; padding-left: 0;
padding-right: 0; padding-right: 0;
} }
......
...@@ -63,8 +63,8 @@ form.edit-issue { ...@@ -63,8 +63,8 @@ form.edit-issue {
.merge-request, .merge-request,
.issue { .issue {
&.today { &.today {
background: #efe; background: #f8feef;
border-color: #cec; border-color: #e1e8d5;
} }
&.closed { &.closed {
......
...@@ -162,9 +162,15 @@ ...@@ -162,9 +162,15 @@
} }
.filtered-labels { .filtered-labels {
font-size: 0;
padding: 12px 16px;
.label-row { .label-row {
margin-top: 4px;
margin-bottom: 4px;
&:not(:last-child) { &:not(:last-child) {
margin-right: 5px; margin-right: 8px;
} }
} }
......
...@@ -73,11 +73,14 @@ ...@@ -73,11 +73,14 @@
color: #888; color: #888;
} }
&.ci-pending, &.ci-pending {
&.ci-running {
color: $gl-warning; color: $gl-warning;
} }
&.ci-running {
color: $blue-normal;
}
&.ci-failed, &.ci-failed,
&.ci-error { &.ci-error {
color: $gl-danger; color: $gl-danger;
......
.pipelines { .pipelines {
.stage { .stage {
max-width: 100px; max-width: 80px;
width: 80px;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
white-space: nowrap; white-space: nowrap;
} }
.duration, .finished_at {
margin: 4px 0;
}
.commit-title { .commit-title {
margin: 0; margin: 0;
} }
...@@ -22,3 +19,136 @@ ...@@ -22,3 +19,136 @@
margin: 4px; margin: 4px;
} }
} }
.content-list {
&.pipelines,
&.builds-content-list {
width: 100%;
overflow: auto;
}
}
.table.builds {
min-width: 1100px;
tr {
th {
padding: 16px;
border: none;
}
}
tbody {
border-top-width: 1px;
}
.branch-commit {
.branch-name {
margin-left: 8px;
font-weight: bold;
max-width: 180px;
overflow: hidden;
display: inline-block;
white-space: nowrap;
vertical-align: top;
text-overflow: ellipsis;
}
svg {
margin: 0 6px;
height: 14px;
width: auto;
vertical-align: middle;
}
.commit-id {
color: $gl-link-color;
margin-right: 8px;
}
.commit-title {
margin-top: 4px;
max-width: 320px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.avatar {
margin-left: 0;
}
.label-container {
.label {
margin-top: 5px;
}
}
}
.duration,
.finished-at {
color: $table-text-gray;
margin: 4px 0;
.fa {
font-size: 12px;
}
svg {
height: 12px;
width: auto;
vertical-align: middle;
}
.fa,
svg {
margin-right: 5px;
}
}
.pipeline-actions {
.btn {
margin: 0;
color: $table-text-gray;
}
.dropdown-toggle,
.dropdown-menu {
color: $table-text-gray;
.fa {
color: $table-text-gray;
margin-right: 6px;
font-size: 14px;
}
}
.btn-remove {
color: $white-light;
}
.btn-group {
&.open {
.btn-default {
background-color: $white-normal;
border-color: $border-white-normal;
}
}
}
}
.build-link {
a {
color: $gl-dark-link-color;
}
}
.btn-group.open .dropdown-toggle {
box-shadow: none;
}
}
...@@ -340,23 +340,30 @@ a.deploy-project-label { ...@@ -340,23 +340,30 @@ a.deploy-project-label {
.project-import { .project-import {
.form-group { .form-group {
margin-bottom: 0; margin-bottom: 5px;
} }
.import-buttons { .import-buttons {
padding-left: 0; padding-left: 0;
display: -webkit-flex; display: -webkit-flex;
display: flex; display: flex;
-webkit-flex-wrap: wrap; -webkit-flex-wrap: wrap;
flex-wrap: wrap; flex-wrap: wrap;
.btn { .btn {
margin-right: 10px; margin: 0 10px 10px 0;
padding: 8px 12px; padding: 8px;
} }
&> div {
margin-bottom: 14px; > div {
padding-left: 0; padding-left: 0;
&:last-child { &:last-child {
margin-bottom: 0; margin-bottom: 0;
.btn {
margin-right: 0;
}
} }
} }
} }
......
...@@ -32,11 +32,15 @@ ...@@ -32,11 +32,15 @@
border-color: $gl-gray; border-color: $gl-gray;
} }
&.ci-pending, &.ci-pending {
&.ci-running {
color: $gl-warning; color: $gl-warning;
border-color: $gl-warning; border-color: $gl-warning;
} }
&.ci-running {
color: $blue-normal;
border-color: $blue-normal;
}
} }
.ci-status-icon-success { .ci-status-icon-success {
...@@ -45,10 +49,12 @@ ...@@ -45,10 +49,12 @@
.ci-status-icon-failed { .ci-status-icon-failed {
color: $gl-danger; color: $gl-danger;
} }
.ci-status-icon-running,
.ci-status-icon-pending { .ci-status-icon-pending {
color: $gl-warning; color: $gl-warning;
} }
.ci-status-icon-running {
color: $blue-normal;
}
.ci-status-icon-canceled, .ci-status-icon-canceled,
.ci-status-icon-disabled, .ci-status-icon-disabled,
.ci-status-icon-not-found, .ci-status-icon-not-found,
......
module DiffForPath
extend ActiveSupport::Concern
def render_diff_for_path(diffs, diff_refs, project)
diff_file = safe_diff_files(diffs, diff_refs: diff_refs, repository: project.repository).find do |diff|
diff.old_path == params[:old_path] && diff.new_path == params[:new_path]
end
return render_404 unless diff_file
diff_commit = commit_for_diff(diff_file)
blob = diff_file.blob(diff_commit)
@expand_all_diffs = true
locals = {
diff_file: diff_file,
diff_commit: diff_commit,
diff_refs: diff_refs,
blob: blob,
project: project
}
render json: { html: view_to_html_string('projects/diffs/_content', locals) }
end
end
class Dashboard::TodosController < Dashboard::ApplicationController class Dashboard::TodosController < Dashboard::ApplicationController
include TodosHelper
before_action :find_todos, only: [:index, :destroy_all] before_action :find_todos, only: [:index, :destroy_all]
def index def index
...@@ -13,7 +11,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -13,7 +11,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
respond_to do |format| respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' } format.html { redirect_to dashboard_todos_path, notice: 'Todo was successfully marked as done.' }
format.js { head :ok } format.js { head :ok }
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } } format.json { render json: todos_counts }
end end
end end
...@@ -23,7 +21,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -23,7 +21,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController
respond_to do |format| respond_to do |format|
format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' } format.html { redirect_to dashboard_todos_path, notice: 'All todos were marked as done.' }
format.js { head :ok } format.js { head :ok }
format.json { render json: { count: todos_pending_count, done_count: todos_done_count } } format.json { render json: todos_counts }
end end
end end
...@@ -36,4 +34,11 @@ class Dashboard::TodosController < Dashboard::ApplicationController ...@@ -36,4 +34,11 @@ class Dashboard::TodosController < Dashboard::ApplicationController
def find_todos def find_todos
@todos ||= TodosFinder.new(current_user, params).execute @todos ||= TodosFinder.new(current_user, params).execute
end end
def todos_counts
{
count: TodosFinder.new(current_user, state: :pending).execute.count,
done_count: TodosFinder.new(current_user, state: :done).execute.count
}
end
end end
...@@ -12,13 +12,12 @@ class HelpController < ApplicationController ...@@ -12,13 +12,12 @@ class HelpController < ApplicationController
end end
def show def show
@category = clean_path_info(path_params[:category]) @path = clean_path_info(path_params[:path])
@file = path_params[:file]
respond_to do |format| respond_to do |format|
format.any(:markdown, :md, :html) do format.any(:markdown, :md, :html) do
# Note: We are purposefully NOT using `Rails.root.join` # Note: We are purposefully NOT using `Rails.root.join`
path = File.join(Rails.root, 'doc', @category, "#{@file}.md") path = File.join(Rails.root, 'doc', "#{@path}.md")
if File.exist?(path) if File.exist?(path)
@markdown = File.read(path) @markdown = File.read(path)
...@@ -33,7 +32,7 @@ class HelpController < ApplicationController ...@@ -33,7 +32,7 @@ class HelpController < ApplicationController
# Allow access to images in the doc folder # Allow access to images in the doc folder
format.any(:png, :gif, :jpeg) do format.any(:png, :gif, :jpeg) do
# Note: We are purposefully NOT using `Rails.root.join` # Note: We are purposefully NOT using `Rails.root.join`
path = File.join(Rails.root, 'doc', @category, "#{@file}.#{params[:format]}") path = File.join(Rails.root, 'doc', "#{@path}.#{params[:format]}")
if File.exist?(path) if File.exist?(path)
send_file(path, disposition: 'inline') send_file(path, disposition: 'inline')
...@@ -57,8 +56,7 @@ class HelpController < ApplicationController ...@@ -57,8 +56,7 @@ class HelpController < ApplicationController
private private
def path_params def path_params
params.require(:category) params.require(:path)
params.require(:file)
params params
end end
......
...@@ -107,7 +107,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController ...@@ -107,7 +107,11 @@ class OmniauthCallbacksController < Devise::OmniauthCallbacksController
# Only allow properly saved users to login. # Only allow properly saved users to login.
if @user.persisted? && @user.valid? if @user.persisted? && @user.valid?
log_audit_event(@user, with: oauth['provider']) log_audit_event(@user, with: oauth['provider'])
if @user.two_factor_enabled?
prompt_for_two_factor(@user)
else
sign_in_and_redirect(@user) sign_in_and_redirect(@user)
end
else else
error_message = @user.errors.full_messages.to_sentence error_message = @user.errors.full_messages.to_sentence
......
...@@ -23,10 +23,9 @@ class Projects::ArtifactsController < Projects::ApplicationController ...@@ -23,10 +23,9 @@ class Projects::ArtifactsController < Projects::ApplicationController
entry = build.artifacts_metadata_entry(params[:path]) entry = build.artifacts_metadata_entry(params[:path])
if entry.exists? if entry.exists?
render json: { archive: build.artifacts_file.path, send_artifacts_entry(build, entry)
entry: Base64.encode64(entry.path) }
else else
render json: {}, status: 404 render_404
end end
end end
......
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
# Not to be confused with CommitsController, plural. # Not to be confused with CommitsController, plural.
class Projects::CommitController < Projects::ApplicationController class Projects::CommitController < Projects::ApplicationController
include CreatesCommit include CreatesCommit
include DiffForPath
include DiffHelper include DiffHelper
# Authorize # Authorize
...@@ -11,29 +12,14 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -11,29 +12,14 @@ class Projects::CommitController < Projects::ApplicationController
before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds] before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds]
before_action :authorize_read_commit_status!, only: [:builds] before_action :authorize_read_commit_status!, only: [:builds]
before_action :commit before_action :commit
before_action :define_show_vars, only: [:show, :builds] before_action :define_commit_vars, only: [:show, :diff_for_path, :builds]
before_action :define_status_vars, only: [:show, :builds]
before_action :define_note_vars, only: [:show, :diff_for_path]
before_action :authorize_edit_tree!, only: [:revert, :cherry_pick] before_action :authorize_edit_tree!, only: [:revert, :cherry_pick]
def show def show
apply_diff_view_cookie! apply_diff_view_cookie!
@grouped_diff_notes = commit.notes.grouped_diff_notes
@notes = commit.notes.non_diff_notes.fresh
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten + @notes,
@project,
current_user,
)
@note = @project.build_commit_note(commit)
@noteable = @commit
@comments_target = {
noteable_type: 'Commit',
commit_id: @commit.id
}
respond_to do |format| respond_to do |format|
format.html format.html
format.diff { render text: @commit.to_diff } format.diff { render text: @commit.to_diff }
...@@ -41,6 +27,10 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -41,6 +27,10 @@ class Projects::CommitController < Projects::ApplicationController
end end
end end
def diff_for_path
render_diff_for_path(@diffs, @commit.diff_refs, @project)
end
def builds def builds
end end
...@@ -114,7 +104,7 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -114,7 +104,7 @@ class Projects::CommitController < Projects::ApplicationController
@ci_builds ||= Ci::Build.where(pipeline: pipelines) @ci_builds ||= Ci::Build.where(pipeline: pipelines)
end end
def define_show_vars def define_commit_vars
return git_not_found! unless commit return git_not_found! unless commit
opts = diff_options opts = diff_options
...@@ -122,7 +112,28 @@ class Projects::CommitController < Projects::ApplicationController ...@@ -122,7 +112,28 @@ class Projects::CommitController < Projects::ApplicationController
@diffs = commit.diffs(opts) @diffs = commit.diffs(opts)
@notes_count = commit.notes.count @notes_count = commit.notes.count
end
def define_note_vars
@grouped_diff_notes = commit.notes.grouped_diff_notes
@notes = commit.notes.non_diff_notes.fresh
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten + @notes,
@project,
current_user,
)
@note = @project.build_commit_note(commit)
@noteable = @commit
@comments_target = {
noteable_type: 'Commit',
commit_id: @commit.id
}
end
def define_status_vars
@statuses = CommitStatus.where(pipeline: pipelines) @statuses = CommitStatus.where(pipeline: pipelines)
@builds = Ci::Build.where(pipeline: pipelines) @builds = Ci::Build.where(pipeline: pipelines)
end end
......
require 'addressable/uri' require 'addressable/uri'
class Projects::CompareController < Projects::ApplicationController class Projects::CompareController < Projects::ApplicationController
include DiffForPath
include DiffHelper include DiffHelper
# Authorize # Authorize
before_action :require_non_empty_project before_action :require_non_empty_project
before_action :authorize_download_code! before_action :authorize_download_code!
before_action :assign_ref_vars, only: [:index, :show] before_action :define_ref_vars, only: [:index, :show, :diff_for_path]
before_action :define_diff_vars, only: [:show, :diff_for_path]
before_action :merge_request, only: [:index, :show] before_action :merge_request, only: [:index, :show]
def index def index
end end
def show def show
compare = CompareService.new. end
execute(@project, @head_ref, @project, @start_ref, diff_options)
def diff_for_path
return render_404 unless @compare
render_diff_for_path(@diffs, @diff_refs, @project)
end
def create
redirect_to namespace_project_compare_path(@project.namespace, @project,
params[:from], params[:to])
end
private
if compare def define_ref_vars
@commits = Commit.decorate(compare.commits, @project) @start_ref = Addressable::URI.unescape(params[:from])
@ref = @head_ref = Addressable::URI.unescape(params[:to])
end
def define_diff_vars
@compare = CompareService.new.execute(@project, @head_ref, @project, @start_ref)
if @compare
@commits = Commit.decorate(@compare.commits, @project)
@start_commit = @project.commit(@start_ref) @start_commit = @project.commit(@start_ref)
@commit = @project.commit(@head_ref) @commit = @project.commit(@head_ref)
@base_commit = @project.merge_base_commit(@start_ref, @head_ref) @base_commit = @project.merge_base_commit(@start_ref, @head_ref)
@diffs = compare.diffs(diff_options) @diffs = @compare.diffs(diff_options)
@diff_refs = Gitlab::Diff::DiffRefs.new( @diff_refs = Gitlab::Diff::DiffRefs.new(
base_sha: @base_commit.try(:sha), base_sha: @base_commit.try(:sha),
start_sha: @start_commit.try(:sha), start_sha: @start_commit.try(:sha),
...@@ -35,18 +57,6 @@ class Projects::CompareController < Projects::ApplicationController ...@@ -35,18 +57,6 @@ class Projects::CompareController < Projects::ApplicationController
end end
end end
def create
redirect_to namespace_project_compare_path(@project.namespace, @project,
params[:from], params[:to])
end
private
def assign_ref_vars
@start_ref = Addressable::URI.unescape(params[:from])
@ref = @head_ref = Addressable::URI.unescape(params[:to])
end
def merge_request def merge_request
@merge_request ||= @project.merge_requests.opened. @merge_request ||= @project.merge_requests.opened.
find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref) find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref)
......
...@@ -119,6 +119,10 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -119,6 +119,10 @@ class Projects::IssuesController < Projects::ApplicationController
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } }) render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
end end
end end
rescue ActiveRecord::StaleObjectError
@conflict = true
render :edit
end end
def referenced_merge_requests def referenced_merge_requests
...@@ -216,7 +220,7 @@ class Projects::IssuesController < Projects::ApplicationController ...@@ -216,7 +220,7 @@ class Projects::IssuesController < Projects::ApplicationController
def issue_params def issue_params
params.require(:issue).permit( params.require(:issue).permit(
:title, :assignee_id, :position, :description, :confidential, :title, :assignee_id, :position, :description, :confidential,
:milestone_id, :due_date, :state_event, :task_num, label_ids: [] :milestone_id, :due_date, :state_event, :task_num, :lock_version, label_ids: []
) )
end end
......
class Projects::MergeRequestsController < Projects::ApplicationController class Projects::MergeRequestsController < Projects::ApplicationController
include ToggleSubscriptionAction include ToggleSubscriptionAction
include DiffForPath
include DiffHelper include DiffHelper
include IssuableActions include IssuableActions
include ToggleAwardEmoji include ToggleAwardEmoji
...@@ -12,6 +13,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -12,6 +13,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController
before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds] before_action :validates_merge_request, only: [:show, :diffs, :commits, :builds]
before_action :define_show_vars, only: [:show, :diffs, :commits, :builds] before_action :define_show_vars, only: [:show, :diffs, :commits, :builds]
before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check] before_action :define_widget_vars, only: [:merge, :cancel_merge_when_build_succeeds, :merge_check]
before_action :define_commit_vars, only: [:diffs]
before_action :define_diff_comment_vars, only: [:diffs]
before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds] before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds]
# Allow read any merge_request # Allow read any merge_request
...@@ -53,7 +56,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -53,7 +56,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def show def show
respond_to do |format| respond_to do |format|
format.html format.html { define_discussion_vars }
format.json do format.json do
render json: @merge_request render json: @merge_request
...@@ -78,35 +81,38 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -78,35 +81,38 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request_diff = @merge_request.merge_request_diff @merge_request_diff = @merge_request.merge_request_diff
@commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
@comments_target = {
noteable_type: 'MergeRequest',
noteable_id: @merge_request.id
}
@use_legacy_diff_notes = !@merge_request.support_new_diff_notes?
@grouped_diff_notes = @merge_request.notes.grouped_diff_notes
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten,
@project,
current_user,
@path,
@project_wiki,
@ref
)
respond_to do |format| respond_to do |format|
format.html format.html { define_discussion_vars }
format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } } format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
end end
end end
# With an ID param, loads the MR at that ID. Otherwise, accepts the same params as #new
# and uses that (unsaved) MR.
#
def diff_for_path
if params[:id]
merge_request
define_diff_comment_vars
else
build_merge_request
@diff_notes_disabled = true
@grouped_diff_notes = {}
end
define_commit_vars
diffs = @merge_request.diffs(diff_options)
render_diff_for_path(diffs, @merge_request.diff_refs, @merge_request.project)
end
def commits def commits
respond_to do |format| respond_to do |format|
format.html { render 'show' } format.html do
define_discussion_vars
render 'show'
end
format.json do format.json do
# Get commits from repository # Get commits from repository
# or from cache if already merged # or from cache if already merged
...@@ -121,14 +127,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -121,14 +127,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def builds def builds
respond_to do |format| respond_to do |format|
format.html { render 'show' } format.html do
define_discussion_vars
render 'show'
end
format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } } format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_builds') } }
end end
end end
def new def new
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project) build_merge_request
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
@noteable = @merge_request @noteable = @merge_request
@target_branches = if @merge_request.target_project @target_branches = if @merge_request.target_project
...@@ -187,6 +196,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -187,6 +196,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
else else
render "edit" render "edit"
end end
rescue ActiveRecord::StaleObjectError
@conflict = true
render :edit
end end
def remove_wip def remove_wip
...@@ -352,14 +364,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -352,14 +364,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.unlock_mr @merge_request.unlock_mr
@merge_request.close @merge_request.close
end end
if request.format == :html || action_name == 'show'
define_show_html_vars
end
end end
# Discussion tab data is only required on html requests # Discussion tab data is rendered on html responses of actions
def define_show_html_vars # :show, :diff, :commits, :builds. but not when request the data through AJAX
def define_discussion_vars
# Build a note object for comment form # Build a note object for comment form
@note = @project.notes.new(noteable: @noteable) @note = @project.notes.new(noteable: @noteable)
...@@ -384,6 +393,30 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -384,6 +393,30 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@pipelines = [@pipeline].compact @pipelines = [@pipeline].compact
end end
def define_commit_vars
@commit = @merge_request.diff_head_commit
@base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit
end
def define_diff_comment_vars
@comments_target = {
noteable_type: 'MergeRequest',
noteable_id: @merge_request.id
}
@use_legacy_diff_notes = !@merge_request.support_new_diff_notes?
@grouped_diff_notes = @merge_request.notes.grouped_diff_notes
Banzai::NoteRenderer.render(
@grouped_diff_notes.values.flatten,
@project,
current_user,
@path,
@project_wiki,
@ref
)
end
def invalid_mr def invalid_mr
# Render special view for MR with removed source or target branch # Render special view for MR with removed source or target branch
render 'invalid' render 'invalid'
...@@ -394,7 +427,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -394,7 +427,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
:title, :assignee_id, :source_project_id, :source_branch, :title, :assignee_id, :source_project_id, :source_branch,
:target_project_id, :target_branch, :milestone_id, :target_project_id, :target_branch, :milestone_id,
:state_event, :description, :task_num, :force_remove_source_branch, :state_event, :description, :task_num, :force_remove_source_branch,
label_ids: [] :lock_version, label_ids: []
) )
end end
...@@ -412,4 +445,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController ...@@ -412,4 +445,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
params[:merge_when_build_succeeds].present? && params[:merge_when_build_succeeds].present? &&
@merge_request.pipeline && @merge_request.pipeline.active? @merge_request.pipeline && @merge_request.pipeline.active?
end end
def build_merge_request
params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
@merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
end
end end
...@@ -50,6 +50,6 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController ...@@ -50,6 +50,6 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
end end
def protected_branch_params def protected_branch_params
params.require(:protected_branch).permit(:name, :developers_can_push) params.require(:protected_branch).permit(:name, :developers_can_push, :developers_can_merge)
end end
end end
...@@ -5,7 +5,7 @@ class Projects::TodosController < Projects::ApplicationController ...@@ -5,7 +5,7 @@ class Projects::TodosController < Projects::ApplicationController
todo = TodoService.new.mark_todo(issuable, current_user) todo = TodoService.new.mark_todo(issuable, current_user)
render json: { render json: {
count: current_user.todos_pending_count, count: TodosFinder.new(current_user, state: :pending).execute.count,
delete_path: dashboard_todo_path(todo) delete_path: dashboard_todo_path(todo)
} }
end end
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
# action_id: integer # action_id: integer
# author_id: integer # author_id: integer
# project_id; integer # project_id; integer
# state: 'pending' or 'done' # state: 'pending' (default) or 'done'
# type: 'Issue' or 'MergeRequest' # type: 'Issue' or 'MergeRequest'
# #
...@@ -37,7 +37,7 @@ class TodosFinder ...@@ -37,7 +37,7 @@ class TodosFinder
private private
def action_id? def action_id?
action_id.present? && [Todo::ASSIGNED, Todo::MENTIONED, Todo::BUILD_FAILED, Todo::MARKED].include?(action_id.to_i) action_id.present? && Todo::ACTION_NAMES.has_key?(action_id.to_i)
end end
def action_id def action_id
......
...@@ -31,7 +31,7 @@ module AppearancesHelper ...@@ -31,7 +31,7 @@ module AppearancesHelper
end end
end end
def navbar_icon(icon_name, size: 16) def custom_icon(icon_name, size: 16)
render "shared/icons/#{icon_name}.svg", size: size render "shared/icons/#{icon_name}.svg", size: size
end end
end end
...@@ -12,7 +12,7 @@ module BranchesHelper ...@@ -12,7 +12,7 @@ module BranchesHelper
def can_push_branch?(project, branch_name) def can_push_branch?(project, branch_name)
return false unless project.repository.branch_exists?(branch_name) return false unless project.repository.branch_exists?(branch_name)
::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(branch_name) ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(branch_name)
end end
def project_branches def project_branches
......
...@@ -15,29 +15,13 @@ module ButtonHelper ...@@ -15,29 +15,13 @@ module ButtonHelper
# #
# See http://clipboardjs.com/#usage # See http://clipboardjs.com/#usage
def clipboard_button(data = {}) def clipboard_button(data = {})
data = { toggle: 'tooltip', placement: 'bottom', container: 'body' }.merge(data)
content_tag :button, content_tag :button,
icon('clipboard'), icon('clipboard'),
class: "btn btn-clipboard", class: "btn btn-clipboard",
data: data, data: data,
type: :button type: :button,
end title: "Copy to Clipboard"
# Output a "Copy to Clipboard" button with a custom CSS class
#
# data - Data attributes passed to `content_tag`
# css_class - Class passed to the `content_tag`
#
# Examples:
#
# # Define the target element
# clipboard_button_with_class({clipboard_target: "div#foo"}, css_class: "btn-clipboard")
# # => "<button class='btn btn-clipboard' data-clipboard-target='div#foo'>...</button>"
def clipboard_button_with_class(data = {}, css_class: 'btn-clipboard')
content_tag :button,
icon('clipboard'),
class: "btn #{css_class}",
data: data,
type: :button
end end
def http_clone_button(project, placement = 'right', append_link: true) def http_clone_button(project, placement = 'right', append_link: true)
......
...@@ -29,8 +29,10 @@ module CiStatusHelper ...@@ -29,8 +29,10 @@ module CiStatusHelper
'check' 'check'
when 'failed' when 'failed'
'close' 'close'
when 'running', 'pending' when 'pending'
'clock-o' 'clock-o'
when 'running'
'spinner'
else else
'circle' 'circle'
end end
......
...@@ -8,6 +8,10 @@ module DiffHelper ...@@ -8,6 +8,10 @@ module DiffHelper
[marked_old_line, marked_new_line] [marked_old_line, marked_new_line]
end end
def expand_all_diffs?
@expand_all_diffs || params[:expand_all_diffs].present?
end
def diff_view def diff_view
diff_views = %w(inline parallel) diff_views = %w(inline parallel)
...@@ -18,16 +22,14 @@ module DiffHelper ...@@ -18,16 +22,14 @@ module DiffHelper
end end
end end
def diff_hard_limit_enabled?
params[:force_show_diff].present?
end
def diff_options def diff_options
options = { ignore_whitespace_change: hide_whitespace? } default_options = Commit.max_diff_options
if diff_hard_limit_enabled?
options.merge!(Commit.max_diff_options) if action_name == 'diff_for_path'
default_options[:paths] = params.values_at(:old_path, :new_path)
end end
options
default_options.merge(ignore_whitespace_change: hide_whitespace?)
end end
def safe_diff_files(diffs, diff_refs: nil, repository: nil) def safe_diff_files(diffs, diff_refs: nil, repository: nil)
...@@ -35,7 +37,7 @@ module DiffHelper ...@@ -35,7 +37,7 @@ module DiffHelper
end end
def unfold_bottom_class(bottom) def unfold_bottom_class(bottom)
bottom ? 'js-unfold-bottom' : '' bottom ? 'js-unfold js-unfold-bottom' : ''
end end
def unfold_class(unfold) def unfold_class(unfold)
......
...@@ -96,4 +96,8 @@ module MergeRequestsHelper ...@@ -96,4 +96,8 @@ module MergeRequestsHelper
["#{source_path}:#{source_branch}", "#{target_path}:#{target_branch}"] ["#{source_path}:#{source_branch}", "#{target_path}:#{target_branch}"]
end end
end end
def merge_request_button_visibility(merge_request, closed)
return 'hidden' if merge_request.closed? == closed || (merge_request.merged? == closed && !merge_request.closed?)
end
end end
...@@ -24,7 +24,15 @@ module NotesHelper ...@@ -24,7 +24,15 @@ module NotesHelper
}.to_json }.to_json
end end
def link_to_new_diff_note(line_code, position, line_type = nil) def diff_view_data
return {} unless @comments_target
@comments_target.slice(:noteable_id, :noteable_type, :commit_id)
end
def diff_view_line_data(line_code, position, line_type)
return if @diff_notes_disabled
use_legacy_diff_note = @use_legacy_diff_notes use_legacy_diff_note = @use_legacy_diff_notes
# If the controller doesn't force the use of legacy diff notes, we # If the controller doesn't force the use of legacy diff notes, we
# determine this on a line-by-line basis by seeing if there already exist # determine this on a line-by-line basis by seeing if there already exist
...@@ -41,11 +49,8 @@ module NotesHelper ...@@ -41,11 +49,8 @@ module NotesHelper
end end
data = { data = {
noteable_type: @comments_target[:noteable_type], line_code: line_code,
noteable_id: @comments_target[:noteable_id],
commit_id: @comments_target[:commit_id],
line_type: line_type, line_type: line_type,
line_code: line_code
} }
if use_legacy_diff_note if use_legacy_diff_note
...@@ -73,11 +78,7 @@ module NotesHelper ...@@ -73,11 +78,7 @@ module NotesHelper
) )
end end
button_tag(class: 'btn add-diff-note js-add-diff-note-button', data
data: data,
title: 'Add a comment to this line') do
icon('comment-o')
end
end end
def link_to_reply_discussion(note, line_type = nil) def link_to_reply_discussion(note, line_type = nil)
......
...@@ -43,15 +43,15 @@ module SearchHelper ...@@ -43,15 +43,15 @@ module SearchHelper
# Autocomplete results for internal help pages # Autocomplete results for internal help pages
def help_autocomplete def help_autocomplete
[ [
{ category: "Help", label: "API Help", url: help_page_path("api", "README") }, { category: "Help", label: "API Help", url: help_page_path("api/README") },
{ category: "Help", label: "Markdown Help", url: help_page_path("markdown", "markdown") }, { category: "Help", label: "Markdown Help", url: help_page_path("markdown/markdown") },
{ category: "Help", label: "Permissions Help", url: help_page_path("permissions", "permissions") }, { category: "Help", label: "Permissions Help", url: help_page_path("permissions/permissions") },
{ category: "Help", label: "Public Access Help", url: help_page_path("public_access", "public_access") }, { category: "Help", label: "Public Access Help", url: help_page_path("public_access/public_access") },
{ category: "Help", label: "Rake Tasks Help", url: help_page_path("raketasks", "README") }, { category: "Help", label: "Rake Tasks Help", url: help_page_path("raketasks/README") },
{ category: "Help", label: "SSH Keys Help", url: help_page_path("ssh", "README") }, { 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: "System Hooks Help", url: help_page_path("system_hooks/system_hooks") },
{ category: "Help", label: "Webhooks Help", url: help_page_path("web_hooks", "web_hooks") }, { category: "Help", label: "Webhooks Help", url: help_page_path("web_hooks/web_hooks") },
{ category: "Help", label: "Workflow Help", url: help_page_path("workflow", "README") }, { category: "Help", label: "Workflow Help", url: help_page_path("workflow/README") },
] ]
end end
......
module TodosHelper module TodosHelper
def todos_pending_count def todos_pending_count
TodosFinder.new(current_user, state: :pending).execute.count @todos_pending_count ||= TodosFinder.new(current_user, state: :pending).execute.count
end end
def todos_done_count def todos_done_count
TodosFinder.new(current_user, state: :done).execute.count @todos_done_count ||= TodosFinder.new(current_user, state: :done).execute.count
end end
def todo_action_name(todo) def todo_action_name(todo)
...@@ -13,6 +13,7 @@ module TodosHelper ...@@ -13,6 +13,7 @@ module TodosHelper
when Todo::MENTIONED then 'mentioned you on' when Todo::MENTIONED then 'mentioned you on'
when Todo::BUILD_FAILED then 'The build failed for your' when Todo::BUILD_FAILED then 'The build failed for your'
when Todo::MARKED then 'added a todo for' when Todo::MARKED then 'added a todo for'
when Todo::APPROVAL_REQUIRED then 'set you as an approver for'
end end
end end
......
...@@ -28,4 +28,10 @@ module WorkhorseHelper ...@@ -28,4 +28,10 @@ module WorkhorseHelper
headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format)) headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
head :ok head :ok
end end
# Send an entry from artifacts through Workhorse
def send_artifacts_entry(build, entry)
headers.store(*Gitlab::Workhorse.send_artifacts_entry(build, entry))
head :ok
end
end end
...@@ -29,6 +29,7 @@ module Emails ...@@ -29,6 +29,7 @@ module Emails
# used in notify layout # used in notify layout
@target_url = @message.target_url @target_url = @message.target_url
@project = Project.find(project_id) @project = Project.find(project_id)
@diff_notes_disabled = true
add_project_headers add_project_headers
headers['X-GitLab-Author'] = @message.author_username headers['X-GitLab-Author'] = @message.author_username
......
...@@ -87,6 +87,12 @@ module Issuable ...@@ -87,6 +87,12 @@ module Issuable
User.find(assignee_id_was).update_cache_counts if assignee_id_was User.find(assignee_id_was).update_cache_counts if assignee_id_was
assignee.update_cache_counts if assignee assignee.update_cache_counts if assignee
end end
# We want to use optimistic lock for cases when only title or description are involved
# http://api.rubyonrails.org/classes/ActiveRecord/Locking/Optimistic.html
def locking_enabled?
title_changed? || description_changed?
end
end end
module ClassMethods module ClassMethods
......
...@@ -19,7 +19,7 @@ class MergeRequest < ActiveRecord::Base ...@@ -19,7 +19,7 @@ class MergeRequest < ActiveRecord::Base
after_create :create_merge_request_diff, unless: :importing? after_create :create_merge_request_diff, unless: :importing?
after_update :update_merge_request_diff after_update :update_merge_request_diff
delegate :commits, :diffs, :real_size, to: :merge_request_diff, prefix: nil delegate :commits, :real_size, to: :merge_request_diff, prefix: nil
# When this attribute is true some MR validation is ignored # When this attribute is true some MR validation is ignored
# It allows us to close or modify broken merge requests # It allows us to close or modify broken merge requests
...@@ -164,6 +164,10 @@ class MergeRequest < ActiveRecord::Base ...@@ -164,6 +164,10 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
end end
def diffs(*args)
merge_request_diff ? merge_request_diff.diffs(*args) : compare.diffs(*args)
end
def diff_size def diff_size
merge_request_diff.size merge_request_diff.size
end end
...@@ -548,7 +552,13 @@ class MergeRequest < ActiveRecord::Base ...@@ -548,7 +552,13 @@ class MergeRequest < ActiveRecord::Base
end end
def can_be_merged_by?(user) def can_be_merged_by?(user)
::Gitlab::GitAccess.new(user, project, 'web').can_push_to_branch?(target_branch) access = ::Gitlab::UserAccess.new(user, project: project)
access.can_push_to_branch?(target_branch) || access.can_merge_to_branch?(target_branch)
end
def can_be_merged_via_command_line_by?(user)
access = ::Gitlab::UserAccess.new(user, project: project)
access.can_push_to_branch?(target_branch)
end end
def mergeable_ci_state? def mergeable_ci_state?
......
...@@ -46,7 +46,8 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -46,7 +46,8 @@ class MergeRequestDiff < ActiveRecord::Base
compare.diffs(options) compare.diffs(options)
end end
else else
@diffs ||= load_diffs(st_diffs, options) @diffs ||= {}
@diffs[options] ||= load_diffs(st_diffs, options)
end end
end end
...@@ -144,6 +145,12 @@ class MergeRequestDiff < ActiveRecord::Base ...@@ -144,6 +145,12 @@ class MergeRequestDiff < ActiveRecord::Base
def load_diffs(raw, options) def load_diffs(raw, options)
if raw.respond_to?(:each) if raw.respond_to?(:each)
if paths = options[:paths]
raw = raw.select do |diff|
paths.include?(diff[:old_path]) || paths.include?(diff[:new_path])
end
end
Gitlab::Git::DiffCollection.new(raw, options) Gitlab::Git::DiffCollection.new(raw, options)
else else
Gitlab::Git::DiffCollection.new([]) Gitlab::Git::DiffCollection.new([])
......
...@@ -10,6 +10,10 @@ class Note < ActiveRecord::Base ...@@ -10,6 +10,10 @@ class Note < ActiveRecord::Base
# Banzai::ObjectRenderer. # Banzai::ObjectRenderer.
attr_accessor :note_html attr_accessor :note_html
# An Array containing the number of visible references as generated by
# Banzai::ObjectRenderer
attr_accessor :user_visible_reference_count
default_value_for :system, false default_value_for :system, false
attr_mentionable :note, pipeline: :note attr_mentionable :note, pipeline: :note
...@@ -193,7 +197,15 @@ class Note < ActiveRecord::Base ...@@ -193,7 +197,15 @@ class Note < ActiveRecord::Base
end end
def cross_reference_not_visible_for?(user) def cross_reference_not_visible_for?(user)
cross_reference? && referenced_mentionables(user).empty? cross_reference? && !has_referenced_mentionables?(user)
end
def has_referenced_mentionables?(user)
if user_visible_reference_count.present?
user_visible_reference_count > 0
else
referenced_mentionables(user).any?
end
end end
def award_emoji? def award_emoji?
......
...@@ -832,6 +832,10 @@ class Project < ActiveRecord::Base ...@@ -832,6 +832,10 @@ class Project < ActiveRecord::Base
protected_branches.matching(branch_name).any?(&:developers_can_push) protected_branches.matching(branch_name).any?(&:developers_can_push)
end end
def developers_can_merge_to_protected_branch?(branch_name)
protected_branches.matching(branch_name).any?(&:developers_can_merge)
end
def forked? def forked?
!(forked_project_link.nil? || forked_project_link.forked_from_project.nil?) !(forked_project_link.nil? || forked_project_link.forked_from_project.nil?)
end end
......
...@@ -769,9 +769,9 @@ class Repository ...@@ -769,9 +769,9 @@ class Repository
end end
end end
def merge(user, source_sha, target_branch, options = {}) def merge(user, merge_request, options = {})
our_commit = rugged.branches[target_branch].target our_commit = rugged.branches[merge_request.target_branch].target
their_commit = rugged.lookup(source_sha) their_commit = rugged.lookup(merge_request.diff_head_sha)
raise "Invalid merge target" if our_commit.nil? raise "Invalid merge target" if our_commit.nil?
raise "Invalid merge source" if their_commit.nil? raise "Invalid merge source" if their_commit.nil?
...@@ -779,14 +779,16 @@ class Repository ...@@ -779,14 +779,16 @@ class Repository
merge_index = rugged.merge_commits(our_commit, their_commit) merge_index = rugged.merge_commits(our_commit, their_commit)
return false if merge_index.conflicts? return false if merge_index.conflicts?
commit_with_hooks(user, target_branch) do |ref| commit_with_hooks(user, merge_request.target_branch) do |tmp_ref|
actual_options = options.merge( actual_options = options.merge(
parents: [our_commit, their_commit], parents: [our_commit, their_commit],
tree: merge_index.write_tree(rugged), tree: merge_index.write_tree(rugged),
update_ref: ref update_ref: tmp_ref
) )
Rugged::Commit.create(rugged, actual_options) commit_id = Rugged::Commit.create(rugged, actual_options)
merge_request.update(in_progress_merge_commit_sha: commit_id)
commit_id
end end
end end
......
...@@ -72,6 +72,19 @@ class SentNotification < ActiveRecord::Base ...@@ -72,6 +72,19 @@ class SentNotification < ActiveRecord::Base
end end
end end
def position=(new_position)
if new_position.is_a?(String)
new_position = JSON.parse(new_position) rescue nil
end
if new_position.is_a?(Hash)
new_position = new_position.with_indifferent_access
new_position = Gitlab::Diff::Position.new(new_position)
end
super(new_position)
end
def to_param def to_param
self.reply_key self.reply_key
end end
......
...@@ -3,12 +3,14 @@ class Todo < ActiveRecord::Base ...@@ -3,12 +3,14 @@ class Todo < ActiveRecord::Base
MENTIONED = 2 MENTIONED = 2
BUILD_FAILED = 3 BUILD_FAILED = 3
MARKED = 4 MARKED = 4
APPROVAL_REQUIRED = 5 # This is an EE-only feature
ACTION_NAMES = { ACTION_NAMES = {
ASSIGNED => :assigned, ASSIGNED => :assigned,
MENTIONED => :mentioned, MENTIONED => :mentioned,
BUILD_FAILED => :build_failed, BUILD_FAILED => :build_failed,
MARKED => :marked MARKED => :marked,
APPROVAL_REQUIRED => :approval_required
} }
belongs_to :author, class_name: "User" belongs_to :author, class_name: "User"
......
...@@ -87,7 +87,7 @@ class User < ActiveRecord::Base ...@@ -87,7 +87,7 @@ class User < ActiveRecord::Base
has_many :builds, dependent: :nullify, class_name: 'Ci::Build' has_many :builds, dependent: :nullify, class_name: 'Ci::Build'
has_many :todos, dependent: :destroy has_many :todos, dependent: :destroy
has_many :notification_settings, dependent: :destroy has_many :notification_settings, dependent: :destroy
has_many :award_emoji, as: :awardable, dependent: :destroy has_many :award_emoji, dependent: :destroy
# #
# Validations # Validations
......
...@@ -23,7 +23,7 @@ module Commits ...@@ -23,7 +23,7 @@ module Commits
private private
def check_push_permissions def check_push_permissions
allowed = ::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(@target_branch) allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
unless allowed unless allowed
raise ValidationError.new('You are not allowed to push into this branch') raise ValidationError.new('You are not allowed to push into this branch')
......
...@@ -3,7 +3,7 @@ require 'securerandom' ...@@ -3,7 +3,7 @@ require 'securerandom'
# Compare 2 branches for one repo or between repositories # Compare 2 branches for one repo or between repositories
# and return Gitlab::Git::Compare object that responds to commits and diffs # and return Gitlab::Git::Compare object that responds to commits and diffs
class CompareService class CompareService
def execute(source_project, source_branch, target_project, target_branch, diff_options = {}) def execute(source_project, source_branch, target_project, target_branch)
source_commit = source_project.commit(source_branch) source_commit = source_project.commit(source_branch)
return unless source_commit return unless source_commit
......
...@@ -42,7 +42,7 @@ module Files ...@@ -42,7 +42,7 @@ module Files
end end
def validate def validate
allowed = ::Gitlab::GitAccess.new(current_user, project, 'web').can_push_to_branch?(@target_branch) allowed = ::Gitlab::UserAccess.new(current_user, project: project).can_push_to_branch?(@target_branch)
unless allowed unless allowed
raise_error("You are not allowed to push into this branch") raise_error("You are not allowed to push into this branch")
......
...@@ -89,7 +89,8 @@ class GitPushService < BaseService ...@@ -89,7 +89,8 @@ class GitPushService < BaseService
# Set protection on the default branch if configured # Set protection on the default branch if configured
if current_application_settings.default_branch_protection != PROTECTION_NONE if current_application_settings.default_branch_protection != PROTECTION_NONE
developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false developers_can_push = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_PUSH ? true : false
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push }) developers_can_merge = current_application_settings.default_branch_protection == PROTECTION_DEV_CAN_MERGE ? true : false
@project.protected_branches.create({ name: @project.default_branch, developers_can_push: developers_can_push, developers_can_merge: developers_can_merge })
end end
end end
......
...@@ -34,7 +34,7 @@ module MergeRequests ...@@ -34,7 +34,7 @@ module MergeRequests
committer: committer committer: committer
} }
commit_id = repository.merge(current_user, merge_request.diff_head_sha, merge_request.target_branch, options) commit_id = repository.merge(current_user, merge_request, options)
merge_request.update(merge_commit_sha: commit_id) merge_request.update(merge_commit_sha: commit_id)
rescue GitHooksService::PreReceiveError => e rescue GitHooksService::PreReceiveError => e
merge_request.update(merge_error: e.message) merge_request.update(merge_error: e.message)
...@@ -43,6 +43,8 @@ module MergeRequests ...@@ -43,6 +43,8 @@ module MergeRequests
merge_request.update(merge_error: "Something went wrong during merge") merge_request.update(merge_error: "Something went wrong during merge")
Rails.logger.error(e.message) Rails.logger.error(e.message)
false false
ensure
merge_request.update(in_progress_merge_commit_sha: nil)
end end
def after_merge def after_merge
......
...@@ -48,7 +48,7 @@ module MergeRequests ...@@ -48,7 +48,7 @@ module MergeRequests
end end
def force_push? def force_push?
Gitlab::ForcePushCheck.force_push?(@project, @oldrev, @newrev) Gitlab::Checks::ForcePush.force_push?(@project, @oldrev, @newrev)
end end
# Refresh merge request diff if we push to source or target branch of merge request # Refresh merge request diff if we push to source or target branch of merge request
......
...@@ -7,8 +7,6 @@ ...@@ -7,8 +7,6 @@
# #
module Projects module Projects
class HousekeepingService < BaseService class HousekeepingService < BaseService
include Gitlab::ShellAdapter
LEASE_TIMEOUT = 3600 LEASE_TIMEOUT = 3600
class LeaseTaken < StandardError class LeaseTaken < StandardError
...@@ -24,11 +22,7 @@ module Projects ...@@ -24,11 +22,7 @@ module Projects
def execute def execute
raise LeaseTaken unless try_obtain_lease raise LeaseTaken unless try_obtain_lease
GitlabShellOneShotWorker.perform_async(:gc, @project.repository_storage_path, @project.path_with_namespace) execute_gitlab_shell_gc
ensure
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
update_pushes_since_gc(0)
end
end end
def needed? def needed?
...@@ -36,18 +30,26 @@ module Projects ...@@ -36,18 +30,26 @@ module Projects
end end
def increment! def increment!
if Gitlab::ExclusiveLease.new("project_housekeeping:increment!:#{@project.id}", timeout: 60).try_obtain
Gitlab::Metrics.measure(:increment_pushes_since_gc) do Gitlab::Metrics.measure(:increment_pushes_since_gc) do
update_pushes_since_gc(@project.pushes_since_gc + 1) update_pushes_since_gc(@project.pushes_since_gc + 1)
end end
end end
end
private private
def execute_gitlab_shell_gc
GitGarbageCollectWorker.perform_async(@project.id)
ensure
Gitlab::Metrics.measure(:reset_pushes_since_gc) do
update_pushes_since_gc(0)
end
end
def update_pushes_since_gc(new_value) def update_pushes_since_gc(new_value)
if Gitlab::ExclusiveLease.new("project_housekeeping:update_pushes_since_gc:#{project.id}", timeout: 60).try_obtain
@project.update_column(:pushes_since_gc, new_value) @project.update_column(:pushes_since_gc, new_value)
end end
end
def try_obtain_lease def try_obtain_lease
Gitlab::Metrics.measure(:obtain_housekeeping_lease) do Gitlab::Metrics.measure(:obtain_housekeeping_lease) do
......
...@@ -13,7 +13,7 @@ ...@@ -13,7 +13,7 @@
.col-sm-10 .col-sm-10
= f.text_area :description, class: "form-control", rows: 10 = f.text_area :description, class: "form-control", rows: 10
.hint .hint
Description parsed with #{link_to "GitLab Flavored Markdown", help_page_path('markdown', 'markdown'), target: '_blank'}. Description parsed with #{link_to "GitLab Flavored Markdown", help_page_path('markdown/markdown'), target: '_blank'}.
.form-group .form-group
= f.label :logo, class: 'control-label' = f.label :logo, class: 'control-label'
.col-sm-10 .col-sm-10
......
...@@ -38,11 +38,11 @@ ...@@ -38,11 +38,11 @@
= source = source
%span.help-block#import-sources-help %span.help-block#import-sources-help
Enabled sources for code import during project creation. OmniAuth must be configured for GitHub Enabled sources for code import during project creation. OmniAuth must be configured for GitHub
= link_to "(?)", help_page_path("integration", "github") = link_to "(?)", help_page_path("integration/github")
, Bitbucket , Bitbucket
= link_to "(?)", help_page_path("integration", "bitbucket") = link_to "(?)", help_page_path("integration/bitbucket")
and GitLab.com and GitLab.com
= link_to "(?)", help_page_path("integration", "gitlab") = link_to "(?)", help_page_path("integration/gitlab")
.form-group .form-group
%label.control-label.col-sm-2 Enabled Git access protocols %label.control-label.col-sm-2 Enabled Git access protocols
.col-sm-10 .col-sm-10
......
- project = build.project - project = build.project
%tr.build %tr.build.commit
%td.status %td.status
= ci_status_with_icon(build.status) = ci_status_with_icon(build.status)
%td.build-link %td
.branch-commit
- if can?(current_user, :read_build, build.project) - if can?(current_user, :read_build, build.project)
= link_to namespace_project_build_url(build.project.namespace, build.project, build) do = link_to namespace_project_build_url(build.project.namespace, build.project, build) do
%strong Build ##{build.id} %span.build-link ##{build.id}
- else - else
%strong Build ##{build.id} %span.build-link ##{build.id}
- if build.stuck? - if build.stuck?
%i.fa.fa-warning.text-warning %i.fa.fa-warning.text-warning
%td
- if project
= link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
%td
= link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace"
%td
- if build.ref - if build.ref
= link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref) = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name"
- else - else
.light none .light none
= custom_icon("icon_commit")
%td = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "monospace commit-id"
- if build.try(:runner)
= runner_link(build.runner)
- else
.light none
%td
#{build.stage} / #{build.name}
%td .label-container
- if build.tags.any? - if build.tags.any?
- build.tags.each do |tag| - build.tags.each do |tag|
%span.label.label-primary %span.label.label-primary
...@@ -45,12 +32,28 @@ ...@@ -45,12 +32,28 @@
- if build.try(:allow_failure) - if build.try(:allow_failure)
%span.label.label-danger allowed to fail %span.label.label-danger allowed to fail
%td.duration %td
- if project
= link_to project.name_with_namespace, admin_namespace_project_path(project.namespace, project)
%td
- if build.try(:runner)
= runner_link(build.runner)
- else
.light none
%td
#{build.stage} / #{build.name}
%td
- if build.duration - if build.duration
#{duration_in_words(build.finished_at, build.started_at)} %p.duration
= custom_icon("icon_timer")
= duration_in_numbers(build.finished_at, build.started_at)
%td.timestamp
- if build.finished_at - if build.finished_at
%p.finished-at
= icon("calendar")
%span #{time_ago_with_tooltip(build.finished_at)} %span #{time_ago_with_tooltip(build.finished_at)}
- if defined?(coverage) && coverage - if defined?(coverage) && coverage
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
.row-content-block.second-block .row-content-block.second-block
#{(@scope || 'all').capitalize} builds #{(@scope || 'all').capitalize} builds
%ul.content-list %ul.content-list.builds-content-list
- if @builds.blank? - if @builds.blank?
%li %li
.nothing-here-block No builds to show .nothing-here-block No builds to show
...@@ -37,15 +37,11 @@ ...@@ -37,15 +37,11 @@
%thead %thead
%tr %tr
%th Status %th Status
%th Build ID
%th Project
%th Commit %th Commit
%th Ref %th Project
%th Runner %th Runner
%th Name %th Name
%th Tags %th
%th Duration
%th Finished at
%th %th
- @builds.each do |build| - @builds.each do |build|
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
.col-sm-10 .col-sm-10
%p.light %p.light
Paste a machine public key here. Read more about how to generate it Paste a machine public key here. Read more about how to generate it
= link_to "here", help_page_path("ssh", "README") = link_to "here", help_page_path("ssh/README")
= f.text_area :key, class: "form-control thin_area", rows: 5 = f.text_area :key, class: "form-control thin_area", rows: 5
.form-actions .form-actions
......
...@@ -79,7 +79,7 @@ ...@@ -79,7 +79,7 @@
.panel-body.form-holder .panel-body.form-holder
%p.light %p.light
Read more about project permissions Read more about project permissions
%strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" %strong= link_to "here", help_page_path("permissions/permissions"), class: "vlink"
= form_tag members_update_admin_group_path(@group), id: "new_project_member", class: "bulk_import", method: :put do = form_tag members_update_admin_group_path(@group), id: "new_project_member", class: "bulk_import", method: :put do
%div %div
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
System hooks System hooks
%p.light %p.light
#{link_to "System hooks ", help_page_path("system_hooks", "system_hooks"), class: "vlink"} can be #{link_to "System hooks ", help_page_path("system_hooks/system_hooks"), class: "vlink"} can be
used for binding events when GitLab creates a User or Project. used for binding events when GitLab creates a User or Project.
%hr %hr
......
...@@ -132,7 +132,7 @@ ...@@ -132,7 +132,7 @@
- else - else
passed. passed.
= link_to icon('question-circle'), help_page_path('administration', 'repository_checks') = link_to icon('question-circle'), help_page_path('administration/repository_checks')
.form-group .form-group
= f.submit 'Trigger repository check', class: 'btn btn-primary' = f.submit 'Trigger repository check', class: 'btn btn-primary'
......
...@@ -9,14 +9,14 @@ ...@@ -9,14 +9,14 @@
%span %span
To do To do
%span.badge %span.badge
= todos_pending_count = number_with_delimiter(todos_pending_count)
- todo_done_active = ('active' if params[:state] == 'done') - todo_done_active = ('active' if params[:state] == 'done')
%li{class: "todos-done #{todo_done_active}"} %li{class: "todos-done #{todo_done_active}"}
= link_to todos_filter_path(state: 'done') do = link_to todos_filter_path(state: 'done') do
%span %span
Done Done
%span.badge %span.badge
= todos_done_count = number_with_delimiter(todos_done_count)
.nav-controls .nav-controls
- if @todos.any?(&:pending?) - if @todos.any?(&:pending?)
......
...@@ -3,4 +3,4 @@ ...@@ -3,4 +3,4 @@
%h3 Access Denied %h3 Access Denied
%hr %hr
%p You are not allowed to access this page. %p You are not allowed to access this page.
%p Read more about project permissions #{link_to "here", help_page_path("permissions", "permissions"), class: "vlink"} %p Read more about project permissions #{link_to "here", help_page_path("permissions/permissions"), class: "vlink"}
...@@ -12,7 +12,7 @@ ...@@ -12,7 +12,7 @@
= select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "project-access-select select2" = select_tag :access_level, options_for_select(GroupMember.access_level_roles, @group_member.access_level), class: "project-access-select select2"
.help-block .help-block
Read more about role permissions Read more about role permissions
%strong= link_to "here", help_page_path("permissions", "permissions"), class: "vlink" %strong= link_to "here", help_page_path("permissions/permissions"), class: "vlink"
.form-actions .form-actions
= f.submit 'Add users to group', class: "btn btn-create" = f.submit 'Add users to group', class: "btn btn-create"
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
.nav-controls .nav-controls
- if can?(current_user, :admin_milestones, @group) - if can?(current_user, :admin_milestones, @group)
= link_to new_group_milestone_path(@group), class: "btn btn-new" do = link_to new_group_milestone_path(@group), class: "btn btn-new" do
= icon('plus')
New Milestone New Milestone
.row-content-block .row-content-block
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
- if can? current_user, :admin_group, @group - if can? current_user, :admin_group, @group
.controls .controls
= link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do = link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do
= icon('plus')
New Project New Project
%ul.well-list %ul.well-list
- @projects.each do |project| - @projects.each do |project|
......
...@@ -6,7 +6,6 @@ ...@@ -6,7 +6,6 @@
.cover-block.groups-cover-block .cover-block.groups-cover-block
%div{ class: container_class } %div{ class: container_class }
= link_to group_icon(@group), target: '_blank' do
= image_tag group_icon(@group), class: "avatar group-avatar s70" = image_tag group_icon(@group), class: "avatar group-avatar s70"
.group-info .group-info
.cover-title .cover-title
......
- page_title @file.humanize, *@category.split("/").reverse.map(&:humanize) - page_title @path.split("/").reverse.map(&:humanize)
.documentation.wiki .documentation.wiki
= markdown @markdown.gsub('$your_email', current_user.try(:email) || "email@example.com") = markdown @markdown.gsub('$your_email', current_user.try(:email) || "email@example.com")
...@@ -549,4 +549,4 @@ ...@@ -549,4 +549,4 @@
%li wiki page %li wiki page
%li help page %li help page
You can check how markdown rendered at #{link_to 'Markdown help page', help_page_path("markdown", "markdown")}. You can check how markdown rendered at #{link_to 'Markdown help page', help_page_path("markdown/markdown")}.
...@@ -38,6 +38,6 @@ ...@@ -38,6 +38,6 @@
As an administrator you may like to configure As an administrator you may like to configure
- else - else
Consider asking your GitLab administrator to configure Consider asking your GitLab administrator to configure
= link_to 'GitHub integration', help_page_path("integration", "github") = link_to 'GitHub integration', help_page_path("integration/github")
which will allow login via GitHub and allow importing projects without which will allow login via GitHub and allow importing projects without
generating a Personal Access Token. generating a Personal Access Token.
= link_to '#', class: 'nav-header-btn text-center toggle-nav-collapse', title: "Open/Close" do
%span.sr-only Toggle navigation
= icon('bars')
.flash-container .flash-container.flash-container-page
- if alert - if alert
.flash-alert .flash-alert
= alert = alert
......
.page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" } .page-with-sidebar{ class: "#{page_sidebar_class} #{page_gutter_class}" }
.sidebar-wrapper.nicescroll{ class: nav_sidebar_class } .sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
= render partial: 'layouts/collapse_button' .sidebar-action-buttons
= link_to '#', class: 'nav-header-btn toggle-nav-collapse', title: "Open/Close" do
%span.sr-only Toggle navigation
= icon('bars')
= link_to '#', class: "nav-header-btn pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
%span.sr-only Toggle navigation pinning
= icon('thumb-tack')
- if defined?(sidebar) && sidebar - if defined?(sidebar) && sidebar
= render "layouts/nav/#{sidebar}" = render "layouts/nav/#{sidebar}"
- elsif current_user - elsif current_user
...@@ -8,9 +15,6 @@ ...@@ -8,9 +15,6 @@
- else - else
= render 'layouts/nav/explore' = render 'layouts/nav/explore'
= link_to '#', class: "nav-header-btn text-center pin-nav-btn has-tooltip #{'is-active' if pinned_nav?} js-nav-pin", title: pinned_nav? ? "Unpin navigation" : "Pin Navigation", data: {placement: 'right', container: 'body'} do
%span.sr-only Toggle navigation pinning
= icon('thumb-tack')
- if defined?(nav) && nav - if defined?(nav) && nav
.layout-nav .layout-nav
.container-fluid .container-fluid
......
%ul.nav.nav-sidebar %ul.nav.nav-sidebar
= nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: "#{project_tab_class} home"}) do
= link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do = link_to dashboard_projects_path, title: 'Projects', class: 'dashboard-shortcuts-projects' do
.icon-container
= navbar_icon('project')
%span %span
Projects Projects
= nav_link(controller: :todos) do = nav_link(controller: :todos) do
= link_to dashboard_todos_path, title: 'Todos' do = link_to dashboard_todos_path, title: 'Todos' do
.icon-container
= icon('bell fw')
%span %span
Todos Todos
%span.count= number_with_delimiter(todos_pending_count) %span.count= number_with_delimiter(todos_pending_count)
= nav_link(path: 'dashboard#activity') do = nav_link(path: 'dashboard#activity') do
= link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do = link_to activity_dashboard_path, class: 'dashboard-shortcuts-activity', title: 'Activity' do
.icon-container
= navbar_icon('activity')
%span %span
Activity Activity
= nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do
= link_to dashboard_groups_path, title: 'Groups' do = link_to dashboard_groups_path, title: 'Groups' do
.icon-container
= navbar_icon('group')
%span %span
Groups Groups
= nav_link(controller: 'dashboard/milestones') do = nav_link(controller: 'dashboard/milestones') do
= link_to dashboard_milestones_path, title: 'Milestones' do = link_to dashboard_milestones_path, title: 'Milestones' do
.icon-container
= navbar_icon('milestones')
%span %span
Milestones Milestones
= nav_link(path: 'dashboard#issues') do = nav_link(path: 'dashboard#issues') do
= link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do = link_to assigned_issues_dashboard_path, title: 'Issues', class: 'dashboard-shortcuts-issues' do
.icon-container
= navbar_icon('issues')
%span %span
Issues Issues
%span.count= number_with_delimiter(current_user.assigned_issues.opened.count) %span.count= number_with_delimiter(current_user.assigned_issues.opened.count)
= nav_link(path: 'dashboard#merge_requests') do = nav_link(path: 'dashboard#merge_requests') do
= link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do = link_to assigned_mrs_dashboard_path, title: 'Merge Requests', class: 'dashboard-shortcuts-merge_requests' do
.icon-container
= navbar_icon('mr')
%span %span
Merge Requests Merge Requests
%span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count) %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count)
= nav_link(controller: :snippets) do = nav_link(controller: :snippets) do
= link_to dashboard_snippets_path, title: 'Snippets' do = link_to dashboard_snippets_path, title: 'Snippets' do
.icon-container
= icon('clipboard fw')
%span %span
Snippets Snippets
= nav_link(controller: :help) do = nav_link(controller: :help) do
= link_to help_path, title: 'Help' do = link_to help_path, title: 'Help' do
.icon-container
= icon('question-circle fw')
%span %span
Help Help
= nav_link(html_options: {class: profile_tab_class}) do = nav_link(html_options: {class: profile_tab_class}) do
= link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do = link_to profile_path, title: 'Profile Settings', data: {placement: 'bottom'} do
.icon-container
= icon('user fw')
%span %span
Profile Settings Profile Settings
...@@ -18,9 +18,9 @@ ...@@ -18,9 +18,9 @@
%span %span
Applications Applications
= nav_link(controller: :personal_access_tokens) do = nav_link(controller: :personal_access_tokens) do
= link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do = link_to profile_personal_access_tokens_path, title: 'Access Tokens' do
%span %span
Personal Access Tokens Access Tokens
= nav_link(controller: :emails) do = nav_link(controller: :emails) do
= link_to profile_emails_path, title: 'Emails' do = link_to profile_emails_path, title: 'Emails' do
%span %span
......
...@@ -11,7 +11,7 @@ ...@@ -11,7 +11,7 @@
Add an SSH key Add an SSH key
%p.profile-settings-content %p.profile-settings-content
Before you can add an SSH key you need to Before you can add an SSH key you need to
= link_to "generate it.", help_page_path("ssh", "README") = link_to "generate it.", help_page_path("ssh/README")
= render 'form' = render 'form'
%hr %hr
%h5 %h5
......
...@@ -43,12 +43,12 @@ ...@@ -43,12 +43,12 @@
.form-group .form-group
= f.label :dashboard, class: 'label-light' do = f.label :dashboard, class: 'label-light' do
Default Dashboard Default Dashboard
= link_to('(?)', help_page_path('profile', 'preferences') + '#default-dashboard', target: '_blank') = link_to('(?)', help_page_path('profile/preferences') + '#default-dashboard', target: '_blank')
= f.select :dashboard, dashboard_choices, {}, class: 'form-control' = f.select :dashboard, dashboard_choices, {}, class: 'form-control'
.form-group .form-group
= f.label :project_view, class: 'label-light' do = f.label :project_view, class: 'label-light' do
Project view Project view
= link_to('(?)', help_page_path('profile', 'preferences') + '#default-project-view', target: '_blank') = link_to('(?)', help_page_path('profile/preferences') + '#default-project-view', target: '_blank')
= f.select :project_view, project_view_choices, {}, class: 'form-control' = f.select :project_view, project_view_choices, {}, class: 'form-control'
.help-block .help-block
Choose what content you want to see on a project's home page. Choose what content you want to see on a project's home page.
......
...@@ -14,7 +14,7 @@ ...@@ -14,7 +14,7 @@
- else - else
%p %p
Download the Google Authenticator application from App Store or Google Play Store and scan this code. Download the Google Authenticator application from App Store or Google Play Store and scan this code.
More information is available in the #{link_to('documentation', help_page_path('profile', 'two_factor_authentication'))}. More information is available in the #{link_to('documentation', help_page_path('profile/two_factor_authentication'))}.
.row.append-bottom-10 .row.append-bottom-10
.col-md-3 .col-md-3
= raw @qr_code = raw @qr_code
......
...@@ -10,4 +10,4 @@ ...@@ -10,4 +10,4 @@
as administrator you need to configure as administrator you need to configure
- else - else
ask your GitLab administrator to configure ask your GitLab administrator to configure
== #{link_to 'OAuth integration', help_page_path("integration", "bitbucket")}. == #{link_to 'OAuth integration', help_page_path("integration/bitbucket")}.
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
- unless @repository.gitlab_ci_yml - unless @repository.gitlab_ci_yml
.form-group .form-group
%p Builds need to be configured before you can begin using Continuous Integration. %p Builds need to be configured before you can begin using Continuous Integration.
= link_to 'Get started with Builds', help_page_path('ci/quick_start', 'README'), class: 'btn btn-info' = link_to 'Get started with Builds', help_page_path('ci/quick_start/README'), class: 'btn btn-info'
.form-group .form-group
%p Get recent application code using the following command: %p Get recent application code using the following command:
.radio .radio
......
...@@ -10,4 +10,4 @@ ...@@ -10,4 +10,4 @@
as administrator you need to configure as administrator you need to configure
- else - else
ask your GitLab administrator to configure ask your GitLab administrator to configure
== #{link_to 'OAuth integration', help_page_path("integration", "gitlab")}. == #{link_to 'OAuth integration', help_page_path("integration/gitlab")}.
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