summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
authorJacob Schatz <jschatz1@gmail.com>2016-07-11 17:19:17 -0400
committerJacob Schatz <jschatz1@gmail.com>2016-07-11 17:19:17 -0400
commit0452e0a57e24fdd65f559cc1a8629892714adc7a (patch)
treebf33ca472a3d434770783d80b5d10211383e706c /app
parent7690513495d02d14af8a1a0cb3bf00f243ec0419 (diff)
parentf76596380fb545c54b14c6bfc339a68a1dc162d3 (diff)
downloadgitlab-ce-0452e0a57e24fdd65f559cc1a8629892714adc7a.tar.gz
Merge branch 'master' into faster-diffsfaster-diffs
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/application.js.coffee2
-rw-r--r--app/assets/javascripts/build.coffee (renamed from app/assets/javascripts/ci/build.coffee)6
-rw-r--r--app/assets/javascripts/ci/application.js.coffee12
-rw-r--r--app/assets/javascripts/ci/projects.js.coffee3
-rw-r--r--app/assets/javascripts/dispatcher.js.coffee1
-rw-r--r--app/assets/javascripts/flash.js.coffee30
-rw-r--r--app/assets/javascripts/gl_dropdown.js.coffee4
-rw-r--r--app/assets/javascripts/lib/cropper.js.coffee1
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js.coffee7
-rw-r--r--app/assets/javascripts/merge_request_tabs.js.coffee1
-rw-r--r--app/assets/javascripts/notes.js.coffee8
-rw-r--r--app/assets/javascripts/profile/application.js.coffee2
-rw-r--r--app/assets/javascripts/profile/gl_crop.js.coffee (renamed from app/assets/javascripts/gl_crop.js.coffee)0
-rw-r--r--app/assets/javascripts/profile/profile.js.coffee (renamed from app/assets/javascripts/profile.js.coffee)3
-rw-r--r--app/assets/javascripts/protected_branch_select.js.coffee40
-rw-r--r--app/assets/javascripts/protected_branches.js.coffee3
-rw-r--r--app/assets/javascripts/single_file_diff.js.coffee54
-rw-r--r--app/assets/stylesheets/framework/blank.scss23
-rw-r--r--app/assets/stylesheets/framework/blocks.scss3
-rw-r--r--app/assets/stylesheets/framework/flash.scss22
-rw-r--r--app/assets/stylesheets/framework/lists.scss6
-rw-r--r--app/assets/stylesheets/framework/nav.scss4
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss7
-rw-r--r--app/assets/stylesheets/framework/variables.scss12
-rw-r--r--app/assets/stylesheets/pages/issues.scss4
-rw-r--r--app/controllers/admin/application_settings_controller.rb1
-rw-r--r--app/controllers/concerns/diff_for_path.rb25
-rw-r--r--app/controllers/projects/artifacts_controller.rb5
-rw-r--r--app/controllers/projects/commit_controller.rb49
-rw-r--r--app/controllers/projects/compare_controller.rb46
-rw-r--r--app/controllers/projects/merge_requests_controller.rb134
-rw-r--r--app/controllers/projects/protected_branches_controller.rb26
-rw-r--r--app/controllers/projects_controller.rb4
-rw-r--r--app/helpers/appearances_helper.rb4
-rw-r--r--app/helpers/diff_helper.rb20
-rw-r--r--app/helpers/merge_requests_helper.rb8
-rw-r--r--app/helpers/time_helper.rb7
-rw-r--r--app/helpers/workhorse_helper.rb6
-rw-r--r--app/models/application_setting.rb1
-rw-r--r--app/models/ci/build.rb5
-rw-r--r--app/models/commit_range.rb4
-rw-r--r--app/models/merge_request.rb10
-rw-r--r--app/models/merge_request_diff.rb9
-rw-r--r--app/models/project.rb25
-rw-r--r--app/models/project_services/irker_service.rb10
-rw-r--r--app/models/protected_branch.rb47
-rw-r--r--app/models/repository.rb4
-rw-r--r--app/models/sent_notification.rb13
-rw-r--r--app/models/user.rb2
-rw-r--r--app/services/compare_service.rb2
-rw-r--r--app/services/merge_requests/refresh_service.rb11
-rw-r--r--app/views/admin/abuse_reports/_abuse_report.html.haml4
-rw-r--r--app/views/admin/application_settings/_form.html.haml7
-rw-r--r--app/views/admin/users/_form.html.haml2
-rw-r--r--app/views/explore/snippets/index.html.haml1
-rw-r--r--app/views/layouts/_flash.html.haml2
-rw-r--r--app/views/profiles/_head.html.haml3
-rw-r--r--app/views/profiles/accounts/show.html.haml1
-rw-r--r--app/views/profiles/audit_log.html.haml1
-rw-r--r--app/views/profiles/emails/index.html.haml1
-rw-r--r--app/views/profiles/keys/_form.html.haml2
-rw-r--r--app/views/profiles/keys/show.html.haml1
-rw-r--r--app/views/profiles/notifications/show.html.haml1
-rw-r--r--app/views/profiles/personal_access_tokens/index.html.haml1
-rw-r--r--app/views/profiles/preferences/show.html.haml1
-rw-r--r--app/views/profiles/show.html.haml2
-rw-r--r--app/views/profiles/two_factor_auths/show.html.haml1
-rw-r--r--app/views/projects/builds/show.html.haml2
-rw-r--r--app/views/projects/ci/pipelines/_pipeline.html.haml2
-rw-r--r--app/views/projects/diffs/_content.html.haml29
-rw-r--r--app/views/projects/diffs/_diffs.html.haml2
-rw-r--r--app/views/projects/diffs/_file.html.haml26
-rw-r--r--app/views/projects/diffs/_warning.html.haml3
-rw-r--r--app/views/projects/issues/index.html.haml48
-rw-r--r--app/views/projects/merge_requests/_show.html.haml4
-rw-r--r--app/views/projects/merge_requests/show/_mr_title.html.haml8
-rw-r--r--app/views/projects/merge_requests/widget/_open.html.haml6
-rw-r--r--app/views/projects/notes/_notes_with_form.html.haml2
-rw-r--r--app/views/projects/protected_branches/_branches_list.html.haml33
-rw-r--r--app/views/projects/protected_branches/_dropdown.html.haml17
-rw-r--r--app/views/projects/protected_branches/_matching_branch.html.haml9
-rw-r--r--app/views/projects/protected_branches/_protected_branch.html.haml21
-rw-r--r--app/views/projects/protected_branches/index.html.haml24
-rw-r--r--app/views/projects/protected_branches/show.html.haml25
-rw-r--r--app/views/projects/snippets/_actions.html.haml42
-rw-r--r--app/views/projects/snippets/index.html.haml6
-rw-r--r--app/views/shared/icons/_issues.svg13
-rw-r--r--app/views/shared/icons/_issues.svg.erb4
-rw-r--r--app/views/snippets/_actions.html.haml41
-rw-r--r--app/views/users/show.html.haml5
90 files changed, 774 insertions, 365 deletions
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 20fe5a5cc27..c98763d6271 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -47,14 +47,12 @@
#= require date.format
#= require_directory ./behaviors
#= require_directory ./blob
-#= require_directory ./ci
#= require_directory ./commit
#= require_directory ./extensions
#= require_directory ./lib/utils
#= require_directory ./u2f
#= require_directory .
#= require fuzzaldrin-plus
-#= require cropper
#= require u2f
window.slugify = (text) ->
diff --git a/app/assets/javascripts/ci/build.coffee b/app/assets/javascripts/build.coffee
index 74691b2c1b5..cf203ea43a0 100644
--- a/app/assets/javascripts/ci/build.coffee
+++ b/app/assets/javascripts/build.coffee
@@ -1,9 +1,9 @@
-class @CiBuild
+class @Build
@interval: null
@state: null
constructor: (@page_url, @build_url, @build_status, @state) ->
- clearInterval(CiBuild.interval)
+ clearInterval(Build.interval)
# Init breakpoint checker
@bp = Breakpoints.get()
@@ -40,7 +40,7 @@ class @CiBuild
# Check for new build output if user still watching build page
# 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
@getBuildTrace()
, 4000
diff --git a/app/assets/javascripts/ci/application.js.coffee b/app/assets/javascripts/ci/application.js.coffee
deleted file mode 100644
index ca24c1d759f..00000000000
--- a/app/assets/javascripts/ci/application.js.coffee
+++ /dev/null
@@ -1,12 +0,0 @@
-#= 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)
diff --git a/app/assets/javascripts/ci/projects.js.coffee b/app/assets/javascripts/ci/projects.js.coffee
deleted file mode 100644
index e6406011d11..00000000000
--- a/app/assets/javascripts/ci/projects.js.coffee
+++ /dev/null
@@ -1,3 +0,0 @@
-$(document).on 'click', '.badge-codes-toggle', ->
- $('.badge-codes-block').toggleClass("hide")
- return false
diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee
index a39df421832..74fd77cf7ab 100644
--- a/app/assets/javascripts/dispatcher.js.coffee
+++ b/app/assets/javascripts/dispatcher.js.coffee
@@ -131,7 +131,6 @@ class Dispatcher
when 'dashboard', 'root'
shortcut_handler = new ShortcutsDashboardNavigation()
when 'profiles'
- new Profile()
new NotificationsForm()
new NotificationsDropdown()
when 'projects'
diff --git a/app/assets/javascripts/flash.js.coffee b/app/assets/javascripts/flash.js.coffee
index b76d214790a..5a493041538 100644
--- a/app/assets/javascripts/flash.js.coffee
+++ b/app/assets/javascripts/flash.js.coffee
@@ -1,24 +1,28 @@
class @Flash
- constructor: (message, type = 'alert')->
- @flash = $(".flash-container")
- @flash.html("")
+ hideFlash = -> $(@).fadeOut()
- 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}"
)
- innerDiv.appendTo(".flash-container")
+ flash.on 'click', hideFlash
- textDiv = $("<div/>",
- class: "flash-text",
+ textDiv = $('<div/>',
+ class: 'flash-text',
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')
- @flash.click -> $(@).fadeOut()
- @flash.show()
+ flash.appendTo(@flashContainer)
+ @flashContainer.show()
- pinTo: (selector) ->
- @flash.detach().appendTo(selector)
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index ed9dfcc917e..1c65e833d47 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -56,6 +56,7 @@ class GitLabDropdownFilter
return BLUR_KEYCODES.indexOf(keyCode) >= 0
filter: (search_text) ->
+ @options.onFilter(search_text) if @options.onFilter
data = @options.data()
if data? and not @options.filterByText
@@ -195,6 +196,7 @@ class GitLabDropdown
@filter = new GitLabDropdownFilter @filterInput,
filterInputBlur: @filterInputBlur
filterByText: @options.filterByText
+ onFilter: @options.onFilter
remote: @options.filterRemote
query: @options.data
keys: searchFields
@@ -530,7 +532,7 @@ class GitLabDropdown
if $el.length
e.preventDefault()
e.stopImmediatePropagation()
- $(selector, @dropdown)[0].click()
+ $el.first().trigger('click')
addArrowKeyEvent: ->
ARROW_KEY_CODES = [38, 40]
diff --git a/app/assets/javascripts/lib/cropper.js.coffee b/app/assets/javascripts/lib/cropper.js.coffee
new file mode 100644
index 00000000000..32536d23fe3
--- /dev/null
+++ b/app/assets/javascripts/lib/cropper.js.coffee
@@ -0,0 +1 @@
+#= require cropper
diff --git a/app/assets/javascripts/lib/utils/common_utils.js.coffee b/app/assets/javascripts/lib/utils/common_utils.js.coffee
index e39dcb2daa9..d4dd3dc329a 100644
--- a/app/assets/javascripts/lib/utils/common_utils.js.coffee
+++ b/app/assets/javascripts/lib/utils/common_utils.js.coffee
@@ -5,12 +5,12 @@
w.gl.utils.isInGroupsPage = ->
- return $('body').data('page').split(':')[0] is 'groups'
+ return gl.utils.getPagePath() is 'groups'
w.gl.utils.isInProjectPage = ->
- return $('body').data('page').split(':')[0] is 'projects'
+ return gl.utils.getPagePath() is 'projects'
w.gl.utils.getProjectSlug = ->
@@ -40,6 +40,9 @@
e.stopImmediatePropagation()
return false
+ gl.utils.getPagePath = ->
+ return $('body').data('page').split(':')[0]
+
jQuery.timefor = (time, suffix, expiredLabel) ->
diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee
index 87916823f81..86539e0d725 100644
--- a/app/assets/javascripts/merge_request_tabs.js.coffee
+++ b/app/assets/javascripts/merge_request_tabs.js.coffee
@@ -159,6 +159,7 @@ class @MergeRequestTabs
$('#diffs').html data.html
gl.utils.localTimeAgo($('.js-timeago', 'div#diffs'))
$('#diffs .js-syntax-highlight').syntaxHighlight()
+ $('#diffs .diff-file').singleFileDiff()
@expandViewContainer() if @diffViewType() is 'parallel'
@diffsLoaded = true
@scrollToElement("#diffs")
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index 0b7d8f64456..0ea54faae1a 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -194,8 +194,7 @@ class @Notes
renderNote: (note) ->
unless note.valid
if note.award
- flash = new Flash('You have already awarded this emoji!', 'alert')
- flash.pinTo('.header-content')
+ new Flash('You have already awarded this emoji!', 'alert')
return
if note.award
@@ -325,6 +324,8 @@ class @Notes
form.find("#note_position").remove()
form.find("#note_type").remove()
+ @parentTimeline = form.parents('.timeline')
+
###
General note form setup.
@@ -357,8 +358,7 @@ class @Notes
@renderNote(note)
addNoteError: (xhr, note, status) =>
- flash = new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert')
- flash.pinTo('.md-area')
+ new Flash('Your comment could not be submitted! Please check your network connection and try again.', 'alert', @parentTimeline)
###
Called in response to the new note form being submitted
diff --git a/app/assets/javascripts/profile/application.js.coffee b/app/assets/javascripts/profile/application.js.coffee
new file mode 100644
index 00000000000..91cacfece46
--- /dev/null
+++ b/app/assets/javascripts/profile/application.js.coffee
@@ -0,0 +1,2 @@
+#
+#= require_tree .
diff --git a/app/assets/javascripts/gl_crop.js.coffee b/app/assets/javascripts/profile/gl_crop.js.coffee
index df9bfdfa6cc..df9bfdfa6cc 100644
--- a/app/assets/javascripts/gl_crop.js.coffee
+++ b/app/assets/javascripts/profile/gl_crop.js.coffee
diff --git a/app/assets/javascripts/profile.js.coffee b/app/assets/javascripts/profile/profile.js.coffee
index 1583d1ba6f9..f3b05f2c646 100644
--- a/app/assets/javascripts/profile.js.coffee
+++ b/app/assets/javascripts/profile/profile.js.coffee
@@ -78,3 +78,6 @@ $ ->
if comment && comment.length > 1 && $title.val() == ''
$title.val(comment[1]).change()
+
+ if gl.utils.getPagePath() == 'profiles'
+ new Profile()
diff --git a/app/assets/javascripts/protected_branch_select.js.coffee b/app/assets/javascripts/protected_branch_select.js.coffee
new file mode 100644
index 00000000000..6d45770ace9
--- /dev/null
+++ b/app/assets/javascripts/protected_branch_select.js.coffee
@@ -0,0 +1,40 @@
+class @ProtectedBranchSelect
+ constructor: (currentProject) ->
+ $('.dropdown-footer').hide();
+ @dropdown = $('.js-protected-branch-select').glDropdown(
+ data: @getProtectedBranches
+ filterable: true
+ remote: false
+ search:
+ fields: ['title']
+ selectable: true
+ toggleLabel: (selected) -> if (selected and 'id' of selected) then selected.title else 'Protected Branch'
+ fieldName: 'protected_branch[name]'
+ text: (protected_branch) -> _.escape(protected_branch.title)
+ id: (protected_branch) -> _.escape(protected_branch.id)
+ onFilter: @toggleCreateNewButton
+ clicked: () -> $('.protect-branch-btn').attr('disabled', false)
+ )
+
+ $('.create-new-protected-branch').on 'click', (event) =>
+ # Refresh the dropdown's data, which ends up calling `getProtectedBranches`
+ @dropdown.data('glDropdown').remote.execute()
+ @dropdown.data('glDropdown').selectRowAtIndex(event, 0)
+
+ getProtectedBranches: (term, callback) =>
+ if @selectedBranch
+ callback(gon.open_branches.concat(@selectedBranch))
+ else
+ callback(gon.open_branches)
+
+ toggleCreateNewButton: (branchName) =>
+ @selectedBranch = { title: branchName, id: branchName, text: branchName }
+
+ if branchName is ''
+ $('.protected-branch-select-footer-list').addClass('hidden')
+ $('.dropdown-footer').hide();
+ else
+ $('.create-new-protected-branch').text("Create Protected Branch: #{branchName}")
+ $('.protected-branch-select-footer-list').removeClass('hidden')
+ $('.dropdown-footer').show();
+
diff --git a/app/assets/javascripts/protected_branches.js.coffee b/app/assets/javascripts/protected_branches.js.coffee
index 5753c9d4e72..79c2306e4d2 100644
--- a/app/assets/javascripts/protected_branches.js.coffee
+++ b/app/assets/javascripts/protected_branches.js.coffee
@@ -11,7 +11,8 @@ $ ->
dataType: "json"
data:
id: id
- developers_can_push: checked
+ protected_branch:
+ developers_can_push: checked
success: ->
row = $(e.target)
diff --git a/app/assets/javascripts/single_file_diff.js.coffee b/app/assets/javascripts/single_file_diff.js.coffee
new file mode 100644
index 00000000000..f3e225c3728
--- /dev/null
+++ b/app/assets/javascripts/single_file_diff.js.coffee
@@ -0,0 +1,54 @@
+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
diff --git a/app/assets/stylesheets/framework/blank.scss b/app/assets/stylesheets/framework/blank.scss
index 40b5171a8c6..d28cda6d62d 100644
--- a/app/assets/stylesheets/framework/blank.scss
+++ b/app/assets/stylesheets/framework/blank.scss
@@ -1,3 +1,12 @@
+.blank-state-welcome {
+ text-align: center;
+ border-bottom: 1px solid $border-color;
+
+ .blank-state-text {
+ margin-bottom: 0;
+ }
+}
+
.blank-state {
padding-top: 20px;
padding-bottom: 20px;
@@ -6,7 +15,15 @@
.blank-state-no-icon {
padding-top: 40px;
- padding-bottom: 40px;
+ padding-bottom: 40px;
+}
+
+.blank-state-icon {
+ padding-bottom: 20px;
+
+ path {
+ fill: $gray-darkest;
+ }
}
.blank-state-title {
@@ -21,3 +38,7 @@
margin-bottom: $gl-padding;
font-size: 15px;
}
+
+.blank-state-welcome-title {
+ font-size: 24px;
+}
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index 41e77a4ac68..24b1ebab4b0 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -16,6 +16,9 @@
font-weight: normal;
font-size: 16px;
line-height: 36px;
+ &.diff-collapsed {
+ cursor: pointer;
+ }
}
.row-content-block {
diff --git a/app/assets/stylesheets/framework/flash.scss b/app/assets/stylesheets/framework/flash.scss
index a951a2b97fe..0c21d0240b3 100644
--- a/app/assets/stylesheets/framework/flash.scss
+++ b/app/assets/stylesheets/framework/flash.scss
@@ -1,8 +1,8 @@
.flash-container {
cursor: pointer;
margin: 0;
+ margin-bottom: $gl-padding;
font-size: 14px;
- width: 100%;
z-index: 100;
.flash-notice {
@@ -18,9 +18,27 @@
}
.flash-notice, .flash-alert {
- .container-fluid.flash-text {
+ border-radius: $border-radius-default;
+
+ .container-fluid.container-limited.flash-text {
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;
+ }
+ }
}
diff --git a/app/assets/stylesheets/framework/lists.scss b/app/assets/stylesheets/framework/lists.scss
index aed0b44d91b..2c40ec430ca 100644
--- a/app/assets/stylesheets/framework/lists.scss
+++ b/app/assets/stylesheets/framework/lists.scss
@@ -175,6 +175,12 @@ ul.content-list {
.panel > .content-list > li {
padding: $gl-padding-top $gl-padding;
+
+ &.commit {
+ @media (min-width: $screen-sm-min) {
+ padding-left: 46px + $gl-padding;
+ }
+ }
}
ul.controls {
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index 02ea98e9d94..364952d3b4a 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -77,10 +77,10 @@
&.sub-nav {
text-align: center;
- background-color: $background-color;
+ background-color: $dark-background-color;
.container-fluid {
- background-color: $background-color;
+ background-color: $dark-background-color;
margin-bottom: 0;
}
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index 188823054fd..1a2220f3b40 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -3,6 +3,12 @@
padding-bottom: 25px;
transition: padding $sidebar-transition-duration;
+ &.page-sidebar-pinned {
+ .sidebar-wrapper {
+ @include box-shadow(none);
+ }
+ }
+
.sidebar-wrapper {
position: fixed;
top: 0;
@@ -11,6 +17,7 @@
height: 100%;
overflow: hidden;
transition: width $sidebar-transition-duration;
+ @include box-shadow(2px 0 16px 0 #bbb);
}
}
diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss
index 211a9af2348..4337fab5d87 100644
--- a/app/assets/stylesheets/framework/variables.scss
+++ b/app/assets/stylesheets/framework/variables.scss
@@ -12,10 +12,11 @@ $sidebar-breakpoint: 1024px;
/*
* UI elements
*/
-$border-color: #e5e5e5;
-$focus-border-color: #3aabf0;
-$table-border-color: #f0f0f0;
-$background-color: #fafafa;
+$border-color: #e5e5e5;
+$focus-border-color: #3aabf0;
+$table-border-color: #f0f0f0;
+$background-color: #fafafa;
+$dark-background-color: #f7f7f7;
/*
* Text
@@ -153,9 +154,6 @@ $warning-message-bg: #fbf2d9;
$warning-message-color: #9e8e60;
$warning-message-border: #f0e2bb;
-/* header */
-$light-grey-header: #faf9f9;
-
/* tanuki logo colors */
$tanuki-red: #e24329;
$tanuki-orange: #fc6d26;
diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss
index 4e35ca329e4..0e4d8c140aa 100644
--- a/app/assets/stylesheets/pages/issues.scss
+++ b/app/assets/stylesheets/pages/issues.scss
@@ -63,8 +63,8 @@ form.edit-issue {
.merge-request,
.issue {
&.today {
- background: #efe;
- border-color: #cec;
+ background: #f8feef;
+ border-color: #e1e8d5;
}
&.closed {
diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb
index cbdf2859898..23ba83aba0e 100644
--- a/app/controllers/admin/application_settings_controller.rb
+++ b/app/controllers/admin/application_settings_controller.rb
@@ -87,6 +87,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController
:version_check_enabled,
:admin_notification_email,
:user_oauth_applications,
+ :user_default_external,
:shared_runners_enabled,
:shared_runners_text,
:max_artifacts_size,
diff --git a/app/controllers/concerns/diff_for_path.rb b/app/controllers/concerns/diff_for_path.rb
new file mode 100644
index 00000000000..e09b8789eb2
--- /dev/null
+++ b/app/controllers/concerns/diff_for_path.rb
@@ -0,0 +1,25 @@
+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
diff --git a/app/controllers/projects/artifacts_controller.rb b/app/controllers/projects/artifacts_controller.rb
index f11c8321464..7241949393b 100644
--- a/app/controllers/projects/artifacts_controller.rb
+++ b/app/controllers/projects/artifacts_controller.rb
@@ -23,10 +23,9 @@ class Projects::ArtifactsController < Projects::ApplicationController
entry = build.artifacts_metadata_entry(params[:path])
if entry.exists?
- render json: { archive: build.artifacts_file.path,
- entry: Base64.encode64(entry.path) }
+ send_artifacts_entry(build, entry)
else
- render json: {}, status: 404
+ render_404
end
end
diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb
index 37d6521026c..727e84b40a1 100644
--- a/app/controllers/projects/commit_controller.rb
+++ b/app/controllers/projects/commit_controller.rb
@@ -3,6 +3,7 @@
# Not to be confused with CommitsController, plural.
class Projects::CommitController < Projects::ApplicationController
include CreatesCommit
+ include DiffForPath
include DiffHelper
# Authorize
@@ -11,29 +12,14 @@ class Projects::CommitController < Projects::ApplicationController
before_action :authorize_update_build!, only: [:cancel_builds, :retry_builds]
before_action :authorize_read_commit_status!, only: [:builds]
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]
def show
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|
format.html
format.diff { render text: @commit.to_diff }
@@ -41,6 +27,10 @@ class Projects::CommitController < Projects::ApplicationController
end
end
+ def diff_for_path
+ render_diff_for_path(@diffs, @commit.diff_refs, @project)
+ end
+
def builds
end
@@ -114,7 +104,7 @@ class Projects::CommitController < Projects::ApplicationController
@ci_builds ||= Ci::Build.where(pipeline: pipelines)
end
- def define_show_vars
+ def define_commit_vars
return git_not_found! unless commit
opts = diff_options
@@ -122,7 +112,28 @@ class Projects::CommitController < Projects::ApplicationController
@diffs = commit.diffs(opts)
@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)
@builds = Ci::Build.where(pipeline: pipelines)
end
diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb
index d240b9fe989..5f3ee71444d 100644
--- a/app/controllers/projects/compare_controller.rb
+++ b/app/controllers/projects/compare_controller.rb
@@ -1,29 +1,51 @@
require 'addressable/uri'
class Projects::CompareController < Projects::ApplicationController
+ include DiffForPath
include DiffHelper
# Authorize
before_action :require_non_empty_project
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]
def index
end
def show
- compare = CompareService.new.
- execute(@project, @head_ref, @project, @start_ref, diff_options)
+ end
+
+ 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
- @commits = Commit.decorate(compare.commits, @project)
+ def define_ref_vars
+ @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)
@commit = @project.commit(@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(
base_sha: @base_commit.try(:sha),
start_sha: @start_commit.try(:sha),
@@ -35,18 +57,6 @@ class Projects::CompareController < Projects::ApplicationController
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
@merge_request ||= @project.merge_requests.opened.
find_by(source_project: @project, source_branch: @head_ref, target_branch: @start_ref)
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index df1943dd9bb..941d68cda17 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -1,5 +1,6 @@
class Projects::MergeRequestsController < Projects::ApplicationController
include ToggleSubscriptionAction
+ include DiffForPath
include DiffHelper
include IssuableActions
include ToggleAwardEmoji
@@ -9,10 +10,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController
:edit, :update, :show, :diffs, :commits, :builds, :merge, :merge_check,
:ci_status, :toggle_subscription, :cancel_merge_when_build_succeeds, :remove_wip
]
- before_action :closes_issues, only: [:edit, :update, :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_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]
# Allow read any merge_request
@@ -53,12 +55,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def show
- @note_counts = Note.where(commit_id: @merge_request.commits.map(&:id)).
- group(:commit_id).count
-
respond_to do |format|
format.html
-
+
format.json do
render json: @merge_request
end
@@ -80,25 +79,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def diffs
apply_diff_view_cookie!
- @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
- )
+ @merge_request_diff = @merge_request.merge_request_diff
respond_to do |format|
format.html
@@ -106,10 +87,37 @@ class Projects::MergeRequestsController < Projects::ApplicationController
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
respond_to do |format|
format.html { render 'show' }
- format.json { render json: { html: view_to_html_string('projects/merge_requests/show/_commits') } }
+ format.json do
+ # Get commits from repository
+ # or from cache if already merged
+ @commits = @merge_request.commits
+ @note_counts = Note.where(commit_id: @commits.map(&:id)).
+ group(:commit_id).count
+
+ render json: { html: view_to_html_string('projects/merge_requests/show/_commits') }
+ end
end
end
@@ -121,8 +129,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def new
- params[:merge_request] ||= ActionController::Parameters.new(source_project: @project)
- @merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params).execute
+ build_merge_request
@noteable = @merge_request
@target_branches = if @merge_request.target_project
@@ -308,10 +315,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController
alias_method :issuable, :merge_request
alias_method :awardable, :merge_request
- def closes_issues
- @closes_issues ||= @merge_request.closes_issues
- end
-
def authorize_update_merge_request!
return render_404 unless can?(current_user, :update_merge_request, @merge_request)
end
@@ -340,14 +343,33 @@ class Projects::MergeRequestsController < Projects::ApplicationController
end
def define_show_vars
+ @noteable = @merge_request
+ @commits_count = @merge_request.commits.count
+
+ @pipeline = @merge_request.pipeline
+ @statuses = @pipeline.statuses if @pipeline
+
+ if @merge_request.locked_long_ago?
+ @merge_request.unlock_mr
+ @merge_request.close
+ end
+
+ if request.format == :html || action_name == 'show'
+ define_show_html_vars
+ end
+ end
+
+ # Discussion tab data is only required on html requests
+ def define_show_html_vars
# Build a note object for comment form
- @note = @project.notes.new(noteable: @merge_request)
+ @note = @project.notes.new(noteable: @noteable)
- @discussions = @merge_request.mr_and_commit_notes.
+ @discussions = @noteable.mr_and_commit_notes.
inc_author_project_award_emoji.
fresh.
discussions
+ # This is not executed lazily
@notes = Banzai::NoteRenderer.render(
@discussions.flatten,
@project,
@@ -356,28 +378,35 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@project_wiki,
@ref
)
-
- @noteable = @merge_request
-
- # Get commits from repository
- # or from cache if already merged
- @commits = @merge_request.commits
-
- @merge_request_diff = @merge_request.merge_request_diff
-
- @pipeline = @merge_request.pipeline
- @statuses = @pipeline.statuses if @pipeline
-
- if @merge_request.locked_long_ago?
- @merge_request.unlock_mr
- @merge_request.close
- end
end
def define_widget_vars
@pipeline = @merge_request.pipeline
@pipelines = [@pipeline].compact
- closes_issues
+ 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
@@ -408,4 +437,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
params[:merge_when_build_succeeds].present? &&
@merge_request.pipeline && @merge_request.pipeline.active?
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
diff --git a/app/controllers/projects/protected_branches_controller.rb b/app/controllers/projects/protected_branches_controller.rb
index efa7bf14d0f..80dad758afa 100644
--- a/app/controllers/projects/protected_branches_controller.rb
+++ b/app/controllers/projects/protected_branches_controller.rb
@@ -2,12 +2,14 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
# Authorize
before_action :require_non_empty_project
before_action :authorize_admin_project!
+ before_action :load_protected_branch, only: [:show, :update, :destroy]
layout "project_settings"
def index
- @branches = @project.protected_branches.to_a
+ @protected_branches = @project.protected_branches.order(:name).page(params[:page])
@protected_branch = @project.protected_branches.new
+ gon.push({ open_branches: @project.open_branches.map { |br| { text: br.name, id: br.name, title: br.name } } })
end
def create
@@ -16,26 +18,24 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
@project)
end
- def update
- protected_branch = @project.protected_branches.find(params[:id])
-
- if protected_branch &&
- protected_branch.update_attributes(
- developers_can_push: params[:developers_can_push]
- )
+ def show
+ @matching_branches = @protected_branch.matching(@project.repository.branches)
+ end
+ def update
+ if @protected_branch && @protected_branch.update_attributes(protected_branch_params)
respond_to do |format|
- format.json { render json: protected_branch, status: :ok }
+ format.json { render json: @protected_branch, status: :ok }
end
else
respond_to do |format|
- format.json { render json: protected_branch.errors, status: :unprocessable_entity }
+ format.json { render json: @protected_branch.errors, status: :unprocessable_entity }
end
end
end
def destroy
- @project.protected_branches.find(params[:id]).destroy
+ @protected_branch.destroy
respond_to do |format|
format.html { redirect_to namespace_project_protected_branches_path }
@@ -45,6 +45,10 @@ class Projects::ProtectedBranchesController < Projects::ApplicationController
private
+ def load_protected_branch
+ @protected_branch = @project.protected_branches.find(params[:id])
+ end
+
def protected_branch_params
params.require(:protected_branch).permit(:name, :developers_can_push)
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 12e0d5a8413..1803aa8eab4 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -53,11 +53,11 @@ class ProjectsController < Projects::ApplicationController
notice: "Project '#{@project.name}' was successfully updated."
)
end
- format.js
else
format.html { render 'edit' }
- format.js
end
+
+ format.js
end
end
diff --git a/app/helpers/appearances_helper.rb b/app/helpers/appearances_helper.rb
index f240584ccbf..950f323e383 100644
--- a/app/helpers/appearances_helper.rb
+++ b/app/helpers/appearances_helper.rb
@@ -31,7 +31,7 @@ module AppearancesHelper
end
end
- def navbar_icon(icon_name)
- render "shared/icons/#{icon_name}.svg"
+ def navbar_icon(icon_name, size: 16)
+ render "shared/icons/#{icon_name}.svg", size: size
end
end
diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb
index b61266465e2..adab901700c 100644
--- a/app/helpers/diff_helper.rb
+++ b/app/helpers/diff_helper.rb
@@ -8,6 +8,10 @@ module DiffHelper
[marked_old_line, marked_new_line]
end
+ def expand_all_diffs?
+ @expand_all_diffs || params[:expand_all_diffs].present?
+ end
+
def diff_view
diff_views = %w(inline parallel)
@@ -18,16 +22,14 @@ module DiffHelper
end
end
- def diff_hard_limit_enabled?
- params[:force_show_diff].present?
- end
-
def diff_options
- options = { ignore_whitespace_change: hide_whitespace? }
- if diff_hard_limit_enabled?
- options.merge!(Commit.max_diff_options)
+ default_options = Commit.max_diff_options
+
+ if action_name == 'diff_for_path'
+ default_options[:paths] = params.values_at(:old_path, :new_path)
end
- options
+
+ default_options.merge(ignore_whitespace_change: hide_whitespace?)
end
def safe_diff_files(diffs, diff_refs: nil, repository: nil)
@@ -90,7 +92,7 @@ module DiffHelper
def commit_for_diff(diff_file)
return diff_file.content_commit if diff_file.content_commit
-
+
if diff_file.deleted_file
@base_commit || @commit.parent || @commit
else
diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb
index c7dedfe9254..db6e731c744 100644
--- a/app/helpers/merge_requests_helper.rb
+++ b/app/helpers/merge_requests_helper.rb
@@ -55,6 +55,10 @@ module MergeRequestsHelper
end.sort.to_sentence
end
+ def mr_closes_issues
+ @mr_closes_issues ||= @merge_request.closes_issues
+ end
+
def mr_change_branches_path(merge_request)
new_namespace_project_merge_request_path(
@project.namespace, @project,
@@ -92,4 +96,8 @@ module MergeRequestsHelper
["#{source_path}:#{source_branch}", "#{target_path}:#{target_branch}"]
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
diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb
index b04b0a5114c..8cb82c2d5cc 100644
--- a/app/helpers/time_helper.rb
+++ b/app/helpers/time_helper.rb
@@ -23,4 +23,11 @@ module TimeHelper
def date_from_to(from, to)
"#{from.to_s(:short)} - #{to.to_s(:short)}"
end
+
+ def duration_in_numbers(finished_at, started_at)
+ diff_in_seconds = finished_at.to_i - started_at.to_i
+ time_format = diff_in_seconds < 1.hour ? "%M:%S" : "%H:%M:%S"
+
+ Time.at(diff_in_seconds).utc.strftime(time_format)
+ end
end
diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb
index 65598ad9ed3..d887cdadc34 100644
--- a/app/helpers/workhorse_helper.rb
+++ b/app/helpers/workhorse_helper.rb
@@ -28,4 +28,10 @@ module WorkhorseHelper
headers.store(*Gitlab::Workhorse.send_git_archive(repository, ref: ref, format: format))
head :ok
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
diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb
index 7bf618d60b9..c6f77cc055f 100644
--- a/app/models/application_setting.rb
+++ b/app/models/application_setting.rb
@@ -142,6 +142,7 @@ class ApplicationSetting < ActiveRecord::Base
send_user_confirmation_email: false,
container_registry_token_expire_delay: 5,
repository_storage: 'default',
+ user_default_external: false,
)
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index 5c973749975..e189dbac285 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -13,6 +13,7 @@ module Ci
scope :ignore_failures, ->() { where(allow_failure: false) }
scope :with_artifacts, ->() { where.not(artifacts_file: nil) }
scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) }
+ scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) }
mount_uploader :artifacts_file, ArtifactUploader
mount_uploader :artifacts_metadata, ArtifactUploader
@@ -25,10 +26,6 @@ module Ci
after_create :execute_hooks
class << self
- def last_month
- where('created_at > ?', Date.today - 1.month)
- end
-
def first_pending
pending.unstarted.order('created_at ASC').first
end
diff --git a/app/models/commit_range.rb b/app/models/commit_range.rb
index 4066958f67c..630ee9601e0 100644
--- a/app/models/commit_range.rb
+++ b/app/models/commit_range.rb
@@ -23,7 +23,7 @@ class CommitRange
attr_reader :commit_from, :notation, :commit_to
attr_reader :ref_from, :ref_to
- # Optional Project model
+ # The Project model
attr_accessor :project
# The beginning and ending refs can be named or SHAs, and
@@ -56,7 +56,7 @@ class CommitRange
# Initialize a CommitRange
#
# range_string - The String commit range.
- # project - An optional Project model.
+ # project - The Project model.
#
# Raises ArgumentError if `range_string` does not match `PATTERN`.
def initialize(range_string, project)
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 083e93f1ee7..157901378d3 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -19,7 +19,7 @@ class MergeRequest < ActiveRecord::Base
after_create :create_merge_request_diff, unless: :importing?
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
# It allows us to close or modify broken merge requests
@@ -164,6 +164,10 @@ class MergeRequest < ActiveRecord::Base
merge_request_diff ? merge_request_diff.first_commit : compare_commits.first
end
+ def diffs(*args)
+ merge_request_diff ? merge_request_diff.diffs(*args) : compare.diffs(*args)
+ end
+
def diff_size
merge_request_diff.size
end
@@ -318,11 +322,11 @@ class MergeRequest < ActiveRecord::Base
end
def merge_event
- self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last
+ @merge_event ||= target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::MERGED).last
end
def closed_event
- self.target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last
+ @closed_event ||= target_project.events.where(target_id: self.id, target_type: "MergeRequest", action: Event::CLOSED).last
end
WIP_REGEX = /\A\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i.freeze
diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb
index ba235750aeb..feaba925bad 100644
--- a/app/models/merge_request_diff.rb
+++ b/app/models/merge_request_diff.rb
@@ -46,7 +46,8 @@ class MergeRequestDiff < ActiveRecord::Base
compare.diffs(options)
end
else
- @diffs ||= load_diffs(st_diffs, options)
+ @diffs ||= {}
+ @diffs[options] ||= load_diffs(st_diffs, options)
end
end
@@ -144,6 +145,12 @@ class MergeRequestDiff < ActiveRecord::Base
def load_diffs(raw, options)
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)
else
Gitlab::Git::DiffCollection.new([])
diff --git a/app/models/project.rb b/app/models/project.rb
index 029026a4e56..a66b750cd48 100644
--- a/app/models/project.rb
+++ b/app/models/project.rb
@@ -425,8 +425,8 @@ class Project < ActiveRecord::Base
container_registry_repository.tags.any?
end
- def commit(id = 'HEAD')
- repository.commit(id)
+ def commit(ref = 'HEAD')
+ repository.commit(ref)
end
def merge_base_commit(first_commit_id, second_commit_id)
@@ -802,18 +802,12 @@ class Project < ActiveRecord::Base
@repo_exists = false
end
+ # Branches that are not _exactly_ matched by a protected branch.
def open_branches
- # We're using a Set here as checking values in a large Set is faster than
- # checking values in a large Array.
- protected_set = Set.new(protected_branch_names)
-
- repository.branches.reject do |branch|
- protected_set.include?(branch.name)
- end
- end
-
- def protected_branch_names
- @protected_branch_names ||= protected_branches.pluck(:name)
+ exact_protected_branch_names = protected_branches.reject(&:wildcard?).map(&:name)
+ branch_names = repository.branches.map(&:name)
+ non_open_branch_names = Set.new(exact_protected_branch_names).intersection(Set.new(branch_names))
+ repository.branches.reject { |branch| non_open_branch_names.include? branch.name }
end
def root_ref?(branch)
@@ -830,11 +824,12 @@ class Project < ActiveRecord::Base
# Check if current branch name is marked as protected in the system
def protected_branch?(branch_name)
- protected_branch_names.include?(branch_name)
+ @protected_branches ||= self.protected_branches.to_a
+ ProtectedBranch.matching(branch_name, protected_branches: @protected_branches).present?
end
def developers_can_push_to_protected_branch?(branch_name)
- protected_branches.any? { |pb| pb.name == branch_name && pb.developers_can_push }
+ protected_branches.matching(branch_name).any?(&:developers_can_push)
end
def forked?
diff --git a/app/models/project_services/irker_service.rb b/app/models/project_services/irker_service.rb
index 58cb720c3c1..ce7d1c5d5b1 100644
--- a/app/models/project_services/irker_service.rb
+++ b/app/models/project_services/irker_service.rb
@@ -112,15 +112,7 @@ class IrkerService < Service
# Authorize both irc://domain.com/#chan and irc://domain.com/chan
if uri.is_a?(URI) && uri.scheme[/^ircs?\z/] && !uri.path.nil?
- # Do not authorize irc://domain.com/
- if uri.fragment.nil? && uri.path.length > 1
- uri.to_s
- else
- # Authorize irc://domain.com/smthg#chan
- # The irker daemon will deal with it by concatenating smthg and
- # chan, thus sending messages on #smthgchan
- uri.to_s
- end
+ uri.to_s
end
end
end
diff --git a/app/models/protected_branch.rb b/app/models/protected_branch.rb
index 33cf046fa75..b7011d7afdf 100644
--- a/app/models/protected_branch.rb
+++ b/app/models/protected_branch.rb
@@ -8,4 +8,51 @@ class ProtectedBranch < ActiveRecord::Base
def commit
project.commit(self.name)
end
+
+ # Returns all protected branches that match the given branch name.
+ # This realizes all records from the scope built up so far, and does
+ # _not_ return a relation.
+ #
+ # This method optionally takes in a list of `protected_branches` to search
+ # through, to avoid calling out to the database.
+ def self.matching(branch_name, protected_branches: nil)
+ (protected_branches || all).select { |protected_branch| protected_branch.matches?(branch_name) }
+ end
+
+ # Returns all branches (among the given list of branches [`Gitlab::Git::Branch`])
+ # that match the current protected branch.
+ def matching(branches)
+ branches.select { |branch| self.matches?(branch.name) }
+ end
+
+ # Checks if the protected branch matches the given branch name.
+ def matches?(branch_name)
+ return false if self.name.blank?
+
+ exact_match?(branch_name) || wildcard_match?(branch_name)
+ end
+
+ # Checks if this protected branch contains a wildcard
+ def wildcard?
+ self.name && self.name.include?('*')
+ end
+
+ protected
+
+ def exact_match?(branch_name)
+ self.name == branch_name
+ end
+
+ def wildcard_match?(branch_name)
+ wildcard_regex === branch_name
+ end
+
+ def wildcard_regex
+ @wildcard_regex ||= begin
+ name = self.name.gsub('*', 'STAR_DONT_ESCAPE')
+ quoted_name = Regexp.quote(name)
+ regex_string = quoted_name.gsub('STAR_DONT_ESCAPE', '.*?')
+ /\A#{regex_string}\z/
+ end
+ end
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index ba66bc47c29..5b670cb4b8f 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -78,9 +78,9 @@ class Repository
end
end
- def commit(id = 'HEAD')
+ def commit(ref = 'HEAD')
return nil unless exists?
- commit = Gitlab::Git::Commit.find(raw_repository, id)
+ commit = Gitlab::Git::Commit.find(raw_repository, ref)
commit = ::Commit.new(commit, @project) if commit
commit
rescue Rugged::OdbError
diff --git a/app/models/sent_notification.rb b/app/models/sent_notification.rb
index 016172c6d7e..f4bcb49b34d 100644
--- a/app/models/sent_notification.rb
+++ b/app/models/sent_notification.rb
@@ -72,6 +72,19 @@ class SentNotification < ActiveRecord::Base
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
self.reply_key
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 695a47ba6eb..79c670cb35a 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -15,7 +15,7 @@ class User < ActiveRecord::Base
add_authentication_token_field :authentication_token
default_value_for :admin, false
- default_value_for :external, false
+ default_value_for(:external) { current_application_settings.user_default_external }
default_value_for :can_create_group, gitlab_config.default_can_create_group
default_value_for :can_create_team, false
default_value_for :hide_no_ssh_key, false
diff --git a/app/services/compare_service.rb b/app/services/compare_service.rb
index e2bccbdbcc3..149822aa647 100644
--- a/app/services/compare_service.rb
+++ b/app/services/compare_service.rb
@@ -3,7 +3,7 @@ require 'securerandom'
# Compare 2 branches for one repo or between repositories
# and return Gitlab::Git::Compare object that responds to commits and diffs
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)
return unless source_commit
diff --git a/app/services/merge_requests/refresh_service.rb b/app/services/merge_requests/refresh_service.rb
index 21490ac77ea..b11ecd97a57 100644
--- a/app/services/merge_requests/refresh_service.rb
+++ b/app/services/merge_requests/refresh_service.rb
@@ -61,19 +61,14 @@ module MergeRequests
merge_requests.each do |merge_request|
if merge_request.source_branch == @branch_name || force_push?
merge_request.reload_diff
- merge_request.mark_as_unchecked
else
mr_commit_ids = merge_request.commits.map(&:id)
push_commit_ids = @commits.map(&:id)
matches = mr_commit_ids & push_commit_ids
-
- if matches.any?
- merge_request.reload_diff
- merge_request.mark_as_unchecked
- else
- merge_request.mark_as_unchecked
- end
+ merge_request.reload_diff if matches.any?
end
+
+ merge_request.mark_as_unchecked
end
end
diff --git a/app/views/admin/abuse_reports/_abuse_report.html.haml b/app/views/admin/abuse_reports/_abuse_report.html.haml
index 862b86d9d4a..dd2e7ebd030 100644
--- a/app/views/admin/abuse_reports/_abuse_report.html.haml
+++ b/app/views/admin/abuse_reports/_abuse_report.html.haml
@@ -3,14 +3,14 @@
%tr
%td
- if user
- = link_to user.name, [:admin, user]
+ = link_to user.name, user
.light.small
Joined #{time_ago_with_tooltip(user.created_at)}
- else
(removed)
%td
- if reporter
- = link_to reporter.name, [:admin, reporter]
+ = link_to reporter.name, reporter
- else
(removed)
.light.small
diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml
index eb325576e4f..8de28528cda 100644
--- a/app/views/admin/application_settings/_form.html.haml
+++ b/app/views/admin/application_settings/_form.html.haml
@@ -100,6 +100,13 @@
= f.label :user_oauth_applications do
= f.check_box :user_oauth_applications
Allow users to register any application to use GitLab as an OAuth provider
+ .form-group
+ = f.label :user_default_external, 'New users set to external', class: 'control-label col-sm-2'
+ .col-sm-10
+ .checkbox
+ = f.label :user_default_external do
+ = f.check_box :user_default_external
+ Newly registered users will by default be external
%fieldset
%legend Sign-in Restrictions
diff --git a/app/views/admin/users/_form.html.haml b/app/views/admin/users/_form.html.haml
index fe0b9d3a491..3145212728f 100644
--- a/app/views/admin/users/_form.html.haml
+++ b/app/views/admin/users/_form.html.haml
@@ -44,7 +44,7 @@
%legend Access
.form-group
= f.label :projects_limit, class: 'control-label'
- .col-sm-10= f.number_field :projects_limit, class: 'form-control'
+ .col-sm-10= f.number_field :projects_limit, min: 0, class: 'form-control'
.form-group
= f.label :can_create_group, class: 'control-label'
diff --git a/app/views/explore/snippets/index.html.haml b/app/views/explore/snippets/index.html.haml
index 9b838b9f3b7..6306fe6d0bf 100644
--- a/app/views/explore/snippets/index.html.haml
+++ b/app/views/explore/snippets/index.html.haml
@@ -10,7 +10,6 @@
- if current_user
.pull-right
= link_to new_snippet_path, class: "btn btn-new", title: "New Snippet" do
- = icon('plus')
New Snippet
.oneline
diff --git a/app/views/layouts/_flash.html.haml b/app/views/layouts/_flash.html.haml
index cc8ea066cb9..3612f1ce5c6 100644
--- a/app/views/layouts/_flash.html.haml
+++ b/app/views/layouts/_flash.html.haml
@@ -1,4 +1,4 @@
-.flash-container
+.flash-container.flash-container-page
- if alert
.flash-alert
= alert
diff --git a/app/views/profiles/_head.html.haml b/app/views/profiles/_head.html.haml
new file mode 100644
index 00000000000..003884a5bd9
--- /dev/null
+++ b/app/views/profiles/_head.html.haml
@@ -0,0 +1,3 @@
+- content_for :page_specific_javascripts do
+ = page_specific_javascript_tag('lib/cropper.js')
+ = page_specific_javascript_tag('profile/application.js')
diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml
index 8efe486e01b..57d16d29158 100644
--- a/app/views/profiles/accounts/show.html.haml
+++ b/app/views/profiles/accounts/show.html.haml
@@ -1,4 +1,5 @@
- page_title "Account"
+= render 'profiles/head'
- if current_user.ldap_user?
.alert.alert-info
diff --git a/app/views/profiles/audit_log.html.haml b/app/views/profiles/audit_log.html.haml
index 9c404b6935f..9fe86e6b291 100644
--- a/app/views/profiles/audit_log.html.haml
+++ b/app/views/profiles/audit_log.html.haml
@@ -1,4 +1,5 @@
- page_title "Audit Log"
+= render 'profiles/head'
.row.prepend-top-default
.col-lg-3.profile-settings-sidebar
diff --git a/app/views/profiles/emails/index.html.haml b/app/views/profiles/emails/index.html.haml
index 6f7fefdb46d..dc499be885b 100644
--- a/app/views/profiles/emails/index.html.haml
+++ b/app/views/profiles/emails/index.html.haml
@@ -1,4 +1,5 @@
- page_title "Emails"
+= render 'profiles/head'
.row.prepend-top-default
.col-lg-3.profile-settings-sidebar
diff --git a/app/views/profiles/keys/_form.html.haml b/app/views/profiles/keys/_form.html.haml
index b3ed59a1a4a..6ea358d9f63 100644
--- a/app/views/profiles/keys/_form.html.haml
+++ b/app/views/profiles/keys/_form.html.haml
@@ -4,7 +4,7 @@
.form-group
= f.label :key, class: 'label-light'
- = f.text_area :key, class: "form-control", rows: 8, required: true
+ = f.text_area :key, class: "form-control", rows: 8, required: true, placeholder: "Don't paste the private part of the SSH key. Paste the public part, which is usually contained in the file '~/.ssh/id_rsa.pub' and begins with 'ssh-rsa'."
.form-group
= f.label :title, class: 'label-light'
= f.text_field :title, class: "form-control", required: true
diff --git a/app/views/profiles/keys/show.html.haml b/app/views/profiles/keys/show.html.haml
index 89f6f01581a..6283ceebf10 100644
--- a/app/views/profiles/keys/show.html.haml
+++ b/app/views/profiles/keys/show.html.haml
@@ -1,2 +1,3 @@
- page_title @key.title, "SSH Keys"
+= render 'profiles/head'
= render "key_details"
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index f77738f97f5..844fce59704 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -1,4 +1,5 @@
- page_title "Notifications"
+= render 'profiles/head'
%div
- if @user.errors.any?
diff --git a/app/views/profiles/personal_access_tokens/index.html.haml b/app/views/profiles/personal_access_tokens/index.html.haml
index 1b45548bd02..71ac367830d 100644
--- a/app/views/profiles/personal_access_tokens/index.html.haml
+++ b/app/views/profiles/personal_access_tokens/index.html.haml
@@ -1,4 +1,5 @@
- page_title "Personal Access Tokens"
+= render 'profiles/head'
.row.prepend-top-default
.col-lg-3.profile-settings-sidebar
diff --git a/app/views/profiles/preferences/show.html.haml b/app/views/profiles/preferences/show.html.haml
index 1b1b16d656f..b4d35dc9a3e 100644
--- a/app/views/profiles/preferences/show.html.haml
+++ b/app/views/profiles/preferences/show.html.haml
@@ -1,4 +1,5 @@
- page_title 'Preferences'
+= render 'profiles/head'
= form_for @user, url: profile_preferences_path, remote: true, method: :put, html: {class: 'row prepend-top-default js-preferences-form'} do |f|
.col-lg-3.profile-settings-sidebar
diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml
index eef50d887c7..d9fa74fad90 100644
--- a/app/views/profiles/show.html.haml
+++ b/app/views/profiles/show.html.haml
@@ -1,3 +1,5 @@
+= render 'profiles/head'
+
= form_for @user, url: profile_path, method: :put, html: { multipart: true, class: "edit-user prepend-top-default" }, authenticity_token: true do |f|
= form_errors(@user)
diff --git a/app/views/profiles/two_factor_auths/show.html.haml b/app/views/profiles/two_factor_auths/show.html.haml
index 593be2617c1..5890456bee2 100644
--- a/app/views/profiles/two_factor_auths/show.html.haml
+++ b/app/views/profiles/two_factor_auths/show.html.haml
@@ -1,5 +1,6 @@
- page_title 'Two-Factor Authentication', 'Account'
- header_title "Two-Factor Authentication", profile_two_factor_auth_path
+= render 'profiles/head'
.row.prepend-top-default
.col-lg-3
diff --git a/app/views/projects/builds/show.html.haml b/app/views/projects/builds/show.html.haml
index 4e801cc72fe..4421f3b9562 100644
--- a/app/views/projects/builds/show.html.haml
+++ b/app/views/projects/builds/show.html.haml
@@ -67,4 +67,4 @@
= render "sidebar"
:javascript
- new CiBuild("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{namespace_project_build_url(@project.namespace, @project, @build, :json)}", "#{@build.status}", "#{trace_with_state[:state]}")
+ new Build("#{namespace_project_build_url(@project.namespace, @project, @build)}", "#{namespace_project_build_url(@project.namespace, @project, @build, :json)}", "#{@build.status}", "#{trace_with_state[:state]}")
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
index e38d1ff5ff0..af8dd5cd02c 100644
--- a/app/views/projects/ci/pipelines/_pipeline.html.haml
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -45,7 +45,7 @@
%td
- if pipeline.started_at && pipeline.finished_at
%p.duration
- #{duration_in_words(pipeline.finished_at, pipeline.started_at)}
+ = duration_in_numbers(pipeline.finished_at, pipeline.started_at)
%td
.controls.hidden-xs.pull-right
diff --git a/app/views/projects/diffs/_content.html.haml b/app/views/projects/diffs/_content.html.haml
new file mode 100644
index 00000000000..0c0424edffd
--- /dev/null
+++ b/app/views/projects/diffs/_content.html.haml
@@ -0,0 +1,29 @@
+.diff-content.diff-wrap-lines
+ - # Skip all non non-supported blobs
+ - return unless blob.respond_to?(:text?)
+ - if diff_file.too_large?
+ .nothing-here-block This diff could not be displayed because it is too large.
+ - elsif blob.only_display_raw?
+ .nothing-here-block This file is too large to display.
+ - elsif blob_text_viewable?(blob)
+ - if !project.repository.diffable?(blob)
+ .nothing-here-block This diff was suppressed by a .gitattributes entry.
+ - elsif diff_file.diff_lines.length > 0
+ - if diff_file.collapsed_by_default? && !expand_all_diffs?
+ - url = url_for(params.merge(action: :diff_for_path, old_path: diff_file.old_path, new_path: diff_file.new_path))
+ .nothing-here-block.diff-collapsed{data: { diff_for_path: url } }
+ This diff is collapsed. Click to expand it.
+ - elsif diff_view == 'parallel'
+ = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob
+ - else
+ = render "projects/diffs/text_file", diff_file: diff_file
+ - else
+ - if diff_file.mode_changed?
+ .nothing-here-block File mode changed
+ - elsif diff_file.renamed_file
+ .nothing-here-block File moved
+ - elsif blob.image?
+ - old_blob = diff_file.old_blob(diff_commit)
+ = render "projects/diffs/image", diff_file: diff_file, old_file: old_blob, file: blob
+ - else
+ .nothing-here-block No preview for this file type
diff --git a/app/views/projects/diffs/_diffs.html.haml b/app/views/projects/diffs/_diffs.html.haml
index 2a15c306f07..20aaab5accf 100644
--- a/app/views/projects/diffs/_diffs.html.haml
+++ b/app/views/projects/diffs/_diffs.html.haml
@@ -6,6 +6,8 @@
.content-block.oneline-block.files-changed
.inline-parallel-buttons
+ - unless expand_all_diffs?
+ = link_to 'Expand all', url_for(params.merge(expand_all_diffs: 1, format: 'html')), class: 'btn btn-default'
- if show_whitespace_toggle
- if current_controller?(:commit)
= commit_diff_whitespace_link(@project, @commit, class: 'hidden-xs')
diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml
index 3b758a1ec4e..c306909fb1a 100644
--- a/app/views/projects/diffs/_file.html.haml
+++ b/app/views/projects/diffs/_file.html.haml
@@ -16,28 +16,4 @@
= view_file_btn(diff_commit.id, diff_file, project)
- .diff-content.diff-wrap-lines
- - # Skip all non non-supported blobs
- - return unless blob.respond_to?(:text?)
- - if diff_file.too_large?
- .nothing-here-block This diff could not be displayed because it is too large.
- - elsif blob.only_display_raw?
- .nothing-here-block This file is too large to display.
- - elsif blob_text_viewable?(blob)
- - if !project.repository.diffable?(blob)
- .nothing-here-block This diff was suppressed by a .gitattributes entry.
- - elsif diff_file.diff_lines.length > 0
- - if diff_view == 'parallel'
- = render "projects/diffs/parallel_view", diff_file: diff_file, project: project, blob: blob, index: i
- - else
- = render "projects/diffs/text_file", diff_file: diff_file, index: i
- - else
- - if diff_file.mode_changed?
- .nothing-here-block File mode changed
- - elsif diff_file.renamed_file
- .nothing-here-block File moved
- - elsif blob.image?
- - old_blob = diff_file.old_blob(diff_commit)
- = render "projects/diffs/image", diff_file: diff_file, old_file: old_blob, file: blob, index: i
- - else
- .nothing-here-block No preview for this file type
+ = render 'projects/diffs/content', diff_file: diff_file, diff_commit: diff_commit, diff_refs: diff_refs, blob: blob, project: project
diff --git a/app/views/projects/diffs/_warning.html.haml b/app/views/projects/diffs/_warning.html.haml
index 15536c17f8e..10fa1ddf2e5 100644
--- a/app/views/projects/diffs/_warning.html.haml
+++ b/app/views/projects/diffs/_warning.html.haml
@@ -2,9 +2,6 @@
%h4
Too many changes to show.
.pull-right
- - unless diff_hard_limit_enabled?
- = link_to "Reload with full diff", url_for(params.merge(force_show_diff: true, format: nil)), class: "btn btn-sm"
-
- if current_controller?(:commit) or current_controller?(:merge_requests)
- if current_controller?(:commit)
= link_to "Plain diff", namespace_project_commit_path(@project.namespace, @project, @commit, format: :diff), class: "btn btn-sm"
diff --git a/app/views/projects/issues/index.html.haml b/app/views/projects/issues/index.html.haml
index 7ce4c1e5555..312bd86ed04 100644
--- a/app/views/projects/issues/index.html.haml
+++ b/app/views/projects/issues/index.html.haml
@@ -6,21 +6,37 @@
- if current_user
= auto_discovery_link_tag(:atom, namespace_project_issues_url(@project.namespace, @project, :atom, private_token: current_user.private_token), title: "#{@project.name} issues")
-%div{ class: container_class }
- .top-area
- = render 'shared/issuable/nav', type: :issues
- .nav-controls
- - if current_user
- = link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
- = icon('rss')
- %span.icon-label
- Subscribe
- = render 'shared/issuable/search_form', path: namespace_project_issues_path(@project.namespace, @project)
+%div{ class: (container_class) }
+ - if @project.issues.any?
+ .top-area
+ = render 'shared/issuable/nav', type: :issues
+ .nav-controls
+ - if current_user
+ = link_to namespace_project_issues_path(@project.namespace, @project, :atom, { private_token: current_user.private_token }), class: 'btn append-right-10' do
+ = icon('rss')
+ %span.icon-label
+ Subscribe
+ = render 'shared/issuable/search_form', path: namespace_project_issues_path(@project.namespace, @project)
+ - if can? current_user, :create_issue, @project
+ = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
+ New Issue
+ = render 'shared/issuable/filter', type: :issues
+
+ .issues-holder
+ = render "issues"
+ - else
+ .blank-state.blank-state-welcome
+ %h2.blank-state-title.blank-state-welcome-title
+ Welcome to GitLab Issues
+ %p.blank-state-text
+ Code, test, and deploy together
+ .blank-state
+ .blank-state-icon
+ = navbar_icon("issues", size: 50)
+ %h3.blank-state-title
+ You don't have any issues right now.
+ %p.blank-state-text
+ Issues are the best way to track your project progress
- if can? current_user, :create_issue, @project
- = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { assignee_id: @issuable_finder.assignee.try(:id), milestone_id: @issuable_finder.milestones.try(:first).try(:id) }), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
+ = link_to new_namespace_project_issue_path(@project.namespace, @project), class: "btn btn-new", title: "New Issue", id: "new_issue_link" do
New Issue
-
- = render 'shared/issuable/filter', type: :issues
-
- .issues-holder
- = render "issues"
diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml
index 2ec96308fd7..873ed9b59ee 100644
--- a/app/views/projects/merge_requests/_show.html.haml
+++ b/app/views/projects/merge_requests/_show.html.haml
@@ -42,7 +42,7 @@
= succeed '.' do
= link_to "command line", "#modal_merge_info", class: "how_to_merge_link vlink", title: "How To Merge", "data-toggle" => "modal"
- - if @commits.present?
+ - if @commits_count.nonzero?
%ul.merge-request-tabs.nav-links.no-top.no-bottom
%li.notes-tab
= link_to namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#notes', action: 'notes', toggle: 'tab'} do
@@ -51,7 +51,7 @@
%li.commits-tab
= link_to commits_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: 'div#commits', action: 'commits', toggle: 'tab'} do
Commits
- %span.badge= @commits.size
+ %span.badge= @commits_count
- if @pipeline
%li.builds-tab
= link_to builds_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), data: {target: '#builds', action: 'builds', toggle: 'tab'} do
diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml
index 5bf5210aeab..b24bdf22ceb 100644
--- a/app/views/projects/merge_requests/show/_mr_title.html.haml
+++ b/app/views/projects/merge_requests/show/_mr_title.html.haml
@@ -19,13 +19,13 @@
Options
.dropdown-menu.dropdown-menu-align-right.hidden-lg
%ul
- %li{ class: issue_button_visibility(@merge_request, true) }
+ %li{ class: merge_request_button_visibility(@merge_request, true) }
= link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, title: 'Close merge request'
- %li{ class: issue_button_visibility(@merge_request, false) }
+ %li{ class: merge_request_button_visibility(@merge_request, false) }
= link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'reopen-mr-link', title: 'Reopen merge request'
%li
= link_to 'Edit', edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'issuable-edit'
- = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{issue_button_visibility(@merge_request, true)}", title: 'Close merge request'
- = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen reopen-mr-link #{issue_button_visibility(@merge_request, false)}", title: 'Reopen merge request'
+ = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: "hidden-xs hidden-sm btn btn-grouped btn-close #{merge_request_button_visibility(@merge_request, true)}", title: 'Close merge request'
+ = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "hidden-xs hidden-sm btn btn-grouped btn-reopen reopen-mr-link #{merge_request_button_visibility(@merge_request, false)}", title: 'Reopen merge request'
= link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: "hidden-xs hidden-sm btn btn-grouped issuable-edit" do
Edit
diff --git a/app/views/projects/merge_requests/widget/_open.html.haml b/app/views/projects/merge_requests/widget/_open.html.haml
index 0e0af57d76e..dc18f715f25 100644
--- a/app/views/projects/merge_requests/widget/_open.html.haml
+++ b/app/views/projects/merge_requests/widget/_open.html.haml
@@ -22,10 +22,10 @@
- elsif @merge_request.can_be_merged?
= render 'projects/merge_requests/widget/open/accept'
- - if @closes_issues.present?
+ - if mr_closes_issues.present?
.mr-widget-footer
%span
%i.fa.fa-check
- Accepting this merge request will close #{"issue".pluralize(@closes_issues.size)}
+ Accepting this merge request will close #{"issue".pluralize(mr_closes_issues.size)}
= succeed '.' do
- != markdown issues_sentence(@closes_issues), pipeline: :gfm, author: @merge_request.author
+ != markdown issues_sentence(mr_closes_issues), pipeline: :gfm, author: @merge_request.author
diff --git a/app/views/projects/notes/_notes_with_form.html.haml b/app/views/projects/notes/_notes_with_form.html.haml
index 1c39ce897a3..56d302fab82 100644
--- a/app/views/projects/notes/_notes_with_form.html.haml
+++ b/app/views/projects/notes/_notes_with_form.html.haml
@@ -2,6 +2,8 @@
= render "projects/notes/notes"
%ul.notes.notes-form.timeline
%li.timeline-entry
+ .flash-container.timeline-content
+
- if can? current_user, :create_note, @project
.timeline-icon.hidden-xs.hidden-sm
%a.author_link{ href: user_path(current_user) }
diff --git a/app/views/projects/protected_branches/_branches_list.html.haml b/app/views/projects/protected_branches/_branches_list.html.haml
index 565905cbe7b..97cb1a9052b 100644
--- a/app/views/projects/protected_branches/_branches_list.html.haml
+++ b/app/views/projects/protected_branches/_branches_list.html.haml
@@ -1,6 +1,6 @@
%h5.prepend-top-0
- Already Protected (#{@branches.size})
-- if @branches.empty?
+ Already Protected (#{@protected_branches.size})
+- if @protected_branches.empty?
%p.settings-message.text-center
No branches are protected, protect a branch with the form above.
- else
@@ -9,33 +9,18 @@
%table.table.protected-branches-list
%colgroup
%col{ width: "30%" }
- %col{ width: "30%" }
+ %col{ width: "25%" }
%col{ width: "25%" }
- if can_admin_project
%col
%thead
%tr
- %th Branch
- %th Last commit
- %th Developers can push
+ %th Protected Branch
+ %th Commit
+ %th Developers Can Push
- if can_admin_project
%th
%tbody
- - @branches.each do |branch|
- - @url = namespace_project_protected_branch_path(@project.namespace, @project, branch)
- %tr
- %td
- = link_to(branch.name, namespace_project_commits_path(@project.namespace, @project, branch.name))
- - if @project.root_ref?(branch.name)
- %span.label.label-info.prepend-left-5 default
- %td
- - if commit = branch.commit
- = link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id')
- #{time_ago_with_tooltip(commit.committed_date)}
- - else
- (branch was removed from repository)
- %td
- = check_box_tag("developers_can_push", branch.id, branch.developers_can_push, data: { url: @url })
- - if can_admin_project
- %td
- = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning btn-sm"
+ = render partial: @protected_branches, locals: { can_admin_project: can_admin_project }
+
+ = paginate @protected_branches, theme: 'gitlab'
diff --git a/app/views/projects/protected_branches/_dropdown.html.haml b/app/views/projects/protected_branches/_dropdown.html.haml
new file mode 100644
index 00000000000..b803d932e67
--- /dev/null
+++ b/app/views/projects/protected_branches/_dropdown.html.haml
@@ -0,0 +1,17 @@
+= f.hidden_field(:name)
+
+= dropdown_tag("Protected Branch",
+ options: { title: "Pick protected branch", toggle_class: 'js-protected-branch-select js-filter-submit',
+ filter: true, dropdown_class: "dropdown-menu-selectable", placeholder: "Search protected branches",
+ footer_content: true,
+ data: { show_no: true, show_any: true, show_upcoming: true,
+ selected: params[:protected_branch_name],
+ project_id: @project.try(:id) } }) do
+
+ %ul.dropdown-footer-list.hidden.protected-branch-select-footer-list
+ %li
+ = link_to '#', title: "New Protected Branch", class: "create-new-protected-branch" do
+ Create new
+
+:javascript
+ new ProtectedBranchSelect();
diff --git a/app/views/projects/protected_branches/_matching_branch.html.haml b/app/views/projects/protected_branches/_matching_branch.html.haml
new file mode 100644
index 00000000000..8a5332ca5bb
--- /dev/null
+++ b/app/views/projects/protected_branches/_matching_branch.html.haml
@@ -0,0 +1,9 @@
+%tr
+ %td
+ = link_to matching_branch.name, namespace_project_tree_path(@project.namespace, @project, matching_branch.name)
+ - if @project.root_ref?(matching_branch.name)
+ %span.label.label-info.prepend-left-5 default
+ %td
+ - commit = @project.commit(matching_branch.name)
+ = link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id')
+ = time_ago_with_tooltip(commit.committed_date)
diff --git a/app/views/projects/protected_branches/_protected_branch.html.haml b/app/views/projects/protected_branches/_protected_branch.html.haml
new file mode 100644
index 00000000000..474aec3a97c
--- /dev/null
+++ b/app/views/projects/protected_branches/_protected_branch.html.haml
@@ -0,0 +1,21 @@
+- url = namespace_project_protected_branch_path(@project.namespace, @project, protected_branch)
+%tr
+ %td
+ = protected_branch.name
+ - if @project.root_ref?(protected_branch.name)
+ %span.label.label-info.prepend-left-5 default
+ %td
+ - if protected_branch.wildcard?
+ - matching_branches = protected_branch.matching(repository.branches)
+ = link_to pluralize(matching_branches.count, "matching branch"), namespace_project_protected_branch_path(@project.namespace, @project, protected_branch)
+ - else
+ - if commit = protected_branch.commit
+ = link_to(commit.short_id, namespace_project_commit_path(@project.namespace, @project, commit.id), class: 'commit_short_id')
+ = time_ago_with_tooltip(commit.committed_date)
+ - else
+ (branch was removed from repository)
+ %td
+ = check_box_tag("developers_can_push", protected_branch.id, protected_branch.developers_can_push, data: { url: url })
+ - if can_admin_project
+ %td
+ = link_to 'Unprotect', [@project.namespace.becomes(Namespace), @project, protected_branch], data: { confirm: 'Branch will be writable for developers. Are you sure?' }, method: :delete, class: "btn btn-warning btn-sm pull-right"
diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml
index c7d317dbaee..5669713d9a1 100644
--- a/app/views/projects/protected_branches/index.html.haml
+++ b/app/views/projects/protected_branches/index.html.haml
@@ -4,30 +4,38 @@
.col-lg-3
%h4.prepend-top-0
= page_title
- %p Keep stable branches secure and force developers to use Merge Requests
- .col-lg-9
- %h5.prepend-top-0
- Protect a branch
- .account-well.append-bottom-default
- %p.light-header.append-bottom-0 Protected branches are designed to
+ %p Keep stable branches secure and force developers to use merge requests.
+ %p.prepend-top-20
+ Protected branches are designed to:
%ul
%li prevent pushes from everybody except #{link_to "masters", help_page_path("permissions", "permissions"), class: "vlink"}
%li prevent anyone from force pushing to the branch
%li prevent anyone from deleting the branch
%p.append-bottom-0 Read more about #{link_to "project permissions", help_page_path("permissions", "permissions"), class: "underlined-link"}
+ .col-lg-9
+ %h5.prepend-top-0
+ Protect a branch
- if can? current_user, :admin_project, @project
= form_for [@project.namespace.becomes(Namespace), @project, @protected_branch] do |f|
= form_errors(@protected_branch)
.form-group
= f.label :name, "Branch", class: "label-light"
- = f.select(:name, @project.open_branches.map { |br| [br.name, br.name] } , {include_blank: true}, {class: "select2", data: {placeholder: "Select branch"}})
+ = render partial: "dropdown", locals: { f: f }
+ %p.help-block
+ = link_to "Wildcards", help_page_path(category: 'workflow', file: 'protected_branches', format: 'md', anchor: "wildcard-protected-branches")
+ such as
+ %code *-stable
+ or
+ %code production/*
+ are supported.
+
.form-group
= f.check_box :developers_can_push, class: "pull-left"
.prepend-left-20
= f.label :developers_can_push, "Developers can push", class: "label-light append-bottom-0"
%p.light.append-bottom-0
Allow developers to push to this branch
- = f.submit "Protect", class: "btn-create btn"
+ = f.submit "Protect", class: "btn-create btn protect-branch-btn", disabled: true
%hr
= render "branches_list"
diff --git a/app/views/projects/protected_branches/show.html.haml b/app/views/projects/protected_branches/show.html.haml
new file mode 100644
index 00000000000..4d8169815b3
--- /dev/null
+++ b/app/views/projects/protected_branches/show.html.haml
@@ -0,0 +1,25 @@
+- page_title @protected_branch.name, "Protected Branches"
+
+.row.prepend-top-default.append-bottom-default
+ .col-lg-3
+ %h4.prepend-top-0
+ = @protected_branch.name
+
+ .col-lg-9
+ %h5 Matching Branches
+ - if @matching_branches.present?
+ .table-responsive
+ %table.table.protected-branches-list
+ %colgroup
+ %col{ width: "30%" }
+ %col{ width: "30%" }
+ %thead
+ %tr
+ %th Branch
+ %th Last commit
+ %tbody
+ - @matching_branches.each do |matching_branch|
+ = render partial: "matching_branch", object: matching_branch
+ - else
+ %p.settings-message.text-center
+ Couldn't find any matching branches.
diff --git a/app/views/projects/snippets/_actions.html.haml b/app/views/projects/snippets/_actions.html.haml
index bf57beb9d07..bdbf3e5f4d6 100644
--- a/app/views/projects/snippets/_actions.html.haml
+++ b/app/views/projects/snippets/_actions.html.haml
@@ -1,27 +1,29 @@
.hidden-xs
- = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New Snippet" do
- = icon('plus')
- New Snippet
+ - if can?(current_user, :create_project_snippet, @project)
+ = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: 'btn btn-grouped btn-create new-snippet-link', title: "New Snippet" do
+ New Snippet
- if can?(current_user, :update_project_snippet, @snippet)
= link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet), class: "btn btn-grouped snippable-edit" do
Edit
- if can?(current_user, :update_project_snippet, @snippet)
= link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-warning", title: 'Delete Snippet' do
Delete
-.visible-xs-block.dropdown
- %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
- Options
- %span.caret
- .dropdown-menu.dropdown-menu-full-width
- %ul
- %li
- = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New Snippet" do
- New Snippet
- - if can?(current_user, :update_project_snippet, @snippet)
- %li
- = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do
- Edit
- - if can?(current_user, :update_project_snippet, @snippet)
- %li
- = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
- Delete
+- if can?(current_user, :create_project_snippet, @project) || can?(current_user, :update_project_snippet, @snippet)
+ .visible-xs-block.dropdown
+ %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
+ Options
+ %span.caret
+ .dropdown-menu.dropdown-menu-full-width
+ %ul
+ - if can?(current_user, :create_project_snippet, @project)
+ %li
+ = link_to new_namespace_project_snippet_path(@project.namespace, @project), title: "New Snippet" do
+ New Snippet
+ - if can?(current_user, :update_project_snippet, @snippet)
+ %li
+ = link_to edit_namespace_project_snippet_path(@project.namespace, @project, @snippet) do
+ Edit
+ - if can?(current_user, :update_project_snippet, @snippet)
+ %li
+ = link_to namespace_project_snippet_path(@project.namespace, @project, @snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
+ Delete
diff --git a/app/views/projects/snippets/index.html.haml b/app/views/projects/snippets/index.html.haml
index 96fee3b17b2..6c994ae486b 100644
--- a/app/views/projects/snippets/index.html.haml
+++ b/app/views/projects/snippets/index.html.haml
@@ -2,9 +2,9 @@
.row-content-block.top-block
.pull-right
- = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
- = icon('plus')
- New Snippet
+ - if can?(current_user, :create_project_snippet, @project)
+ = link_to new_namespace_project_snippet_path(@project.namespace, @project), class: "btn btn-new", title: "New Snippet" do
+ New Snippet
.oneline
Share code pastes with others out of git repository
diff --git a/app/views/shared/icons/_issues.svg b/app/views/shared/icons/_issues.svg
deleted file mode 100644
index 2682c27ade9..00000000000
--- a/app/views/shared/icons/_issues.svg
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0" encoding="UTF-8" standalone="no"?>
-<svg width="16px" height="16px" viewBox="0 0 16 16" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
- <!-- Generator: Sketch 3.7.2 (28276) - http://www.bohemiancoding.com/sketch -->
- <title>Group</title>
- <desc>Created with Sketch.</desc>
- <defs></defs>
- <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
- <g id="Group" fill="#7E7C7C">
- <path d="M8,0 C3.581,0 0,3.581 0,8 C0,12.419 3.581,16 8,16 C12.419,16 16,12.419 16,8 C16,3.581 12.419,0 8,0 M8,2 C11.308,2 14,4.692 14,8 C14,11.308 11.308,14 8,14 C4.692,14 2,11.308 2,8 C2,4.692 4.692,2 8,2" id="Fill-1"></path>
- <path d="M7.1597,4 L8.8887,4 L8.8887,8 L7.1107,8 L7.1597,4 Z M7.1597,9.6667 L8.8887,9.6667 L8.8887,11.4447 L7.1107,11.4447 L7.1597,9.6667 Z" id="Combined-Shape"></path>
- </g>
- </g>
-</svg> \ No newline at end of file
diff --git a/app/views/shared/icons/_issues.svg.erb b/app/views/shared/icons/_issues.svg.erb
new file mode 100644
index 00000000000..fa8655b5609
--- /dev/null
+++ b/app/views/shared/icons/_issues.svg.erb
@@ -0,0 +1,4 @@
+<svg width="<%= size %>" height="<%= size %>" viewBox="0 0 16 16" class="gitlab-icon">
+ <path fill="#7E7C7C" d="M8,0 C3.581,0 0,3.581 0,8 C0,12.419 3.581,16 8,16 C12.419,16 16,12.419 16,8 C16,3.581 12.419,0 8,0 M8,2 C11.308,2 14,4.692 14,8 C14,11.308 11.308,14 8,14 C4.692,14 2,11.308 2,8 C2,4.692 4.692,2 8,2"></path>
+ <path fill="#7E7C7C" d="M7.1597,4 L8.8887,4 L8.8887,8 L7.1107,8 L7.1597,4 Z M7.1597,9.6667 L8.8887,9.6667 L8.8887,11.4447 L7.1107,11.4447 L7.1597,9.6667 Z"></path>
+</svg>
diff --git a/app/views/snippets/_actions.html.haml b/app/views/snippets/_actions.html.haml
index a7769654b61..160c6cd84da 100644
--- a/app/views/snippets/_actions.html.haml
+++ b/app/views/snippets/_actions.html.haml
@@ -1,27 +1,28 @@
.hidden-xs
- = link_to new_snippet_path, class: "btn btn-grouped btn-create new-snippet-link", title: "New Snippet" do
- = icon('plus')
- New Snippet
+ - if current_user
+ = link_to new_snippet_path, class: "btn btn-grouped btn-create new-snippet-link", title: "New Snippet" do
+ New Snippet
- if can?(current_user, :update_personal_snippet, @snippet)
= link_to edit_snippet_path(@snippet), class: "btn btn-grouped snippable-edit" do
Edit
- if can?(current_user, :admin_personal_snippet, @snippet)
- = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-warning", title: 'Delete Snippet' do
+ = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, class: "btn btn-grouped btn-danger", title: 'Delete Snippet' do
Delete
-.visible-xs-block.dropdown
- %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
- Options
- %span.caret
- .dropdown-menu.dropdown-menu-full-width
- %ul
- %li
- = link_to new_snippet_path, title: "New Snippet" do
- New Snippet
- - if can?(current_user, :update_personal_snippet, @snippet)
+- if current_user
+ .visible-xs-block.dropdown
+ %button.btn.btn-default.btn-block.append-bottom-0.prepend-top-5{ data: { toggle: "dropdown" } }
+ Options
+ %span.caret
+ .dropdown-menu.dropdown-menu-full-width
+ %ul
%li
- = link_to edit_snippet_path(@snippet) do
- Edit
- - if can?(current_user, :admin_personal_snippet, @snippet)
- %li
- = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
- Delete
+ = link_to new_snippet_path, title: "New Snippet" do
+ New Snippet
+ - if can?(current_user, :update_personal_snippet, @snippet)
+ %li
+ = link_to edit_snippet_path(@snippet) do
+ Edit
+ - if can?(current_user, :admin_personal_snippet, @snippet)
+ %li
+ = link_to snippet_path(@snippet), method: :delete, data: { confirm: "Are you sure?" }, title: 'Delete Snippet' do
+ Delete
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 68665858c3e..db2b4885861 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -29,6 +29,11 @@
&nbsp;
= link_to user_path(@user, :atom, { private_token: current_user.private_token }), class: 'btn btn-gray' do
= icon('rss')
+ - if current_user.admin?
+ &nbsp;
+ = link_to [:admin, @user], class: 'btn btn-gray', title: 'View user in admin area',
+ data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do
+ = icon('users')
.avatar-holder
= link_to avatar_icon(@user, 400), target: '_blank' do