summaryrefslogtreecommitdiff
path: root/app
diff options
context:
space:
mode:
Diffstat (limited to 'app')
-rw-r--r--app/assets/javascripts/api.js.coffee7
-rw-r--r--app/assets/javascripts/application.js.coffee75
-rw-r--r--app/assets/javascripts/blob/blob_ci_yaml.js.coffee23
-rw-r--r--app/assets/javascripts/blob/edit_blob.js.coffee1
-rw-r--r--app/assets/javascripts/blob/template_selector.js.coffee4
-rw-r--r--app/assets/javascripts/gl_dropdown.js.coffee29
-rw-r--r--app/assets/javascripts/gl_form.js.coffee3
-rw-r--r--app/assets/javascripts/graphs/application.js.coffee1
-rw-r--r--app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee6
-rw-r--r--app/assets/javascripts/issuable.js.coffee8
-rw-r--r--app/assets/javascripts/issue.js.coffee2
-rw-r--r--app/assets/javascripts/issue_status_select.js.coffee7
-rw-r--r--app/assets/javascripts/issues-bulk-assignment.js.coffee14
-rw-r--r--app/assets/javascripts/labels_select.js.coffee7
-rw-r--r--app/assets/javascripts/layout_nav.js.coffee9
-rw-r--r--app/assets/javascripts/lib/chart.js.coffee1
-rw-r--r--app/assets/javascripts/lib/d3.js.coffee1
-rw-r--r--app/assets/javascripts/lib/raphael.js.coffee3
-rw-r--r--app/assets/javascripts/lib/utils/animate.js.coffee (renamed from app/assets/javascripts/lib/animate.js.coffee)0
-rw-r--r--app/assets/javascripts/lib/utils/common_utils.js.coffee (renamed from app/assets/javascripts/lib/common_utils.js.coffee)0
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js.coffee (renamed from app/assets/javascripts/lib/datetime_utility.js.coffee)0
-rw-r--r--app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb (renamed from app/assets/javascripts/lib/emoji_aliases.js.coffee.erb)0
-rw-r--r--app/assets/javascripts/lib/utils/jquery.timeago.js (renamed from app/assets/javascripts/lib/jquery.timeago.js)0
-rw-r--r--app/assets/javascripts/lib/utils/md5.js (renamed from app/assets/javascripts/lib/md5.js)0
-rw-r--r--app/assets/javascripts/lib/utils/notify.js.coffee (renamed from app/assets/javascripts/lib/notify.js.coffee)0
-rw-r--r--app/assets/javascripts/lib/utils/text_utility.js.coffee79
-rw-r--r--app/assets/javascripts/lib/utils/type_utility.js.coffee (renamed from app/assets/javascripts/lib/type_utility.js.coffee)0
-rw-r--r--app/assets/javascripts/lib/utils/url_utility.js.coffee (renamed from app/assets/javascripts/lib/url_utility.js.coffee)0
-rw-r--r--app/assets/javascripts/lib/utils/utf8_encode.js (renamed from app/assets/javascripts/lib/utf8_encode.js)0
-rw-r--r--app/assets/javascripts/milestone.js.coffee63
-rw-r--r--app/assets/javascripts/network/application.js.coffee3
-rw-r--r--app/assets/javascripts/notes.js.coffee5
-rw-r--r--app/assets/javascripts/notifications_dropdown.js.coffee7
-rw-r--r--app/assets/javascripts/project.js.coffee41
-rw-r--r--app/assets/javascripts/users/application.js.coffee6
-rw-r--r--app/assets/stylesheets/framework.scss1
-rw-r--r--app/assets/stylesheets/framework/blank.scss23
-rw-r--r--app/assets/stylesheets/framework/blocks.scss16
-rw-r--r--app/assets/stylesheets/framework/dropdowns.scss10
-rw-r--r--app/assets/stylesheets/framework/header.scss5
-rw-r--r--app/assets/stylesheets/framework/markdown_area.scss30
-rw-r--r--app/assets/stylesheets/framework/mixins.scss14
-rw-r--r--app/assets/stylesheets/framework/nav.scss112
-rw-r--r--app/assets/stylesheets/framework/panels.scss4
-rw-r--r--app/assets/stylesheets/framework/selects.scss5
-rw-r--r--app/assets/stylesheets/framework/sidebar.scss1
-rw-r--r--app/assets/stylesheets/pages/commits.scss12
-rw-r--r--app/assets/stylesheets/pages/diff.scss5
-rw-r--r--app/assets/stylesheets/pages/editor.scss11
-rw-r--r--app/assets/stylesheets/pages/events.scss4
-rw-r--r--app/assets/stylesheets/pages/help.scss8
-rw-r--r--app/assets/stylesheets/pages/issuable.scss8
-rw-r--r--app/assets/stylesheets/pages/labels.scss5
-rw-r--r--app/assets/stylesheets/pages/merge_requests.scss15
-rw-r--r--app/assets/stylesheets/pages/note_form.scss17
-rw-r--r--app/assets/stylesheets/pages/notes.scss24
-rw-r--r--app/assets/stylesheets/pages/projects.scss31
-rw-r--r--app/assets/stylesheets/pages/stat_graph.scss22
-rw-r--r--app/assets/stylesheets/pages/todos.scss4
-rw-r--r--app/controllers/admin/appearances_controller.rb1
-rw-r--r--app/controllers/admin/runner_projects_controller.rb11
-rw-r--r--app/controllers/application_controller.rb4
-rw-r--r--app/controllers/concerns/membership_actions.rb31
-rw-r--r--app/controllers/groups/group_members_controller.rb8
-rw-r--r--app/controllers/import/gitlab_projects_controller.rb6
-rw-r--r--app/controllers/projects/application_controller.rb2
-rw-r--r--app/controllers/projects/blob_controller.rb7
-rw-r--r--app/controllers/projects/issues_controller.rb7
-rw-r--r--app/controllers/projects/merge_requests_controller.rb44
-rw-r--r--app/controllers/projects/notes_controller.rb10
-rw-r--r--app/controllers/projects/pipelines_controller.rb2
-rw-r--r--app/controllers/projects/project_members_controller.rb8
-rw-r--r--app/controllers/projects/runner_projects_controller.rb4
-rw-r--r--app/controllers/projects/runners_controller.rb7
-rw-r--r--app/controllers/projects_controller.rb28
-rw-r--r--app/helpers/application_helper.rb20
-rw-r--r--app/helpers/blob_helper.rb18
-rw-r--r--app/helpers/branches_helper.rb2
-rw-r--r--app/helpers/gitlab_markdown_helper.rb15
-rw-r--r--app/helpers/javascript_helper.rb6
-rw-r--r--app/mailers/emails/members.rb5
-rw-r--r--app/models/ability.rb3
-rw-r--r--app/models/ci/build.rb8
-rw-r--r--app/models/ci/pipeline.rb23
-rw-r--r--app/models/ci/runner.rb23
-rw-r--r--app/models/commit.rb26
-rw-r--r--app/models/commit_status.rb2
-rw-r--r--app/models/concerns/awardable.rb12
-rw-r--r--app/models/concerns/issuable.rb39
-rw-r--r--app/models/concerns/participable.rb10
-rw-r--r--app/models/key.rb2
-rw-r--r--app/models/legacy_diff_note.rb2
-rw-r--r--app/models/member.rb7
-rw-r--r--app/models/members/group_member.rb12
-rw-r--r--app/models/members/project_member.rb12
-rw-r--r--app/models/merge_request.rb8
-rw-r--r--app/models/note.rb6
-rw-r--r--app/models/repository.rb13
-rw-r--r--app/models/user.rb5
-rw-r--r--app/services/ci/register_build_service.rb2
-rw-r--r--app/services/members/destroy_service.rb21
-rw-r--r--app/services/merge_requests/build_service.rb2
-rw-r--r--app/services/notification_service.rb21
-rw-r--r--app/services/todo_service.rb3
-rw-r--r--app/views/admin/appearances/_form.html.haml2
-rw-r--r--app/views/admin/appearances/preview.html.haml34
-rw-r--r--app/views/admin/appearances/show.html.haml2
-rw-r--r--app/views/admin/application_settings/show.html.haml1
-rw-r--r--app/views/admin/groups/show.html.haml33
-rw-r--r--app/views/admin/projects/show.html.haml49
-rw-r--r--app/views/admin/runners/show.html.haml4
-rw-r--r--app/views/ci/errors/show.haml2
-rw-r--r--app/views/ci/shared/_guide.html.haml13
-rw-r--r--app/views/ci/shared/_no_runners.html.haml7
-rw-r--r--app/views/dashboard/_projects_head.html.haml2
-rw-r--r--app/views/groups/group_members/index.html.haml3
-rw-r--r--app/views/groups/show.html.haml2
-rw-r--r--app/views/help/_shortcuts.html.haml6
-rw-r--r--app/views/layouts/_head.html.haml4
-rw-r--r--app/views/layouts/_page.html.haml2
-rw-r--r--app/views/layouts/ci/_info.html.haml2
-rw-r--r--app/views/layouts/ci/_page.html.haml22
-rw-r--r--app/views/layouts/ci/notify.html.haml19
-rw-r--r--app/views/layouts/nav/_admin.html.haml90
-rw-r--r--app/views/layouts/nav/_admin_settings.html.haml31
-rw-r--r--app/views/layouts/nav/_group.html.haml9
-rw-r--r--app/views/layouts/nav/_group_settings.html.haml36
-rw-r--r--app/views/layouts/nav/_profile.html.haml91
-rw-r--r--app/views/layouts/nav/_project.html.haml23
-rw-r--r--app/views/layouts/nav/_project_settings.html.haml2
-rw-r--r--app/views/notify/project_was_not_exported_email.html.haml2
-rw-r--r--app/views/notify/project_was_not_exported_email.text.erb6
-rw-r--r--app/views/notify/project_was_not_exported_email.text.haml6
-rw-r--r--app/views/profiles/notifications/show.html.haml2
-rw-r--r--app/views/projects/_md_preview.html.haml15
-rw-r--r--app/views/projects/_merge_request_settings.html.haml2
-rw-r--r--app/views/projects/badges/index.html.haml2
-rw-r--r--app/views/projects/blob/_editor.html.haml2
-rw-r--r--app/views/projects/ci/pipelines/_pipeline.html.haml4
-rw-r--r--app/views/projects/commits/_commit.html.haml47
-rw-r--r--app/views/projects/commits/_head.html.haml8
-rw-r--r--app/views/projects/compare/index.html.haml2
-rw-r--r--app/views/projects/compare/show.html.haml36
-rw-r--r--app/views/projects/container_registry/_tag.html.haml6
-rw-r--r--app/views/projects/environments/index.html.haml16
-rw-r--r--app/views/projects/environments/new.html.haml5
-rw-r--r--app/views/projects/environments/show.html.haml12
-rw-r--r--app/views/projects/forks/index.html.haml6
-rw-r--r--app/views/projects/graphs/_head.html.haml28
-rw-r--r--app/views/projects/graphs/ci.html.haml25
-rw-r--r--app/views/projects/graphs/commits.html.haml90
-rw-r--r--app/views/projects/graphs/languages.html.haml36
-rw-r--r--app/views/projects/graphs/show.html.haml48
-rw-r--r--app/views/projects/merge_requests/_discussion.html.haml4
-rw-r--r--app/views/projects/network/show.html.haml6
-rw-r--r--app/views/projects/notes/_hints.html.haml2
-rw-r--r--app/views/projects/notes/_note.html.haml6
-rw-r--r--app/views/projects/project_members/_group_members.html.haml3
-rw-r--r--app/views/projects/project_members/_shared_group_members.html.haml5
-rw-r--r--app/views/projects/project_members/_team.html.haml3
-rw-r--r--app/views/projects/project_members/index.html.haml2
-rw-r--r--app/views/projects/runners/_form.html.haml6
-rw-r--r--app/views/projects/runners/_runner.html.haml6
-rw-r--r--app/views/projects/runners/_specific_runners.html.haml10
-rw-r--r--app/views/projects/runners/show.html.haml3
-rw-r--r--app/views/projects/show.html.haml4
-rw-r--r--app/views/projects/wikis/_main_links.html.haml3
-rw-r--r--app/views/projects/wikis/_nav.html.haml11
-rw-r--r--app/views/projects/wikis/_new.html.haml31
-rw-r--r--app/views/projects/wikis/edit.html.haml32
-rw-r--r--app/views/projects/wikis/git_access.html.haml52
-rw-r--r--app/views/projects/wikis/history.html.haml66
-rw-r--r--app/views/projects/wikis/pages.html.haml18
-rw-r--r--app/views/projects/wikis/show.html.haml34
-rw-r--r--app/views/search/results/_blob.html.haml5
-rw-r--r--app/views/shared/_event_filter.html.haml2
-rw-r--r--app/views/shared/_file_highlight.html.haml3
-rw-r--r--app/views/shared/_ref_switcher.html.haml9
-rw-r--r--app/views/shared/members/_requests.html.haml2
-rw-r--r--app/views/shared/milestones/_issuable.html.haml9
-rw-r--r--app/views/shared/projects/_list.html.haml6
-rw-r--r--app/views/users/show.html.haml4
182 files changed, 1531 insertions, 1013 deletions
diff --git a/app/assets/javascripts/api.js.coffee b/app/assets/javascripts/api.js.coffee
index 3f61ea1eaf4..cf46f15a156 100644
--- a/app/assets/javascripts/api.js.coffee
+++ b/app/assets/javascripts/api.js.coffee
@@ -7,6 +7,7 @@
labelsPath: "/api/:version/projects/:id/labels"
licensePath: "/api/:version/licenses/:key"
gitignorePath: "/api/:version/gitignores/:key"
+ gitlabCiYmlPath: "/api/:version/gitlab_ci_ymls/:key"
group: (group_id, callback) ->
url = Api.buildUrl(Api.groupPath)
@@ -110,6 +111,12 @@
$.get url, (gitignore) ->
callback(gitignore)
+ gitlabCiYml: (key, callback) ->
+ url = Api.buildUrl(Api.gitlabCiYmlPath).replace(':key', key)
+
+ $.get url, (file) ->
+ callback(file)
+
buildUrl: (url) ->
url = gon.relative_url_root + url if gon.relative_url_root?
return url.replace(':version', gon.api_version)
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee
index 2f9f6c3ef5b..5c5a4ca7670 100644
--- a/app/assets/javascripts/application.js.coffee
+++ b/app/assets/javascripts/application.js.coffee
@@ -50,7 +50,7 @@
#= require_directory ./ci
#= require_directory ./commit
#= require_directory ./extensions
-#= require_directory ./lib
+#= require_directory ./lib/utils
#= require_directory ./u2f
#= require_directory .
#= require fuzzaldrin-plus
@@ -121,6 +121,11 @@ window.onload = ->
setTimeout shiftWindow, 100
$ ->
+
+ $document = $(document)
+ $window = $(window)
+ $body = $('body')
+
gl.utils.preventDisabledButtons()
bootstrapBreakpoint = bp.getBreakpointSize()
@@ -152,7 +157,7 @@ $ ->
), 1
# Initialize tooltips
- $('body').tooltip(
+ $body.tooltip(
selector: '.has-tooltip, [data-toggle="tooltip"]'
placement: (_, el) ->
$el = $(el)
@@ -171,7 +176,7 @@ $ ->
flash.show()
# Disable form buttons while a form is submitting
- $('body').on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
+ $body.on 'ajax:complete, ajax:beforeSend, submit', 'form', (e) ->
buttons = $('[type="submit"]', @)
switch e.type
@@ -184,7 +189,7 @@ $ ->
$('.account-box').hover -> $(@).toggleClass('hover')
# Commit show suppressed diff
- $(document).on 'click', '.diff-content .js-show-suppressed-diff', ->
+ $document.on 'click', '.diff-content .js-show-suppressed-diff', ->
$container = $(@).parent()
$container.next('table').show()
$container.remove()
@@ -197,13 +202,13 @@ $ ->
$('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left")
# Show/hide comments on diff
- $("body").on "click", ".js-toggle-diff-comments", (e) ->
+ $body.on "click", ".js-toggle-diff-comments", (e) ->
$(@).toggleClass('active')
$(@).closest(".diff-file").find(".notes_holder").toggle()
e.preventDefault()
- $(document).off "click", '.js-confirm-danger'
- $(document).on "click", '.js-confirm-danger', (e) ->
+ $document.off "click", '.js-confirm-danger'
+ $document.on "click", '.js-confirm-danger', (e) ->
e.preventDefault()
btn = $(e.target)
text = btn.data("confirm-danger-message")
@@ -211,7 +216,7 @@ $ ->
new ConfirmDangerModal(form, text)
- $(document).on 'click', 'button', ->
+ $document.on 'click', 'button', ->
$(this).blur()
$('input[type="search"]').each ->
@@ -219,7 +224,7 @@ $ ->
$this.attr 'value', $this.val()
return
- $(document)
+ $document
.off 'keyup', 'input[type="search"]'
.on 'keyup', 'input[type="search"]' , (e) ->
$this = $(this)
@@ -227,7 +232,7 @@ $ ->
$sidebarGutterToggle = $('.js-sidebar-toggle')
- $(document)
+ $document
.off 'breakpoint:change'
.on 'breakpoint:change', (e, breakpoint) ->
if breakpoint is 'sm' or breakpoint is 'xs'
@@ -239,14 +244,14 @@ $ ->
oldBootstrapBreakpoint = bootstrapBreakpoint
bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint != oldBootstrapBreakpoint
- $(document).trigger('breakpoint:change', [bootstrapBreakpoint])
+ $document.trigger('breakpoint:change', [bootstrapBreakpoint])
checkInitialSidebarSize = ->
bootstrapBreakpoint = bp.getBreakpointSize()
if bootstrapBreakpoint is "xs" or "sm"
- $(document).trigger('breakpoint:change', [bootstrapBreakpoint])
+ $document.trigger('breakpoint:change', [bootstrapBreakpoint])
- $(window)
+ $window
.off "resize.app"
.on "resize.app", (e) ->
fitSidebarForSize()
@@ -256,29 +261,45 @@ $ ->
new Aside()
# Sidenav pinning
- if $(window).width() < 1440 and $.cookie('pin_nav') is 'true'
- $.cookie('pin_nav', 'false')
+ if $window.width() < 1440 and $.cookie('pin_nav') is 'true'
+ $.cookie('pin_nav', 'false', { path: '/' })
$('.page-with-sidebar')
.toggleClass('page-sidebar-collapsed page-sidebar-expanded')
.removeClass('page-sidebar-pinned')
$('.navbar-fixed-top').removeClass('header-pinned-nav')
- $(document)
+ $document
.off 'click', '.js-nav-pin'
.on 'click', '.js-nav-pin', (e) ->
e.preventDefault()
+ $pinBtn = $(e.currentTarget)
+ $page = $ '.page-with-sidebar'
+ $topNav = $ '.navbar-fixed-top'
+ $tooltip = $ "##{$pinBtn.attr('aria-describedby')}"
+ doPinNav = not $page.is('.page-sidebar-pinned')
+ tooltipText = 'Pin navigation'
+
$(this).toggleClass 'is-active'
- if $.cookie('pin_nav') is 'true'
- $.cookie 'pin_nav', 'false'
- $('.page-with-sidebar')
- .removeClass('page-sidebar-pinned')
- .toggleClass('page-sidebar-collapsed page-sidebar-expanded')
- $('.navbar-fixed-top')
- .removeClass('header-pinned-nav')
- .toggleClass('header-collapsed header-expanded')
+ if doPinNav
+ $page.addClass('page-sidebar-pinned')
+ $topNav.addClass('header-pinned-nav')
else
- $.cookie 'pin_nav', 'true'
- $('.page-with-sidebar').addClass('page-sidebar-pinned')
- $('.navbar-fixed-top').addClass('header-pinned-nav')
+ $tooltip.remove() # Remove it immediately when collapsing the sidebar
+ $page.removeClass('page-sidebar-pinned')
+ .toggleClass('page-sidebar-collapsed page-sidebar-expanded')
+ $topNav.removeClass('header-pinned-nav')
+ .toggleClass('header-collapsed header-expanded')
+
+ # Save settings
+ $.cookie 'pin_nav', doPinNav, { path: '/' }
+
+ if $.cookie('pin_nav') is 'true' or doPinNav
+ tooltipText = 'Unpin navigation'
+
+ # Update tooltip text immediately
+ $tooltip.find('.tooltip-inner').text(tooltipText)
+
+ # Persist tooltip title
+ $pinBtn.attr('title', tooltipText).tooltip('fixTitle')
diff --git a/app/assets/javascripts/blob/blob_ci_yaml.js.coffee b/app/assets/javascripts/blob/blob_ci_yaml.js.coffee
new file mode 100644
index 00000000000..d9a03d05529
--- /dev/null
+++ b/app/assets/javascripts/blob/blob_ci_yaml.js.coffee
@@ -0,0 +1,23 @@
+#= require blob/template_selector
+
+class @BlobCiYamlSelector extends TemplateSelector
+ requestFile: (query) ->
+ Api.gitlabCiYml query.name, @requestFileSuccess.bind(@)
+
+class @BlobCiYamlSelectors
+ constructor: (opts) ->
+ {
+ @$dropdowns = $('.js-gitlab-ci-yml-selector')
+ @editor
+ } = opts
+
+ @$dropdowns.each (i, dropdown) =>
+ $dropdown = $(dropdown)
+
+ new BlobCiYamlSelector(
+ pattern: /(.gitlab-ci.yml)/,
+ data: $dropdown.data('data'),
+ wrapper: $dropdown.closest('.js-gitlab-ci-yml-selector-wrap'),
+ dropdown: $dropdown,
+ editor: @editor
+ )
diff --git a/app/assets/javascripts/blob/edit_blob.js.coffee b/app/assets/javascripts/blob/edit_blob.js.coffee
index 636f909dbd0..19e584519d7 100644
--- a/app/assets/javascripts/blob/edit_blob.js.coffee
+++ b/app/assets/javascripts/blob/edit_blob.js.coffee
@@ -15,6 +15,7 @@ class @EditBlob
new BlobLicenseSelectors { @editor }
new BlobGitignoreSelectors { @editor }
+ new BlobCiYamlSelectors { @editor }
initModePanesAndLinks: ->
@$editModePanes = $(".js-edit-mode-pane")
diff --git a/app/assets/javascripts/blob/template_selector.js.coffee b/app/assets/javascripts/blob/template_selector.js.coffee
index e76e303189d..40c9169beac 100644
--- a/app/assets/javascripts/blob/template_selector.js.coffee
+++ b/app/assets/javascripts/blob/template_selector.js.coffee
@@ -19,6 +19,7 @@ class @TemplateSelector
data: @data,
filterable: true,
selectable: true,
+ toggleLabel: @toggleLabel,
search:
fields: ['name']
clicked: @onClick
@@ -31,6 +32,9 @@ class @TemplateSelector
@onFilenameUpdate()
)
+ toggleLabel: (item) ->
+ item.name
+
onFilenameUpdate: ->
return unless @$input.length
diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee
index 2a8a1f05b35..703128fecb3 100644
--- a/app/assets/javascripts/gl_dropdown.js.coffee
+++ b/app/assets/javascripts/gl_dropdown.js.coffee
@@ -58,7 +58,7 @@ class GitLabDropdownFilter
filter: (search_text) ->
data = @options.data()
- if data?
+ if data? and not @options.filterByText
results = data
if search_text isnt ''
@@ -102,10 +102,11 @@ class GitLabDropdownFilter
$el = $(@)
matches = fuzzaldrinPlus.match($el.text().trim(), search_text)
- if matches.length
- $el.show()
- else
- $el.hide()
+ unless $el.is('.dropdown-header')
+ if matches.length
+ $el.show()
+ else
+ $el.hide()
else
elements.show()
@@ -191,6 +192,7 @@ class GitLabDropdown
if @options.filterable
@filter = new GitLabDropdownFilter @filterInput,
filterInputBlur: @filterInputBlur
+ filterByText: @options.filterByText
remote: @options.filterRemote
query: @options.data
keys: searchFields
@@ -278,7 +280,7 @@ class GitLabDropdown
html = @renderData(data)
# Render the full menu
- full_html = @renderMenu(html.join(""))
+ full_html = @renderMenu(html)
@appendMenu(full_html)
@@ -349,7 +351,8 @@ class GitLabDropdown
if @options.renderMenu
menu_html = @options.renderMenu(html)
else
- menu_html = "<ul>#{html}</ul>"
+ menu_html = $('<ul />')
+ .append(html)
return menu_html
@@ -358,7 +361,9 @@ class GitLabDropdown
selector = '.dropdown-content'
if @dropdown.find(".dropdown-toggle-page").length
selector = ".dropdown-page-one .dropdown-content"
- $(selector, @dropdown).html html
+ $(selector, @dropdown)
+ .empty()
+ .append(html)
# Render the row
renderItem: (data, group = false, index = false) ->
@@ -457,7 +462,7 @@ class GitLabDropdown
# Toggle the dropdown label
if @options.toggleLabel
- @updateLabel()
+ @updateLabel(selectedObject, el, @)
else
selectedObject
else if el.hasClass(INDETERMINATE_CLASS)
@@ -484,7 +489,7 @@ class GitLabDropdown
# Toggle the dropdown label
if @options.toggleLabel
- @updateLabel(selectedObject, el)
+ @updateLabel(selectedObject, el, @)
if value?
if !field.length and fieldName
@addInput(fieldName, value)
@@ -583,8 +588,8 @@ class GitLabDropdown
# Scroll the dropdown content up
$dropdownContent.scrollTop(listItemTop - dropdownContentTop)
- updateLabel: (selected = null, el = null) =>
- $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el)
+ updateLabel: (selected = null, el = null, instance = null) =>
+ $(@el).find(".dropdown-toggle-text").text @options.toggleLabel(selected, el, instance)
$.fn.glDropdown = (opts) ->
return @.each ->
diff --git a/app/assets/javascripts/gl_form.js.coffee b/app/assets/javascripts/gl_form.js.coffee
index d540cc4dc46..77512d187c9 100644
--- a/app/assets/javascripts/gl_form.js.coffee
+++ b/app/assets/javascripts/gl_form.js.coffee
@@ -34,6 +34,8 @@ class @GLForm
# form and textarea event listeners
@addEventListeners()
+ gl.text.init(@form)
+
# hide discard button
@form.find('.js-note-discard').hide()
@@ -42,6 +44,7 @@ class @GLForm
clearEventListeners: ->
@textarea.off 'focus'
@textarea.off 'blur'
+ gl.text.removeListeners(@form)
addEventListeners: ->
@textarea.on 'focus', ->
diff --git a/app/assets/javascripts/graphs/application.js.coffee b/app/assets/javascripts/graphs/application.js.coffee
index 91f81a5d249..e0f681acf0b 100644
--- a/app/assets/javascripts/graphs/application.js.coffee
+++ b/app/assets/javascripts/graphs/application.js.coffee
@@ -4,5 +4,4 @@
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
-#= require Chart
#= require_tree .
diff --git a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee
index 584d281a510..834a81af459 100644
--- a/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee
+++ b/app/assets/javascripts/graphs/stat_graph_contributors_graph.js.coffee
@@ -121,7 +121,11 @@ class @ContributorsMasterGraph extends ContributorsGraph
class @ContributorsAuthorGraph extends ContributorsGraph
constructor: (@data) ->
- @width = $('.content').width()/2 - 100
+ # Don't split graph size in half for mobile devices.
+ if $(window).width() < 768
+ @width = $('.content').width() - 80
+ else
+ @width = ($('.content').width() / 2) - 100
@height = 200
@x = null
@y = null
diff --git a/app/assets/javascripts/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee
index d0901be1509..6a108c033ea 100644
--- a/app/assets/javascripts/issuable.js.coffee
+++ b/app/assets/javascripts/issuable.js.coffee
@@ -68,12 +68,15 @@ issuable_created = false
Turbolinks.visit(issuesUrl);
initChecks: ->
+ @issuableBulkActions = $('.bulk-update').data('bulkActions')
+
$('.check_all_issues').off('click').on('click', ->
$('.selected_issue').prop('checked', @checked)
Issuable.checkChanged()
)
- $('.selected_issue').off('change').on('change', Issuable.checkChanged)
+ $('.selected_issue').off('change').on('change', Issuable.checkChanged.bind(@))
+
checkChanged: ->
checked_issues = $('.selected_issue:checked')
@@ -88,3 +91,6 @@ issuable_created = false
$('#update_issues_ids').val []
$('.issues_bulk_update').hide()
$('.issues-other-filters').show()
+ @issuableBulkActions.willUpdateLabels = false
+
+ return true
diff --git a/app/assets/javascripts/issue.js.coffee b/app/assets/javascripts/issue.js.coffee
index 157361404e0..f446aa49cde 100644
--- a/app/assets/javascripts/issue.js.coffee
+++ b/app/assets/javascripts/issue.js.coffee
@@ -99,7 +99,7 @@ class @Issue
# If the user doesn't have the required permissions the container isn't
# rendered at all.
- return unless $container
+ return if $container.length is 0
$.getJSON($container.data('path'))
.error ->
diff --git a/app/assets/javascripts/issue_status_select.js.coffee b/app/assets/javascripts/issue_status_select.js.coffee
index c5740f27ddd..ed50e2e698f 100644
--- a/app/assets/javascripts/issue_status_select.js.coffee
+++ b/app/assets/javascripts/issue_status_select.js.coffee
@@ -6,6 +6,13 @@ class @IssueStatusSelect
$(el).glDropdown(
selectable: true
fieldName: fieldName
+ toggleLabel: (selected, el, instance) =>
+ label = 'Author'
+ $item = instance.dropdown.find('.is-active')
+ label = $item.text() if $item.length
+ label
+ clicked: (item, $el, e)->
+ e.preventDefault()
id: (obj, el) ->
$(el).data("id")
)
diff --git a/app/assets/javascripts/issues-bulk-assignment.js.coffee b/app/assets/javascripts/issues-bulk-assignment.js.coffee
index b454f9389dd..6b0e69dbae7 100644
--- a/app/assets/javascripts/issues-bulk-assignment.js.coffee
+++ b/app/assets/javascripts/issues-bulk-assignment.js.coffee
@@ -7,6 +7,11 @@ class @IssuableBulkActions
@issues = @getElement('.issues-list .issue')
} = opts
+ # Save instance
+ @form.data 'bulkActions', @
+
+ @willUpdateLabels = false
+
@bindEvents()
# Fixes bulk-assign not working when navigating through pages
@@ -87,11 +92,12 @@ class @IssuableBulkActions
add_label_ids : []
remove_label_ids : []
- @getLabelsToApply().map (id) ->
- formData.update.add_label_ids.push id
+ if @willUpdateLabels
+ @getLabelsToApply().map (id) ->
+ formData.update.add_label_ids.push id
- @getLabelsToRemove().map (id) ->
- formData.update.remove_label_ids.push id
+ @getLabelsToRemove().map (id) ->
+ formData.update.remove_label_ids.push id
formData
diff --git a/app/assets/javascripts/labels_select.js.coffee b/app/assets/javascripts/labels_select.js.coffee
index 6a10db10eb1..e95fd96a83f 100644
--- a/app/assets/javascripts/labels_select.js.coffee
+++ b/app/assets/javascripts/labels_select.js.coffee
@@ -319,6 +319,8 @@ class @LabelsSelect
multiSelect: $dropdown.hasClass 'js-multiselect'
clicked: (label) ->
+ _this.enableBulkLabelDropdown()
+
if $dropdown.hasClass('js-filter-bulk-update')
return
@@ -377,3 +379,8 @@ class @LabelsSelect
label_ids.push $("#issue_#{issue_id}").data('labels')
_.intersection.apply _, label_ids
+
+ enableBulkLabelDropdown: ->
+ if $('.selected_issue:checked').length
+ issuableBulkActions = $('.bulk-update').data('bulkActions')
+ issuableBulkActions.willUpdateLabels = true
diff --git a/app/assets/javascripts/layout_nav.js.coffee b/app/assets/javascripts/layout_nav.js.coffee
index f8f0aea427e..f639f7f5892 100644
--- a/app/assets/javascripts/layout_nav.js.coffee
+++ b/app/assets/javascripts/layout_nav.js.coffee
@@ -3,11 +3,10 @@ hideEndFade = ($scrollingTabs) ->
$this = $(@)
$this
- .find('.fade-right')
- .toggleClass('end-scroll', $this.width() is $this.prop('scrollWidth'))
+ .siblings('.fade-right')
+ .toggleClass('scrolling', $this.width() < $this.prop('scrollWidth'))
$ ->
- $('.fade-left').addClass('end-scroll')
hideEndFade($('.scrolling-tabs'))
@@ -21,5 +20,5 @@ $ ->
currentPosition = $this.scrollLeft()
maxPosition = $this.prop('scrollWidth') - $this.outerWidth()
- $this.find('.fade-left').toggleClass('end-scroll', currentPosition is 0)
- $this.find('.fade-right').toggleClass('end-scroll', currentPosition is maxPosition)
+ $this.siblings('.fade-left').toggleClass('scrolling', currentPosition > 0)
+ $this.siblings('.fade-right').toggleClass('scrolling', currentPosition < maxPosition - 1)
diff --git a/app/assets/javascripts/lib/chart.js.coffee b/app/assets/javascripts/lib/chart.js.coffee
new file mode 100644
index 00000000000..82217fc5107
--- /dev/null
+++ b/app/assets/javascripts/lib/chart.js.coffee
@@ -0,0 +1 @@
+#= require Chart
diff --git a/app/assets/javascripts/lib/d3.js.coffee b/app/assets/javascripts/lib/d3.js.coffee
new file mode 100644
index 00000000000..74f0a0bb06a
--- /dev/null
+++ b/app/assets/javascripts/lib/d3.js.coffee
@@ -0,0 +1 @@
+#= require d3
diff --git a/app/assets/javascripts/lib/raphael.js.coffee b/app/assets/javascripts/lib/raphael.js.coffee
new file mode 100644
index 00000000000..ab8e5979b87
--- /dev/null
+++ b/app/assets/javascripts/lib/raphael.js.coffee
@@ -0,0 +1,3 @@
+#= require raphael
+#= require g.raphael
+#= require g.bar
diff --git a/app/assets/javascripts/lib/animate.js.coffee b/app/assets/javascripts/lib/utils/animate.js.coffee
index ec3b44d6126..ec3b44d6126 100644
--- a/app/assets/javascripts/lib/animate.js.coffee
+++ b/app/assets/javascripts/lib/utils/animate.js.coffee
diff --git a/app/assets/javascripts/lib/common_utils.js.coffee b/app/assets/javascripts/lib/utils/common_utils.js.coffee
index e39dcb2daa9..e39dcb2daa9 100644
--- a/app/assets/javascripts/lib/common_utils.js.coffee
+++ b/app/assets/javascripts/lib/utils/common_utils.js.coffee
diff --git a/app/assets/javascripts/lib/datetime_utility.js.coffee b/app/assets/javascripts/lib/utils/datetime_utility.js.coffee
index 948d6dbf07e..948d6dbf07e 100644
--- a/app/assets/javascripts/lib/datetime_utility.js.coffee
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js.coffee
diff --git a/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb b/app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb
index 80f9936b9c2..80f9936b9c2 100644
--- a/app/assets/javascripts/lib/emoji_aliases.js.coffee.erb
+++ b/app/assets/javascripts/lib/utils/emoji_aliases.js.coffee.erb
diff --git a/app/assets/javascripts/lib/jquery.timeago.js b/app/assets/javascripts/lib/utils/jquery.timeago.js
index cc17aa7d3d1..cc17aa7d3d1 100644
--- a/app/assets/javascripts/lib/jquery.timeago.js
+++ b/app/assets/javascripts/lib/utils/jquery.timeago.js
diff --git a/app/assets/javascripts/lib/md5.js b/app/assets/javascripts/lib/utils/md5.js
index b63716eaad2..b63716eaad2 100644
--- a/app/assets/javascripts/lib/md5.js
+++ b/app/assets/javascripts/lib/utils/md5.js
diff --git a/app/assets/javascripts/lib/notify.js.coffee b/app/assets/javascripts/lib/utils/notify.js.coffee
index 9e28353ac34..9e28353ac34 100644
--- a/app/assets/javascripts/lib/notify.js.coffee
+++ b/app/assets/javascripts/lib/utils/notify.js.coffee
diff --git a/app/assets/javascripts/lib/utils/text_utility.js.coffee b/app/assets/javascripts/lib/utils/text_utility.js.coffee
new file mode 100644
index 00000000000..bb2772dfed2
--- /dev/null
+++ b/app/assets/javascripts/lib/utils/text_utility.js.coffee
@@ -0,0 +1,79 @@
+((w) ->
+ w.gl ?= {}
+ w.gl.text ?= {}
+
+ gl.text.randomString = -> Math.random().toString(36).substring(7)
+
+ gl.text.replaceRange = (s, start, end, substitute) ->
+ s.substring(0, start) + substitute + s.substring(end);
+
+ gl.text.selectedText = (text, textarea) ->
+ text.substring(textarea.selectionStart, textarea.selectionEnd)
+
+ gl.text.insertText = (textArea, text, tag, selected, wrap) ->
+ selectedSplit = selected.split('\n')
+ startChar = if not wrap and textArea.selectionStart > 0 then '\n' else ''
+
+ if selectedSplit.length > 1 and not wrap
+ insertText = selectedSplit.map((val) ->
+ if val.indexOf(tag) is 0
+ "#{val.replace(tag, '')}"
+ else
+ "#{tag}#{val}"
+ ).join('\n')
+ else
+ insertText = "#{startChar}#{tag}#{selected}#{if wrap then tag else ' '}"
+
+ if document.queryCommandSupported('insertText')
+ document.execCommand 'insertText', false, insertText
+ else
+ try
+ document.execCommand("ms-beginUndoUnit")
+
+ textArea.value = @replaceRange(
+ text,
+ textArea.selectionStart,
+ textArea.selectionEnd,
+ insertText)
+ try
+ document.execCommand("ms-endUndoUnit")
+
+ @moveCursor(textArea, tag, wrap)
+
+ gl.text.moveCursor = (textArea, tag, wrapped) ->
+ return unless textArea.setSelectionRange
+
+ if textArea.selectionStart is textArea.selectionEnd
+ if wrapped
+ pos = textArea.selectionStart - tag.length
+ else
+ pos = textArea.selectionStart
+
+ textArea.setSelectionRange pos, pos
+
+ gl.text.updateText = (textArea, tag, wrap) ->
+ $textArea = $(textArea)
+ oldVal = $textArea.val()
+ textArea = $textArea.get(0)
+ text = $textArea.val()
+ selected = @selectedText(text, textArea)
+ $textArea.focus()
+
+ @insertText(textArea, text, tag, selected, wrap)
+
+ gl.text.init = (form) ->
+ self = @
+ $('.js-md', form)
+ .off 'click'
+ .on 'click', ->
+ $this = $(@)
+ self.updateText(
+ $this.closest('.md-area').find('textarea'),
+ $this.data('md-tag'),
+ not $this.data('md-prepend')
+ )
+
+ gl.text.removeListeners = (form) ->
+ $('.js-md', form).off()
+
+) window
diff --git a/app/assets/javascripts/lib/type_utility.js.coffee b/app/assets/javascripts/lib/utils/type_utility.js.coffee
index 957f0d86b36..957f0d86b36 100644
--- a/app/assets/javascripts/lib/type_utility.js.coffee
+++ b/app/assets/javascripts/lib/utils/type_utility.js.coffee
diff --git a/app/assets/javascripts/lib/url_utility.js.coffee b/app/assets/javascripts/lib/utils/url_utility.js.coffee
index e8085e1c2e4..e8085e1c2e4 100644
--- a/app/assets/javascripts/lib/url_utility.js.coffee
+++ b/app/assets/javascripts/lib/utils/url_utility.js.coffee
diff --git a/app/assets/javascripts/lib/utf8_encode.js b/app/assets/javascripts/lib/utils/utf8_encode.js
index 39ffe44dae0..39ffe44dae0 100644
--- a/app/assets/javascripts/lib/utf8_encode.js
+++ b/app/assets/javascripts/lib/utils/utf8_encode.js
diff --git a/app/assets/javascripts/milestone.js.coffee b/app/assets/javascripts/milestone.js.coffee
index 0037a3a21c2..a19e68b39e2 100644
--- a/app/assets/javascripts/milestone.js.coffee
+++ b/app/assets/javascripts/milestone.js.coffee
@@ -4,18 +4,10 @@ class @Milestone
type: "PUT"
url: issue_url
data: data
- success: (data) ->
- if data.saved == true
- if data.assignee_avatar_url
- img_tag = $('<img/>')
- img_tag.attr('src', data.assignee_avatar_url)
- img_tag.addClass('avatar s16')
- $(li).find('.assignee-icon').html(img_tag)
- else
- $(li).find('.assignee-icon').html('')
- $(li).effect 'highlight'
- else
- new Flash("Issue update failed", 'alert')
+ success: (_data) =>
+ @successCallback(_data, li)
+ error: (data) ->
+ new Flash("Issue update failed", 'alert')
dataType: "json"
@sortIssues: (data) ->
@@ -25,9 +17,10 @@ class @Milestone
type: "PUT"
url: sort_issues_url
data: data
- success: (data) ->
- if data.saved != true
- new Flash("Issues update failed", 'alert')
+ success: (_data) =>
+ @successCallback(_data)
+ error: ->
+ new Flash("Issues update failed", 'alert')
dataType: "json"
@sortMergeRequests: (data) ->
@@ -37,9 +30,10 @@ class @Milestone
type: "PUT"
url: sort_mr_url
data: data
- success: (data) ->
- if data.saved != true
- new Flash("MR update failed", 'alert')
+ success: (_data) =>
+ @successCallback(_data)
+ error: (data) ->
+ new Flash("Issue update failed", 'alert')
dataType: "json"
@updateMergeRequest: (li, merge_request_url, data) ->
@@ -47,20 +41,23 @@ class @Milestone
type: "PUT"
url: merge_request_url
data: data
- success: (data) ->
- if data.saved == true
- if data.assignee_avatar_url
- img_tag = $('<img/>')
- img_tag.attr('src', data.assignee_avatar_url)
- img_tag.addClass('avatar s16')
- $(li).find('.assignee-icon').html(img_tag)
- else
- $(li).find('.assignee-icon').html('')
- $(li).effect 'highlight'
- else
- new Flash("Issue update failed", 'alert')
+ success: (_data) =>
+ @successCallback(_data, li)
+ error: (data) ->
+ new Flash("Issue update failed", 'alert')
dataType: "json"
+ @successCallback: (data, element) =>
+ if data.assignee
+ img_tag = $('<img/>')
+ img_tag.attr('src', data.assignee.avatar_url)
+ img_tag.addClass('avatar s16')
+ $(element).find('.assignee-icon').html(img_tag)
+ else
+ $(element).find('.assignee-icon').html('')
+
+ $(element).effect 'highlight'
+
constructor: ->
oldMouseStart = $.ui.sortable.prototype._mouseStart
$.ui.sortable.prototype._mouseStart = (event, overrideHandle, noActivation) ->
@@ -81,8 +78,10 @@ class @Milestone
stop: (event, ui) ->
$(".issues-sortable-list").css "min-height", "0px"
update: (event, ui) ->
- data = $(this).sortable("serialize")
- Milestone.sortIssues(data)
+ # Prevents sorting from container which element has been removed.
+ if $(this).find(ui.item).length > 0
+ data = $(this).sortable("serialize")
+ Milestone.sortIssues(data)
receive: (event, ui) ->
new_state = $(this).data('state')
diff --git a/app/assets/javascripts/network/application.js.coffee b/app/assets/javascripts/network/application.js.coffee
index cb9eead855b..f75f63869c5 100644
--- a/app/assets/javascripts/network/application.js.coffee
+++ b/app/assets/javascripts/network/application.js.coffee
@@ -4,9 +4,6 @@
# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
# the compiled file.
#
-#= require raphael
-#= require g.raphael
-#= require g.bar
#= require_tree .
$ ->
diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee
index e2d3241437b..17f7e180127 100644
--- a/app/assets/javascripts/notes.js.coffee
+++ b/app/assets/javascripts/notes.js.coffee
@@ -102,12 +102,15 @@ class @Notes
keydownNoteText: (e) ->
$this = $(this)
- if $this.val() is '' and e.which is 38 #aka the up key
+ if $this.val() is '' and e.which is 38 and not isMetaKey e
myLastNote = $("li.note[data-author-id='#{gon.current_user_id}'][data-editable]:last")
if myLastNote.length
myLastNoteEditBtn = myLastNote.find('.js-note-edit')
myLastNoteEditBtn.trigger('click', [true, myLastNote])
+ isMetaKey = (e) ->
+ (e.metaKey or e.ctrlKey or e.altKey or e.shiftKey)
+
initRefresh: ->
clearInterval(Notes.interval)
Notes.interval = setInterval =>
diff --git a/app/assets/javascripts/notifications_dropdown.js.coffee b/app/assets/javascripts/notifications_dropdown.js.coffee
index 74d2298c1fa..0bbd082c156 100644
--- a/app/assets/javascripts/notifications_dropdown.js.coffee
+++ b/app/assets/javascripts/notifications_dropdown.js.coffee
@@ -1,5 +1,5 @@
class @NotificationsDropdown
- $ ->
+ constructor: ->
$(document)
.off 'click', '.update-notification'
.on 'click', '.update-notification', (e) ->
@@ -18,7 +18,8 @@ class @NotificationsDropdown
.off 'ajax:success', '.notification-form'
.on 'ajax:success', '.notification-form', (e, data) ->
if data.saved
- new Flash('Notification settings saved', 'notice')
- $(e.currentTarget).closest('.notification-dropdown').replaceWith(data.html)
+ $(e.currentTarget)
+ .closest('.notification-dropdown')
+ .replaceWith(data.html)
else
new Flash('Failed to save new settings', 'alert')
diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee
index d12bad97a05..3288c801388 100644
--- a/app/assets/javascripts/project.js.coffee
+++ b/app/assets/javascripts/project.js.coffee
@@ -19,6 +19,7 @@ class @Project
$('.clone').text(url)
# Ref switcher
+ @initRefSwitcher()
$('.project-refs-select').on 'change', ->
$(@).parents('form').submit()
@@ -34,7 +35,6 @@ class @Project
$(@).parents('.no-password-message').remove()
e.preventDefault()
-
@projectSelectDropdown()
projectSelectDropdown: ->
@@ -50,3 +50,42 @@ class @Project
changeProject: (url) ->
window.location = url
+
+ initRefSwitcher: ->
+ $('.js-project-refs-dropdown').each ->
+ $dropdown = $(@)
+ selected = $dropdown.data('selected')
+
+ $dropdown.glDropdown(
+ data: (term, callback) ->
+ $.ajax(
+ url: $dropdown.data('refs-url')
+ data:
+ ref: $dropdown.data('ref')
+ ).done (refs) ->
+ callback(refs)
+ selectable: true
+ filterable: true
+ filterByText: true
+ fieldName: 'ref'
+ renderRow: (ref) ->
+ if ref.header?
+ $('<li />')
+ .addClass('dropdown-header')
+ .text(ref.header)
+ else
+ link = $('<a />')
+ .attr('href', '#')
+ .addClass(if ref is selected then 'is-active' else '')
+ .text(ref)
+ .attr('data-ref', escape(ref))
+
+ $('<li />')
+ .append(link)
+ id: (obj, $el) ->
+ $el.attr('data-ref')
+ toggleLabel: (obj, $el) ->
+ $el.text().trim()
+ clicked: (e) ->
+ $dropdown.closest('form').submit()
+ )
diff --git a/app/assets/javascripts/users/application.js.coffee b/app/assets/javascripts/users/application.js.coffee
index 647ffbf5f45..91cacfece46 100644
--- a/app/assets/javascripts/users/application.js.coffee
+++ b/app/assets/javascripts/users/application.js.coffee
@@ -1,8 +1,2 @@
-# This is a manifest file that'll be compiled into including all the files listed below.
-# Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
-# be included in the compiled file accessible from http://example.com/assets/application.js
-# It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
-# the compiled file.
#
-#= require d3
#= require_tree .
diff --git a/app/assets/stylesheets/framework.scss b/app/assets/stylesheets/framework.scss
index 3cbddc59f11..a306b8f3f29 100644
--- a/app/assets/stylesheets/framework.scss
+++ b/app/assets/stylesheets/framework.scss
@@ -37,3 +37,4 @@
@import "framework/timeline.scss";
@import "framework/typography.scss";
@import "framework/zen.scss";
+@import "framework/blank";
diff --git a/app/assets/stylesheets/framework/blank.scss b/app/assets/stylesheets/framework/blank.scss
new file mode 100644
index 00000000000..40b5171a8c6
--- /dev/null
+++ b/app/assets/stylesheets/framework/blank.scss
@@ -0,0 +1,23 @@
+.blank-state {
+ padding-top: 20px;
+ padding-bottom: 20px;
+ text-align: center;
+}
+
+.blank-state-no-icon {
+ padding-top: 40px;
+ padding-bottom: 40px;
+}
+
+.blank-state-title {
+ margin-top: 0;
+ margin-bottom: 5px;
+ font-size: 19px;
+ font-weight: normal;
+}
+
+.blank-state-text {
+ margin-top: 0;
+ margin-bottom: $gl-padding;
+ font-size: 15px;
+}
diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss
index d5fe5bc2ef1..38023818709 100644
--- a/app/assets/stylesheets/framework/blocks.scss
+++ b/app/assets/stylesheets/framework/blocks.scss
@@ -97,6 +97,22 @@
}
}
+.sub-header-block {
+ background-color: $white-light;
+ border-bottom: 1px solid $white-dark;
+ padding: 11px 0;
+ margin-bottom: 11px;
+
+ .oneline {
+ line-height: 35px;
+ }
+
+ &.no-bottom-space {
+ border-bottom: 0;
+ margin-bottom: 0;
+ }
+}
+
.cover-block {
text-align: center;
background: $background-color;
diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss
index d4d579a083d..00111dfa706 100644
--- a/app/assets/stylesheets/framework/dropdowns.scss
+++ b/app/assets/stylesheets/framework/dropdowns.scss
@@ -461,10 +461,12 @@
}
}
- .ui-state-active,
- .ui-state-hover {
- color: $md-link-color;
- background-color: $calendar-hover-bg;
+ .ui-datepicker-calendar {
+ .ui-state-hover,
+ .ui-state-active {
+ color: #fff;
+ border: 0;
+ }
}
.ui-datepicker-prev,
diff --git a/app/assets/stylesheets/framework/header.scss b/app/assets/stylesheets/framework/header.scss
index a7bcb456560..c32ce5195c6 100644
--- a/app/assets/stylesheets/framework/header.scss
+++ b/app/assets/stylesheets/framework/header.scss
@@ -26,7 +26,6 @@ header {
text-align: center;
#tanuki-logo, img {
- width: 36px;
height: 36px;
}
}
@@ -132,6 +131,10 @@ header {
transition-duration: .3s;
z-index: 999;
+ svg, img {
+ height: 36px;
+ }
+
&:hover {
cursor: pointer;
}
diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss
index fd885b38680..fd8eaa8a691 100644
--- a/app/assets/stylesheets/framework/markdown_area.scss
+++ b/app/assets/stylesheets/framework/markdown_area.scss
@@ -65,6 +65,11 @@
a {
padding-top: 0;
line-height: 1;
+ border-bottom: 1px solid $border-color;
+
+ &.btn.btn-xs {
+ padding: 2px 5px;
+ }
}
}
}
@@ -97,5 +102,30 @@
white-space: pre-wrap;
word-break: keep-all;
}
+
+ @include bulleted-list;
+ }
+}
+
+.toolbar-group {
+ float: left;
+ margin-right: -5px;
+ margin-left: $gl-padding;
+
+ &:first-child {
+ margin-left: 0;
+ }
+}
+
+.toolbar-btn {
+ float: left;
+ padding: 0 5px;
+ color: #959494;
+ background: transparent;
+ border: 0;
+ outline: 0;
+
+ &:hover {
+ color: $gl-link-color;
}
}
diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss
index 828e7224231..5ec5a96a597 100644
--- a/app/assets/stylesheets/framework/mixins.scss
+++ b/app/assets/stylesheets/framework/mixins.scss
@@ -110,3 +110,17 @@
font-size: 16px;
line-height: 24px;
}
+
+@mixin bulleted-list {
+ > ul {
+ list-style-type: disc;
+
+ ul {
+ list-style-type: circle;
+
+ ul {
+ list-style-type: square;
+ }
+ }
+ }
+} \ No newline at end of file
diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss
index a55918f8711..6211bc04597 100644
--- a/app/assets/stylesheets/framework/nav.scss
+++ b/app/assets/stylesheets/framework/nav.scss
@@ -1,6 +1,6 @@
@mixin fade($gradient-direction, $rgba, $gradient-color) {
- visibility: visible;
- opacity: 1;
+ visibility: hidden;
+ opacity: 0;
z-index: 2;
position: absolute;
bottom: 12px;
@@ -13,11 +13,18 @@
background: -moz-linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
background: linear-gradient($gradient-direction, $rgba, $gradient-color 45%);
- &.end-scroll {
- visibility: hidden;
- opacity: 0;
+ &.scrolling {
+ visibility: visible;
+ opacity: 1;
transition-duration: .3s;
}
+
+ .fa {
+ position: relative;
+ top: 3px;
+ font-size: 13px;
+ color: $btn-placeholder-gray;
+ }
}
@mixin scrolling-links() {
@@ -25,6 +32,7 @@
overflow-x: auto;
overflow-y: hidden;
-webkit-overflow-scrolling: touch;
+
&::-webkit-scrollbar {
display: none;
}
@@ -104,10 +112,6 @@
width: 50%;
line-height: 28px;
- &.wiki-page {
- padding: 16px 10px 11px;
- }
-
/* Small devices (phones, tablets, 768px and lower) */
@media (max-width: $screen-sm-min) {
width: 100%;
@@ -136,7 +140,7 @@
}
/* Small devices (phones, tablets, 768px and lower) */
- @media (max-width: $screen-sm-max) {
+ @media (max-width: $screen-xs-max) {
width: 100%;
}
}
@@ -220,6 +224,7 @@
form {
display: block;
height: auto;
+ margin-bottom: 14px;
input {
width: 100%;
@@ -268,7 +273,7 @@
float: right;
padding: 7px 0 0;
- @media (max-width: $screen-xs-max) {
+ @media (max-width: $screen-sm-max) {
display: none;
}
@@ -299,33 +304,9 @@
}
.nav-links {
- @include scrolling-links();
border-bottom: none;
height: 51px;
- svg {
- position: relative;
- top: 2px;
- margin-right: 2px;
- height: 15px;
- width: auto;
-
- path,
- polygon {
- fill: $layout-link-gray;
- }
- }
-
- .fade-right {
- @include fade(left, rgba(250, 250, 250, 0.4), $background-color);
- right: 0;
- }
-
- .fade-left {
- @include fade(right, rgba(250, 250, 250, 0.4), $background-color);
- left: 0;
- }
-
li {
a {
@@ -361,18 +342,6 @@
}
}
}
-
- .nav-control {
-
- .fade-right {
- @media (min-width: $screen-xs-max) {
- right: 68px;
- }
- @media (max-width: $screen-xs-min) {
- right: 0;
- }
- }
- }
}
.scrolling-tabs-container {
@@ -380,15 +349,42 @@
.nav-links {
@include scrolling-links();
+ }
+
+ .fade-right {
+ @include fade(left, rgba(255, 255, 255, 0.4), $background-color);
+ right: -5px;
+
+ .fa {
+ right: -7px;
+ }
+ }
+
+ .fade-left {
+ @include fade(right, rgba(255, 255, 255, 0.4), $background-color);
+ left: -5px;
+
+ .fa {
+ left: -7px;
+ }
+ }
+
+ &.sub-nav-scroll {
.fade-right {
- @include fade(left, rgba(255, 255, 255, 0.4), $background-color);
right: 0;
+
+ .fa {
+ right: -23px;
+ }
}
.fade-left {
- @include fade(right, rgba(255, 255, 255, 0.4), $background-color);
left: 0;
+
+ .fa {
+ left: 10px;
+ }
}
}
}
@@ -401,21 +397,19 @@
.fade-right {
@include fade(left, rgba(255, 255, 255, 0.4), $white-light);
- right: 0;
+ right: -5px;
+
+ .fa {
+ right: -7px;
+ }
}
.fade-left {
@include fade(right, rgba(255, 255, 255, 0.4), $white-light);
- left: 0;
- }
-
- &.event-filter {
- .fade-right {
- visibility: hidden;
+ left: -5px;
- @media (max-width: $screen-xs-max) {
- visibility: visible;
- }
+ .fa {
+ left: -7px;
}
}
}
diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss
index ae7bdf14c40..874416e1007 100644
--- a/app/assets/stylesheets/framework/panels.scss
+++ b/app/assets/stylesheets/framework/panels.scss
@@ -9,6 +9,10 @@
margin-top: -2px;
float: right;
}
+
+ .dropdown-menu-toggle {
+ line-height: 20px;
+ }
}
.panel-body {
diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss
index f242706ebe4..21d87cc9d34 100644
--- a/app/assets/stylesheets/framework/selects.scss
+++ b/app/assets/stylesheets/framework/selects.scss
@@ -165,11 +165,6 @@
background-size: 16px 16px !important;
}
-/** Branch/tag selector **/
-.project-refs-form .select2-container {
- width: 160px !important;
-}
-
.select2-results .select2-no-results,
.select2-results .select2-searching,
.select2-results .select2-ajax-error,
diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss
index a0bb3427af0..98f917ce69b 100644
--- a/app/assets/stylesheets/framework/sidebar.scss
+++ b/app/assets/stylesheets/framework/sidebar.scss
@@ -91,7 +91,6 @@
text-decoration: none;
font-weight: normal;
outline: none;
- white-space: nowrap;
&:hover,
&:active,
diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss
index 761e33f0df7..de534d28421 100644
--- a/app/assets/stylesheets/pages/commits.scss
+++ b/app/assets/stylesheets/pages/commits.scss
@@ -80,9 +80,14 @@
.commit {
padding: 10px 0;
+ position: relative;
@media (min-width: $screen-sm-min) {
- padding-left: 46px;
+ padding-left: 20px;
+
+ .commit-info-block {
+ padding-left: 44px;
+ }
}
&:not(:last-child) {
@@ -95,8 +100,11 @@
vertical-align: baseline;
}
+
.avatar {
- margin-left: -46px;
+ position: absolute;
+ top: 10px;
+ left: 16px;
}
.item-title {
diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss
index 1a7d5f9666e..5286b73cc50 100644
--- a/app/assets/stylesheets/pages/diff.scss
+++ b/app/assets/stylesheets/pages/diff.scss
@@ -4,6 +4,11 @@
margin-bottom: $gl-padding;
border-radius: 3px;
+ .commit-short-id {
+ font-family: $regular_font;
+ font-weight: 400;
+ }
+
.diff-header {
position: relative;
background: $background-color;
diff --git a/app/assets/stylesheets/pages/editor.scss b/app/assets/stylesheets/pages/editor.scss
index a34b06f1054..1aa4e06d975 100644
--- a/app/assets/stylesheets/pages/editor.scss
+++ b/app/assets/stylesheets/pages/editor.scss
@@ -60,13 +60,14 @@
.encoding-selector,
.license-selector,
- .gitignore-selector {
+ .gitignore-selector,
+ .gitlab-ci-yml-selector {
display: inline-block;
vertical-align: top;
font-family: $regular_font;
}
- .gitignore-selector, .license-selector {
+ .gitignore-selector, .license-selector, .gitlab-ci-yml-selector {
.dropdown {
line-height: 21px;
}
@@ -76,4 +77,10 @@
width: 220px;
}
}
+
+ .gitlab-ci-yml-selector {
+ .dropdown-menu-toggle {
+ width: 250px;
+ }
+ }
}
diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss
index 6c36f603daf..a2145956eb5 100644
--- a/app/assets/stylesheets/pages/events.scss
+++ b/app/assets/stylesheets/pages/events.scss
@@ -54,6 +54,10 @@
}
}
+ code {
+ white-space: pre-wrap;
+ }
+
pre {
border: none;
background: #f9f9f9;
diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss
index 4a95b7b852e..00ab42bec5c 100644
--- a/app/assets/stylesheets/pages/help.scss
+++ b/app/assets/stylesheets/pages/help.scss
@@ -57,4 +57,12 @@
.documentation {
padding: 7px;
+
+ // Border around images in the help pages.
+ img:not(.emoji) {
+ border: 1px solid $table-border-gray;
+ padding: 5px;
+ margin: 5px;
+ max-height: calc(100vh - 100px);
+ }
}
diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss
index 687117233f6..542fa244689 100644
--- a/app/assets/stylesheets/pages/issuable.scss
+++ b/app/assets/stylesheets/pages/issuable.scss
@@ -4,6 +4,14 @@
margin-right: 1px;
}
}
+
+ // Border around images in issue and MR descriptions.
+ .description img:not(.emoji) {
+ border: 1px solid $table-border-gray;
+ padding: 5px;
+ margin: 5px;
+ max-height: calc(100vh - 100px);
+ }
}
.issuable-filter-count {
diff --git a/app/assets/stylesheets/pages/labels.scss b/app/assets/stylesheets/pages/labels.scss
index 046c38aba44..47bfd144930 100644
--- a/app/assets/stylesheets/pages/labels.scss
+++ b/app/assets/stylesheets/pages/labels.scss
@@ -6,6 +6,7 @@
height: 30px;
display: inline-block;
margin-right: 10px;
+ margin-bottom: 10px;
}
&.suggest-colors-dropdown {
@@ -50,11 +51,10 @@
.label-row {
.label-name {
- display: block;
+ display: inline-block;
margin-bottom: 10px;
@media (min-width: $screen-sm-min) {
- display: inline-block;
width: 200px;
margin-bottom: 0;
}
@@ -63,6 +63,7 @@
.label-description {
display: block;
margin-bottom: 10px;
+ margin-left: 50px;
@media (min-width: $screen-sm-min) {
display: inline-block;
diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss
index e67271adfb1..aca82f7f7bf 100644
--- a/app/assets/stylesheets/pages/merge_requests.scss
+++ b/app/assets/stylesheets/pages/merge_requests.scss
@@ -119,7 +119,12 @@
margin-bottom: 0;
}
- @media (max-width: $screen-sm-max) {
+ .btn-grouped {
+ margin-left: 0;
+ margin-right: 7px;
+ }
+
+ @media (max-width: $screen-xs-max) {
h4 {
font-size: 15px;
}
@@ -131,10 +136,14 @@
.btn,
.btn-group,
.accept-action {
- width: 100%;
margin-bottom: 4px;
}
+ .accept-action {
+ width: 100%;
+ text-align: center;
+ }
+
.accept-control {
width: 100%;
text-align: center;
@@ -284,7 +293,7 @@
margin-bottom: 0;
}
- @media (min-width: $screen-sm-min) {
+ @media (min-width: $screen-xs-min) {
float: left;
width: 50%;
margin-bottom: 0;
diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss
index 577dddae741..3784010348a 100644
--- a/app/assets/stylesheets/pages/note_form.scss
+++ b/app/assets/stylesheets/pages/note_form.scss
@@ -179,6 +179,10 @@
border-top: 1px solid $border-color;
}
+.md-helper {
+ padding-top: 10px;
+}
+
.toolbar-button {
padding: 0;
background: none;
@@ -219,3 +223,16 @@
float: left;
}
}
+
+.note-form-actions {
+ @media (max-width: $screen-xs-max) {
+ .btn {
+ float: none;
+ width: 100%;
+
+ &:not(:last-child) {
+ margin-bottom: 10px;
+ }
+ }
+ }
+}
diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss
index 35d728aec83..ee7c98f805b 100644
--- a/app/assets/stylesheets/pages/notes.scss
+++ b/app/assets/stylesheets/pages/notes.scss
@@ -84,24 +84,14 @@ ul.notes {
word-wrap: break-word;
@include md-typography;
+ // Reset ul style types since we're nested inside a ul already
+ @include bulleted-list;
+
// On diffs code should wrap nicely and not overflow
code {
white-space: pre-wrap;
}
- // Reset ul style types since we're nested inside a ul already
- & > ul {
- list-style-type: disc;
-
- ul {
- list-style-type: circle;
-
- ul {
- list-style-type: square;
- }
- }
- }
-
ul.task-list {
ul:not(.task-list) {
padding-left: 1.3em;
@@ -117,6 +107,14 @@ ul.notes {
code {
word-break: keep-all;
}
+
+ // Border around images in issue and MR comments.
+ img:not(.emoji) {
+ border: 1px solid $table-border-gray;
+ padding: 5px;
+ margin: 5px 0;
+ max-height: calc(100vh - 100px);
+ }
}
}
diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss
index f138a2f5387..d3e59d7fdb9 100644
--- a/app/assets/stylesheets/pages/projects.scss
+++ b/app/assets/stylesheets/pages/projects.scss
@@ -101,7 +101,8 @@
.notifications-btn {
- .fa-bell {
+ .fa-bell,
+ .fa-spinner {
margin-right: 6px;
}
@@ -373,7 +374,7 @@ a.deploy-project-label {
.project-stats {
margin-top: $gl-padding;
margin-bottom: 0;
- padding: 16px 0;
+ padding: 0;
background-color: $white-light;
font-size: 0;
@@ -382,13 +383,14 @@ a.deploy-project-label {
}
.nav li {
- display: inline;
+ display: inline-block;
+ margin: 16px 0;
+ margin-right: 16px;
}
.nav > li > a {
background-color: transparent;
- margin-right: 12px;
- padding: 0 10px;
+ padding: 5px 10px;
font-size: 15px;
color: $notes-light-color;
}
@@ -402,12 +404,17 @@ a.deploy-project-label {
font-size: 17px;
}
- li.missing a {
- color: #5a6069;
- border: 1px dashed #dce0e5;
+ li.missing {
+ border: 1px dashed $border-gray-light;
+ border-radius: $border-radius-default;
+
+ a {
+ color: $notes-light-color;
+ display: block;
+ }
&:hover {
- background-color: #f0f2f5;
+ background-color: $gray-normal;
}
}
@@ -616,3 +623,9 @@ pre.light-well {
color: $gl-text-green;
}
}
+
+.project-refs-form {
+ .dropdown-menu {
+ width: 300px;
+ }
+}
diff --git a/app/assets/stylesheets/pages/stat_graph.scss b/app/assets/stylesheets/pages/stat_graph.scss
index 85a0304196c..69288b31cc4 100644
--- a/app/assets/stylesheets/pages/stat_graph.scss
+++ b/app/assets/stylesheets/pages/stat_graph.scss
@@ -14,24 +14,38 @@
font-size: 10px;
}
+#contributors-master {
+ @include make-md-column(12);
+
+ svg {
+ width: 100%;
+ }
+}
+
#contributors {
.contributors-list {
margin: 0 0 10px;
list-style: none;
padding: 0;
+
+ svg {
+ width: 100%;
+ }
}
.person {
- &:nth-child(even) {
- float: right;
- }
- float: left;
+ @include make-md-column(6);
margin-top: 10px;
+
+ @media (max-width: $screen-sm-min) {
+ width: 100%;
+ }
}
.person .spark {
display: block;
background: #f3f3f3;
+ width: 100%;
}
.person .area-contributor {
diff --git a/app/assets/stylesheets/pages/todos.scss b/app/assets/stylesheets/pages/todos.scss
index afc00a68572..cf16d070cfe 100644
--- a/app/assets/stylesheets/pages/todos.scss
+++ b/app/assets/stylesheets/pages/todos.scss
@@ -62,6 +62,10 @@
}
}
+ code {
+ white-space: pre-wrap;
+ }
+
pre {
border: none;
background: #f9f9f9;
diff --git a/app/controllers/admin/appearances_controller.rb b/app/controllers/admin/appearances_controller.rb
index 26cf74e4849..4b0ec54b3f4 100644
--- a/app/controllers/admin/appearances_controller.rb
+++ b/app/controllers/admin/appearances_controller.rb
@@ -5,6 +5,7 @@ class Admin::AppearancesController < Admin::ApplicationController
end
def preview
+ render 'preview', layout: 'devise'
end
def create
diff --git a/app/controllers/admin/runner_projects_controller.rb b/app/controllers/admin/runner_projects_controller.rb
index d25619d94e0..bf20c5305a7 100644
--- a/app/controllers/admin/runner_projects_controller.rb
+++ b/app/controllers/admin/runner_projects_controller.rb
@@ -1,15 +1,14 @@
class Admin::RunnerProjectsController < Admin::ApplicationController
before_action :project, only: [:create]
- def index
- @runner_projects = project.runner_projects.all
- @runner_project = project.runner_projects.new
- end
-
def create
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
- if @runner.assign_to(@project, current_user)
+ return head(403) if @runner.is_shared? || @runner.locked?
+
+ runner_project = @runner.assign_to(@project, current_user)
+
+ if runner_project.persisted?
redirect_to admin_runner_path(@runner)
else
redirect_to admin_runner_path(@runner), alert: 'Failed adding runner to project'
diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb
index dd1bc6f5d52..9cc31620d9f 100644
--- a/app/controllers/application_controller.rb
+++ b/app/controllers/application_controller.rb
@@ -36,6 +36,10 @@ class ApplicationController < ActionController::Base
render_404
end
+ rescue_from Gitlab::Access::AccessDeniedError do |exception|
+ render_403
+ end
+
def redirect_back_or_default(default: root_path, options: {})
redirect_to request.referer.present? ? :back : default, options
end
diff --git a/app/controllers/concerns/membership_actions.rb b/app/controllers/concerns/membership_actions.rb
index a24273fad0b..52dc396af6a 100644
--- a/app/controllers/concerns/membership_actions.rb
+++ b/app/controllers/concerns/membership_actions.rb
@@ -21,29 +21,18 @@ module MembershipActions
def leave
@member = membershipable.members.find_by(user_id: current_user)
- return render_403 unless @member
+ Members::DestroyService.new(@member, current_user).execute
source_type = @member.real_source_type.humanize(capitalize: false)
-
- if can?(current_user, action_member_permission(:destroy, @member), @member)
- notice =
- if @member.request?
- "Your access request to the #{source_type} has been withdrawn."
- else
- "You left the \"#{@member.source.human_name}\" #{source_type}."
- end
- @member.destroy
-
- redirect_to [:dashboard, @member.real_source_type.tableize], notice: notice
- else
- if cannot_leave?
- alert = "You can not leave the \"#{@member.source.human_name}\" #{source_type}."
- alert << " Transfer or delete the #{source_type}."
- redirect_to polymorphic_url(membershipable), alert: alert
+ notice =
+ if @member.request?
+ "Your access request to the #{source_type} has been withdrawn."
else
- render_403
+ "You left the \"#{@member.source.human_name}\" #{source_type}."
end
- end
+ redirect_path = @member.request? ? @member.source : [:dashboard, @member.real_source_type.tableize]
+
+ redirect_to redirect_path, notice: notice
end
protected
@@ -51,8 +40,4 @@ module MembershipActions
def membershipable
raise NotImplementedError
end
-
- def cannot_leave?
- raise NotImplementedError
- end
end
diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb
index d0f2e2949f0..2c49fe3833e 100644
--- a/app/controllers/groups/group_members_controller.rb
+++ b/app/controllers/groups/group_members_controller.rb
@@ -36,9 +36,7 @@ class Groups::GroupMembersController < Groups::ApplicationController
def destroy
@group_member = @group.group_members.find(params[:id])
- return render_403 unless can?(current_user, :destroy_group_member, @group_member)
-
- @group_member.destroy
+ Members::DestroyService.new(@group_member, current_user).execute
respond_to do |format|
format.html { redirect_to group_group_members_path(@group), notice: 'User was successfully removed from group.' }
@@ -68,8 +66,4 @@ class Groups::GroupMembersController < Groups::ApplicationController
# MembershipActions concern
alias_method :membershipable, :group
-
- def cannot_leave?
- @group.last_owner?(current_user)
- end
end
diff --git a/app/controllers/import/gitlab_projects_controller.rb b/app/controllers/import/gitlab_projects_controller.rb
index f99aa490d3e..513348c39af 100644
--- a/app/controllers/import/gitlab_projects_controller.rb
+++ b/app/controllers/import/gitlab_projects_controller.rb
@@ -12,9 +12,13 @@ class Import::GitlabProjectsController < Import::BaseController
return redirect_back_or_default(options: { alert: "You need to upload a GitLab project export archive." })
end
+ imported_file = project_params[:file].path + "-import"
+
+ FileUtils.copy_entry(project_params[:file].path, imported_file)
+
@project = Gitlab::ImportExport::ProjectCreator.new(project_params[:namespace_id],
current_user,
- File.expand_path(project_params[:file].path),
+ File.expand_path(imported_file),
project_params[:path]).execute
if @project.saved?
diff --git a/app/controllers/projects/application_controller.rb b/app/controllers/projects/application_controller.rb
index 776ba92c9ab..996909a28c6 100644
--- a/app/controllers/projects/application_controller.rb
+++ b/app/controllers/projects/application_controller.rb
@@ -74,7 +74,7 @@ class Projects::ApplicationController < ApplicationController
end
def require_branch_head
- unless @repository.branch_names.include?(@ref)
+ unless @repository.branch_exists?(@ref)
redirect_to(
namespace_project_tree_path(@project.namespace, @project, @ref),
notice: "This action is not allowed unless you are on a branch"
diff --git a/app/controllers/projects/blob_controller.rb b/app/controllers/projects/blob_controller.rb
index cd8b2911674..7599fec3cdf 100644
--- a/app/controllers/projects/blob_controller.rb
+++ b/app/controllers/projects/blob_controller.rb
@@ -16,6 +16,7 @@ class Projects::BlobController < Projects::ApplicationController
before_action :from_merge_request, only: [:edit, :update]
before_action :require_branch_head, only: [:edit, :update]
before_action :editor_variables, except: [:show, :preview, :diff]
+ before_action :validate_diff_params, only: :diff
def new
commit unless @repository.empty?
@@ -146,4 +147,10 @@ class Projects::BlobController < Projects::ApplicationController
file_content_encoding: params[:encoding]
}
end
+
+ def validate_diff_params
+ if [:since, :to, :offset].any? { |key| params[key].blank? }
+ render nothing: true
+ end
+ end
end
diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb
index 4e2d3bebb2e..8b8df680739 100644
--- a/app/controllers/projects/issues_controller.rb
+++ b/app/controllers/projects/issues_controller.rb
@@ -62,8 +62,12 @@ class Projects::IssuesController < Projects::ApplicationController
end
def show
+ raw_notes = @issue.notes_with_associations.fresh
+
+ @notes = Banzai::NoteRenderer.
+ render(raw_notes, @project, current_user, @path, @project_wiki, @ref)
+
@note = @project.notes.new(noteable: @issue)
- @notes = @issue.notes.with_associations.fresh
@noteable = @issue
respond_to do |format|
@@ -111,6 +115,7 @@ class Projects::IssuesController < Projects::ApplicationController
render :edit
end
end
+
format.json do
render json: @issue.to_json(include: { milestone: {}, assignee: { methods: :avatar_url }, labels: { methods: :text_color } })
end
diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb
index 851822d805a..39c8ba40ca2 100644
--- a/app/controllers/projects/merge_requests_controller.rb
+++ b/app/controllers/projects/merge_requests_controller.rb
@@ -85,6 +85,15 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@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|
format.html
format.json { render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } }
@@ -190,7 +199,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def merge
return access_denied! unless @merge_request.can_be_merged_by?(current_user)
- unless @merge_request.mergeable?
+ # Disable the CI check if merge_when_build_succeeds is enabled since we have
+ # to wait until CI completes to know
+ unless @merge_request.mergeable?(skip_ci_check: merge_when_build_succeeds_active?)
@status = :failed
return
end
@@ -204,8 +215,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController
@merge_request.update(merge_error: nil)
- if params[:merge_when_build_succeeds].present?
- if @merge_request.pipeline && @merge_request.pipeline.active?
+ if params[:merge_when_build_succeeds].present?
+ unless @merge_request.pipeline
+ @status = :failed
+ return
+ end
+
+ if @merge_request.pipeline.active?
MergeRequests::MergeWhenBuildSucceedsService.new(@project, current_user, merge_params)
.execute(@merge_request)
@status = :merge_when_build_succeeds
@@ -320,8 +336,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def define_show_vars
# Build a note object for comment form
@note = @project.notes.new(noteable: @merge_request)
- @notes = @merge_request.mr_and_commit_notes.inc_author.fresh
- @discussions = @notes.discussions
+
+ @discussions = @merge_request.mr_and_commit_notes.
+ inc_author_project_award_emoji.
+ fresh.
+ discussions
+
+ @notes = Banzai::NoteRenderer.render(
+ @discussions.flatten,
+ @project,
+ current_user,
+ @path,
+ @project_wiki,
+ @ref
+ )
+
@noteable = @merge_request
# Get commits from repository
@@ -368,4 +397,9 @@ class Projects::MergeRequestsController < Projects::ApplicationController
def ensure_ref_fetched
@merge_request.ensure_ref_fetched
end
+
+ def merge_when_build_succeeds_active?
+ params[:merge_when_build_succeeds].present? &&
+ @merge_request.pipeline && @merge_request.pipeline.active?
+ end
end
diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb
index 836f79ff080..e14fe26dde7 100644
--- a/app/controllers/projects/notes_controller.rb
+++ b/app/controllers/projects/notes_controller.rb
@@ -24,6 +24,10 @@ class Projects::NotesController < Projects::ApplicationController
def create
@note = Notes::CreateService.new(project, current_user, note_params).execute
+ if @note.is_a?(Note)
+ Banzai::NoteRenderer.render([@note], @project, current_user)
+ end
+
respond_to do |format|
format.json { render json: note_json(@note) }
format.html { redirect_back_or_default }
@@ -33,6 +37,10 @@ class Projects::NotesController < Projects::ApplicationController
def update
@note = Notes::UpdateService.new(project, current_user, note_params).execute(note)
+ if @note.is_a?(Note)
+ Banzai::NoteRenderer.render([@note], @project, current_user)
+ end
+
respond_to do |format|
format.json { render json: note_json(@note) }
format.html { redirect_back_or_default }
@@ -118,6 +126,8 @@ class Projects::NotesController < Projects::ApplicationController
name: note.name
}
elsif note.valid?
+ Banzai::NoteRenderer.render([note], @project, current_user)
+
{
valid: true,
id: note.id,
diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb
index 127bd1a4318..487963fdcd7 100644
--- a/app/controllers/projects/pipelines_controller.rb
+++ b/app/controllers/projects/pipelines_controller.rb
@@ -54,6 +54,6 @@ class Projects::PipelinesController < Projects::ApplicationController
end
def commit
- @commit ||= @pipeline.commit_data
+ @commit ||= @pipeline.commit
end
end
diff --git a/app/controllers/projects/project_members_controller.rb b/app/controllers/projects/project_members_controller.rb
index 35d067cd029..6ba32d33403 100644
--- a/app/controllers/projects/project_members_controller.rb
+++ b/app/controllers/projects/project_members_controller.rb
@@ -50,9 +50,7 @@ class Projects::ProjectMembersController < Projects::ApplicationController
def destroy
@project_member = @project.project_members.find(params[:id])
- return render_403 unless can?(current_user, :destroy_project_member, @project_member)
-
- @project_member.destroy
+ Members::DestroyService.new(@project_member, current_user).execute
respond_to do |format|
format.html do
@@ -98,8 +96,4 @@ class Projects::ProjectMembersController < Projects::ApplicationController
# MembershipActions concern
alias_method :membershipable, :project
-
- def cannot_leave?
- current_user == @project.owner
- end
end
diff --git a/app/controllers/projects/runner_projects_controller.rb b/app/controllers/projects/runner_projects_controller.rb
index bedeb4a295c..dc1a18f8d42 100644
--- a/app/controllers/projects/runner_projects_controller.rb
+++ b/app/controllers/projects/runner_projects_controller.rb
@@ -6,11 +6,13 @@ class Projects::RunnerProjectsController < Projects::ApplicationController
def create
@runner = Ci::Runner.find(params[:runner_project][:runner_id])
+ return head(403) if @runner.is_shared? || @runner.locked?
return head(403) unless current_user.ci_authorized_runners.include?(@runner)
path = runners_path(project)
+ runner_project = @runner.assign_to(project, current_user)
- if @runner.assign_to(project, current_user)
+ if runner_project.persisted?
redirect_to path
else
redirect_to path, alert: 'Failed adding runner to project'
diff --git a/app/controllers/projects/runners_controller.rb b/app/controllers/projects/runners_controller.rb
index 0b4fa572501..53c36635efe 100644
--- a/app/controllers/projects/runners_controller.rb
+++ b/app/controllers/projects/runners_controller.rb
@@ -5,10 +5,9 @@ class Projects::RunnersController < Projects::ApplicationController
layout 'project_settings'
def index
- @runners = project.runners.ordered
- @specific_runners = current_user.ci_authorized_runners.
- where.not(id: project.runners).
- ordered.page(params[:page]).per(20)
+ @project_runners = project.runners.ordered
+ @assignable_runners = current_user.ci_authorized_runners.
+ assignable_for(project).ordered.page(params[:page]).per(20)
@shared_runners = Ci::Runner.shared.active
@shared_runners_count = @shared_runners.count(:all)
end
diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb
index 8044c637825..2b1f50fd01e 100644
--- a/app/controllers/projects_controller.rb
+++ b/app/controllers/projects_controller.rb
@@ -1,7 +1,7 @@
class ProjectsController < Projects::ApplicationController
include ExtractsPath
- before_action :authenticate_user!, except: [:show, :activity]
+ before_action :authenticate_user!, except: [:show, :activity, :refs]
before_action :project, except: [:new, :create]
before_action :repository, except: [:new, :create]
before_action :assign_ref_vars, :tree, only: [:show], if: :repo_exists?
@@ -251,6 +251,24 @@ class ProjectsController < Projects::ApplicationController
}
end
+ def refs
+ options = {
+ 'Branches' => @repository.branch_names,
+ }
+
+ unless @repository.tag_count.zero?
+ options['Tags'] = VersionSorter.rsort(@repository.tag_names)
+ end
+
+ # If reference is commit id - we should add it to branch/tag selectbox
+ ref = Addressable::URI.unescape(params[:ref])
+ if ref && options.flatten(2).exclude?(ref) && ref =~ /\A[0-9a-zA-Z]{6,52}\z/
+ options['Commits'] = [ref]
+ end
+
+ render json: options.to_json
+ end
+
private
def determine_layout
@@ -285,8 +303,14 @@ class ProjectsController < Projects::ApplicationController
project.repository_exists? && !project.empty_repo?
end
- # Override get_id from ExtractsPath, which returns the branch and file path
+ # Override extract_ref from ExtractsPath, which returns the branch and file path
# for the blob/tree, which in this case is just the root of the default branch.
+ # This way we avoid to access the repository.ref_names.
+ def extract_ref(_id)
+ [get_id, '']
+ end
+
+ # Override get_id from ExtractsPath in this case is just the root of the default branch.
def get_id
project.repository.root_ref
end
diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb
index 439b015b3b8..62d13a4b4f3 100644
--- a/app/helpers/application_helper.rb
+++ b/app/helpers/application_helper.rb
@@ -101,22 +101,6 @@ module ApplicationHelper
'Never'
end
- def grouped_options_refs
- repository = @project.repository
-
- options = [
- ['Branches', repository.branch_names],
- ['Tags', VersionSorter.rsort(repository.tag_names)]
- ]
-
- # If reference is commit id - we should add it to branch/tag selectbox
- if @ref && !options.flatten.include?(@ref) && @ref =~ /\A[0-9a-zA-Z]{6,52}\z/
- options << ['Commit', [@ref]]
- end
-
- grouped_options_for_select(options, @ref || @project.default_branch)
- end
-
# Define whenever show last push event
# with suggestion to create MR
def show_last_push_widget?(event)
@@ -132,7 +116,7 @@ module ApplicationHelper
return false if project.merge_requests.where(source_branch: event.branch_name).opened.any?
# Skip if user removed branch right after that
- return false unless project.repository.branch_names.include?(event.branch_name)
+ return false unless project.repository.branch_exists?(event.branch_name)
true
end
@@ -213,7 +197,7 @@ module ApplicationHelper
def render_markup(file_name, file_content)
if gitlab_markdown?(file_name)
- Haml::Helpers.preserve(markdown(file_content))
+ Hamlit::RailsHelpers.preserve(markdown(file_content))
elsif asciidoc?(file_name)
asciidoc(file_content)
elsif plain?(file_name)
diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb
index 5b54b34070c..4b4bc3d4276 100644
--- a/app/helpers/blob_helper.rb
+++ b/app/helpers/blob_helper.rb
@@ -29,7 +29,7 @@ module BlobHelper
if !on_top_of_branch?(project, ref)
button_tag "Edit", class: "btn disabled has-tooltip btn-file-option", title: "You can only edit files when you are on a branch", data: { container: 'body' }
elsif can_edit_blob?(blob, project, ref)
- link_to "Edit", edit_path, class: 'btn btn-file-option'
+ link_to "Edit", edit_path, class: 'btn btn-sm'
elsif can?(current_user, :fork_project, project)
continue_params = {
to: edit_path,
@@ -186,12 +186,16 @@ module BlobHelper
end
def gitignore_names
- return @gitignore_names if defined?(@gitignore_names)
+ @gitignore_names ||=
+ Gitlab::Template::Gitignore.categories.keys.map do |k|
+ [k, Gitlab::Template::Gitignore.by_category(k).map { |t| { name: t.name } }]
+ end.to_h
+ end
- @gitignore_names = {
- Global: Gitlab::Gitignore.global.map { |gitignore| { name: gitignore.name } },
- # Note that the key here doesn't cover it really
- Languages: Gitlab::Gitignore.languages_frameworks.map{ |gitignore| { name: gitignore.name } }
- }
+ def gitlab_ci_ymls
+ @gitlab_ci_ymls ||=
+ Gitlab::Template::GitlabCiYml.categories.keys.map do |k|
+ [k, Gitlab::Template::GitlabCiYml.by_category(k).map { |t| { name: t.name } }]
+ end.to_h
end
end
diff --git a/app/helpers/branches_helper.rb b/app/helpers/branches_helper.rb
index 3ee3fc74f0c..c533659b600 100644
--- a/app/helpers/branches_helper.rb
+++ b/app/helpers/branches_helper.rb
@@ -10,7 +10,7 @@ module BranchesHelper
end
def can_push_branch?(project, branch_name)
- return false unless project.repository.branch_names.include?(branch_name)
+ return false unless project.repository.branch_exists?(branch_name)
::Gitlab::GitAccess.new(current_user, project).can_push_to_branch?(branch_name)
end
diff --git a/app/helpers/gitlab_markdown_helper.rb b/app/helpers/gitlab_markdown_helper.rb
index 067a00660aa..1a259656f31 100644
--- a/app/helpers/gitlab_markdown_helper.rb
+++ b/app/helpers/gitlab_markdown_helper.rb
@@ -50,8 +50,6 @@ module GitlabMarkdownHelper
context[:project] ||= @project
- text = Banzai.pre_process(text, context)
-
html = Banzai.render(text, context)
context.merge!(
@@ -185,4 +183,17 @@ module GitlabMarkdownHelper
''
end
end
+
+ def markdown_toolbar_button(options = {})
+ data = options[:data].merge({ container: "body" })
+ content_tag :button,
+ type: "button",
+ class: "toolbar-btn js-md has-tooltip hidden-xs",
+ tabindex: -1,
+ data: data,
+ title: options[:title],
+ aria: { label: options[:title] } do
+ icon(options[:icon])
+ end
+ end
end
diff --git a/app/helpers/javascript_helper.rb b/app/helpers/javascript_helper.rb
index 91dd91718dc..0e456214d37 100644
--- a/app/helpers/javascript_helper.rb
+++ b/app/helpers/javascript_helper.rb
@@ -1,7 +1,5 @@
module JavascriptHelper
- def page_specific_javascripts(js = nil)
- @page_specific_javascripts = js unless js.nil?
-
- @page_specific_javascripts
+ def page_specific_javascript_tag(js)
+ javascript_include_tag asset_path(js), { "data-turbolinks-track" => true }
end
end
diff --git a/app/mailers/emails/members.rb b/app/mailers/emails/members.rb
index 6dde2e9847d..45311690293 100644
--- a/app/mailers/emails/members.rb
+++ b/app/mailers/emails/members.rb
@@ -12,6 +12,11 @@ module Emails
@member_id = member_id
admins = member_source.members.owners_and_masters.includes(:user).pluck(:notification_email)
+ # A project in a group can have no explicit owners/masters, in that case
+ # we fallbacks to the group's owners/masters.
+ if admins.empty? && member_source.respond_to?(:group) && member_source.group
+ admins = member_source.group.members.owners_and_masters.includes(:user).pluck(:notification_email)
+ end
mail(to: admins,
subject: subject("Request to join the #{member_source.human_name} #{member_source.model_name.singular}"))
diff --git a/app/models/ability.rb b/app/models/ability.rb
index 9c58b956007..f5950879ccb 100644
--- a/app/models/ability.rb
+++ b/app/models/ability.rb
@@ -196,7 +196,8 @@ class Ability
@public_project_rules ||= project_guest_rules + [
:download_code,
:fork_project,
- :read_commit_status
+ :read_commit_status,
+ :read_pipeline
]
end
diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb
index d618c84e983..2b0bec33131 100644
--- a/app/models/ci/build.rb
+++ b/app/models/ci/build.rb
@@ -300,18 +300,12 @@ module Ci
project.valid_runners_token? token
end
- def can_be_served?(runner)
- return false unless has_tags? || runner.run_untagged?
-
- (tag_list - runner.tag_list).empty?
- end
-
def has_tags?
tag_list.any?
end
def any_runners_online?
- project.any_runners? { |runner| runner.active? && runner.online? && can_be_served?(runner) }
+ project.any_runners? { |runner| runner.active? && runner.online? && runner.can_pick?(self) }
end
def stuck?
diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb
index 5b264ecffc5..10324bf2257 100644
--- a/app/models/ci/pipeline.rb
+++ b/app/models/ci/pipeline.rb
@@ -37,22 +37,22 @@ module Ci
end
def git_author_name
- commit_data.author_name if commit_data
+ commit.try(:author_name)
end
def git_author_email
- commit_data.author_email if commit_data
+ commit.try(:author_email)
end
def git_commit_message
- commit_data.message if commit_data
+ commit.try(:message)
end
def short_sha
Ci::Pipeline.truncate_sha(sha)
end
- def commit_data
+ def commit
@commit ||= project.commit(sha)
rescue
nil
@@ -163,13 +163,26 @@ module Ci
end
def skip_ci?
- git_commit_message =~ /(\[ci skip\])/ if git_commit_message
+ git_commit_message =~ /\[(ci skip|skip ci)\]/i if git_commit_message
end
def environments
builds.where.not(environment: nil).success.pluck(:environment).uniq
end
+ # Manually set the notes for a Ci::Pipeline
+ # There is no ActiveRecord relation between Ci::Pipeline and notes
+ # as they are related to a commit sha. This method helps importing
+ # them using the +Gitlab::ImportExport::RelationFactory+ class.
+ def notes=(notes)
+ notes.each do |note|
+ note[:id] = nil
+ note[:commit_id] = sha
+ note[:noteable_id] = self['id']
+ note.save!
+ end
+ end
+
def notes
Note.for_commit_id(sha)
end
diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb
index adb65292208..b64ec79ec2b 100644
--- a/app/models/ci/runner.rb
+++ b/app/models/ci/runner.rb
@@ -4,7 +4,7 @@ module Ci
LAST_CONTACT_TIME = 5.minutes.ago
AVAILABLE_SCOPES = %w[specific shared active paused online]
- FORM_EDITABLE = %i[description tag_list active run_untagged]
+ FORM_EDITABLE = %i[description tag_list active run_untagged locked]
has_many :builds, class_name: 'Ci::Build'
has_many :runner_projects, dependent: :destroy, class_name: 'Ci::RunnerProject'
@@ -26,6 +26,13 @@ module Ci
.where("ci_runner_projects.gl_project_id = :project_id OR ci_runners.is_shared = true", project_id: project_id)
end
+ scope :assignable_for, ->(project) do
+ # FIXME: That `to_sql` is needed to workaround a weird Rails bug.
+ # Without that, placeholders would miss one and couldn't match.
+ where(locked: false).
+ where.not("id IN (#{project.runners.select(:id).to_sql})").specific
+ end
+
validate :tag_constraints
acts_as_taggable
@@ -56,7 +63,7 @@ module Ci
def assign_to(project, current_user = nil)
self.is_shared = false if shared?
self.save
- project.runner_projects.create!(runner_id: self.id)
+ project.runner_projects.create(runner_id: self.id)
end
def display_name
@@ -91,6 +98,10 @@ module Ci
!shared?
end
+ def can_pick?(build)
+ assignable_for?(build.project) && accepting_tags?(build)
+ end
+
def only_for?(project)
projects == [project]
end
@@ -111,5 +122,13 @@ module Ci
'can not be empty when runner is not allowed to pick untagged jobs')
end
end
+
+ def assignable_for?(project)
+ !locked? || projects.exists?(id: project.id)
+ end
+
+ def accepting_tags?(build)
+ (run_untagged? || build.has_tags?) && (build.tag_list - tag_list).empty?
+ end
end
end
diff --git a/app/models/commit.rb b/app/models/commit.rb
index d69d518fadd..174ccbaea6c 100644
--- a/app/models/commit.rb
+++ b/app/models/commit.rb
@@ -271,6 +271,32 @@ class Commit
merged_merge_request ? 'merge request' : 'commit'
end
+ # Get the URI type of the given path
+ #
+ # Used to build URLs to files in the repository in GFM.
+ #
+ # path - String path to check
+ #
+ # Examples:
+ #
+ # uri_type('doc/README.md') # => :blob
+ # uri_type('doc/logo.png') # => :raw
+ # uri_type('doc/api') # => :tree
+ # uri_type('not/found') # => :nil
+ #
+ # Returns a symbol
+ def uri_type(path)
+ entry = @raw.tree.path(path)
+ if entry[:type] == :blob
+ blob = Gitlab::Git::Blob.new(name: entry[:name])
+ blob.image? ? :raw : :blob
+ else
+ entry[:type]
+ end
+ rescue Rugged::TreeError
+ nil
+ end
+
private
def repo_changes
diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb
index ab13db4b297..e437e3417a8 100644
--- a/app/models/commit_status.rb
+++ b/app/models/commit_status.rb
@@ -8,6 +8,8 @@ class CommitStatus < ActiveRecord::Base
belongs_to :pipeline, class_name: 'Ci::Pipeline', foreign_key: :commit_id, touch: true
belongs_to :user
+ delegate :commit, to: :pipeline
+
validates :pipeline, presence: true, unless: :importing?
validates_presence_of :name
diff --git a/app/models/concerns/awardable.rb b/app/models/concerns/awardable.rb
index 539c7c31e30..06beff177b1 100644
--- a/app/models/concerns/awardable.rb
+++ b/app/models/concerns/awardable.rb
@@ -2,10 +2,11 @@ module Awardable
extend ActiveSupport::Concern
included do
- has_many :award_emoji, as: :awardable, dependent: :destroy
+ has_many :award_emoji, -> { includes(:user) }, as: :awardable, dependent: :destroy
if self < Participable
- participant :award_emoji_with_associations
+ # By default we always load award_emoji user association
+ participant :award_emoji
end
end
@@ -34,12 +35,9 @@ module Awardable
end
end
- def award_emoji_with_associations
- award_emoji.includes(:user)
- end
-
def grouped_awards(with_thumbs: true)
- awards = award_emoji_with_associations.group_by(&:name)
+ # By default we always load award_emoji user association
+ awards = award_emoji.group_by(&:name)
if with_thumbs
awards[AwardEmoji::UPVOTE_NAME] ||= []
diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb
index 0ccd3474b81..d6f55885dd6 100644
--- a/app/models/concerns/issuable.rb
+++ b/app/models/concerns/issuable.rb
@@ -19,9 +19,14 @@ module Issuable
belongs_to :milestone
has_many :notes, as: :noteable, dependent: :destroy do
def authors_loaded?
- # We check first if we're loaded to not load unnecesarily.
+ # We check first if we're loaded to not load unnecessarily.
loaded? && to_a.all? { |note| note.association(:author).loaded? }
end
+
+ def award_emojis_loaded?
+ # We check first if we're loaded to not load unnecessarily.
+ loaded? && to_a.all? { |note| note.association(:award_emoji).loaded? }
+ end
end
has_many :label_links, as: :target, dependent: :destroy
has_many :labels, through: :label_links
@@ -49,7 +54,7 @@ module Issuable
scope :without_label, -> { joins("LEFT OUTER JOIN label_links ON label_links.target_type = '#{name}' AND label_links.target_id = #{table_name}.id").where(label_links: { id: nil }) }
scope :join_project, -> { joins(:project) }
- scope :inc_notes_with_associations, -> { includes(notes: :author) }
+ scope :inc_notes_with_associations, -> { includes(notes: [ :project, :author, :award_emoji ]) }
scope :references_project, -> { references(:project) }
scope :non_archived, -> { join_project.where(projects: { archived: false }) }
@@ -112,15 +117,18 @@ module Issuable
end
def sort(method, excluded_labels: [])
- case method.to_s
- when 'milestone_due_asc' then order_milestone_due_asc
- when 'milestone_due_desc' then order_milestone_due_desc
- when 'downvotes_desc' then order_downvotes_desc
- when 'upvotes_desc' then order_upvotes_desc
- when 'priority' then order_labels_priority(excluded_labels: excluded_labels)
- else
- order_by(method)
- end
+ sorted = case method.to_s
+ when 'milestone_due_asc' then order_milestone_due_asc
+ when 'milestone_due_desc' then order_milestone_due_desc
+ when 'downvotes_desc' then order_downvotes_desc
+ when 'upvotes_desc' then order_upvotes_desc
+ when 'priority' then order_labels_priority(excluded_labels: excluded_labels)
+ else
+ order_by(method)
+ end
+
+ # Break ties with the ID column for pagination
+ sorted.order(id: :desc)
end
def order_labels_priority(excluded_labels: [])
@@ -257,7 +265,14 @@ module Issuable
# already have their authors loaded (possibly because the scope
# `inc_notes_with_associations` was used) and skip the inclusion if that's
# the case.
- notes.authors_loaded? ? notes : notes.includes(:author)
+ includes = []
+ includes << :author unless notes.authors_loaded?
+ includes << :award_emoji unless notes.award_emojis_loaded?
+ if includes.any?
+ notes.includes(includes)
+ else
+ notes
+ end
end
def updated_tasks
diff --git a/app/models/concerns/participable.rb b/app/models/concerns/participable.rb
index 9056722f45e..9822844357d 100644
--- a/app/models/concerns/participable.rb
+++ b/app/models/concerns/participable.rb
@@ -53,6 +53,16 @@ module Participable
#
# Returns an Array of User instances.
def participants(current_user = nil)
+ @participants ||= Hash.new do |hash, user|
+ hash[user] = raw_participants(user)
+ end
+
+ @participants[current_user]
+ end
+
+ private
+
+ def raw_participants(current_user = nil)
current_user ||= author
ext = Gitlab::ReferenceExtractor.new(project, current_user)
participants = Set.new
diff --git a/app/models/key.rb b/app/models/key.rb
index 0532e84f47d..b9bc38a0436 100644
--- a/app/models/key.rb
+++ b/app/models/key.rb
@@ -9,7 +9,7 @@ class Key < ActiveRecord::Base
before_validation :strip_white_space, :generate_fingerprint
validates :title, presence: true, length: { within: 0..255 }
- validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ }, uniqueness: true
+ validates :key, presence: true, length: { within: 0..5000 }, format: { with: /\A(ssh|ecdsa)-.*\Z/ }
validates :key, format: { without: /\n|\r/, message: 'should be a single line' }
validates :fingerprint, uniqueness: true, presence: { message: 'cannot be generated' }
diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb
index 95fd510eb3a..33d2a69ebaf 100644
--- a/app/models/legacy_diff_note.rb
+++ b/app/models/legacy_diff_note.rb
@@ -20,7 +20,7 @@ class LegacyDiffNote < Note
end
def discussion_id
- @discussion_id ||= self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code, active?)
+ @discussion_id ||= self.class.build_discussion_id(noteable_type, noteable_id || commit_id, line_code)
end
def diff_file_hash
diff --git a/app/models/member.rb b/app/models/member.rb
index 4ee3f1bb5c2..c74a16367db 100644
--- a/app/models/member.rb
+++ b/app/models/member.rb
@@ -48,7 +48,6 @@ class Member < ActiveRecord::Base
after_create :post_create_hook, unless: [:pending?, :importing?]
after_update :post_update_hook, unless: [:pending?, :importing?]
after_destroy :post_destroy_hook, unless: :pending?
- after_destroy :post_decline_request, if: :request?
delegate :name, :username, :email, to: :user, prefix: true
@@ -188,7 +187,7 @@ class Member < ActiveRecord::Base
end
def send_request
- # override in subclass
+ notification_service.new_access_request(self)
end
def post_create_hook
@@ -215,10 +214,6 @@ class Member < ActiveRecord::Base
post_create_hook
end
- def post_decline_request
- # override in subclass
- end
-
def system_hook_service
SystemHooksService.new
end
diff --git a/app/models/members/group_member.rb b/app/models/members/group_member.rb
index 363db877968..2f13d339c89 100644
--- a/app/models/members/group_member.rb
+++ b/app/models/members/group_member.rb
@@ -33,12 +33,6 @@ class GroupMember < Member
super
end
- def send_request
- notification_service.new_group_access_request(self)
-
- super
- end
-
def post_create_hook
notification_service.new_group_member(self)
@@ -64,10 +58,4 @@ class GroupMember < Member
super
end
-
- def post_decline_request
- notification_service.decline_group_access_request(self)
-
- super
- end
end
diff --git a/app/models/members/project_member.rb b/app/models/members/project_member.rb
index 250ee04fd1d..e9d3a82ba15 100644
--- a/app/models/members/project_member.rb
+++ b/app/models/members/project_member.rb
@@ -111,12 +111,6 @@ class ProjectMember < Member
super
end
- def send_request
- notification_service.new_project_access_request(self)
-
- super
- end
-
def post_create_hook
unless owner?
event_service.join_project(self.project, self.user)
@@ -152,12 +146,6 @@ class ProjectMember < Member
super
end
- def post_decline_request
- notification_service.decline_project_access_request(self)
-
- super
- end
-
def event_service
EventCreateService.new
end
diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb
index 36bc98bdb1e..f5c5b7c1306 100644
--- a/app/models/merge_request.rb
+++ b/app/models/merge_request.rb
@@ -264,19 +264,19 @@ class MergeRequest < ActiveRecord::Base
self.title.sub(WIP_REGEX, "")
end
- def mergeable?
- return false unless mergeable_state?
+ def mergeable?(skip_ci_check: false)
+ return false unless mergeable_state?(skip_ci_check: skip_ci_check)
check_if_can_be_merged
can_be_merged?
end
- def mergeable_state?
+ def mergeable_state?(skip_ci_check: false)
return false unless open?
return false if work_in_progress?
return false if broken?
- return false unless mergeable_ci_state?
+ return false unless skip_ci_check || mergeable_ci_state?
true
end
diff --git a/app/models/note.rb b/app/models/note.rb
index 8d164647550..8db500a5219 100644
--- a/app/models/note.rb
+++ b/app/models/note.rb
@@ -6,6 +6,10 @@ class Note < ActiveRecord::Base
include Awardable
include Importable
+ # Attribute containing rendered and redacted Markdown as generated by
+ # Banzai::ObjectRenderer.
+ attr_accessor :note_html
+
default_value_for :system, false
attr_mentionable :note, pipeline: :note
@@ -49,11 +53,13 @@ class Note < ActiveRecord::Base
scope :fresh, ->{ order(created_at: :asc, id: :asc) }
scope :inc_author_project, ->{ includes(:project, :author) }
scope :inc_author, ->{ includes(:author) }
+ scope :inc_author_project_award_emoji, ->{ includes(:project, :author, :award_emoji) }
scope :legacy_diff_notes, ->{ where(type: 'LegacyDiffNote') }
scope :non_diff_notes, ->{ where(type: ['Note', nil]) }
scope :with_associations, -> do
+ # FYI noteable cannot be loaded for LegacyDiffNote for commits
includes(:author, :noteable, :updated_by,
project: [:project_members, { group: [:group_members] }])
end
diff --git a/app/models/repository.rb b/app/models/repository.rb
index bbd7682d8e7..acc720ccfa3 100644
--- a/app/models/repository.rb
+++ b/app/models/repository.rb
@@ -130,7 +130,7 @@ class Repository
end
def find_tag(name)
- raw_repository.tags.find { |tag| tag.name == name }
+ tags.find { |tag| tag.name == name }
end
def add_branch(user, branch_name, target)
@@ -191,8 +191,12 @@ class Repository
end
end
+ def ref_names
+ branch_names + tag_names
+ end
+
def branch_names
- cache.fetch(:branch_names) { branches.map(&:name) }
+ @branch_names ||= cache.fetch(:branch_names) { branches.map(&:name) }
end
def branch_exists?(branch_name)
@@ -267,6 +271,7 @@ class Repository
def expire_branches_cache
cache.expire(:branch_names)
+ @branch_names = nil
@local_branches = nil
end
@@ -332,10 +337,6 @@ class Repository
@lookup_cache ||= {}
end
- def expire_branch_names
- cache.expire(:branch_names)
- end
-
def expire_avatar_cache(branch_name = nil, revision = nil)
# Avatars are pulled from the default branch, thus if somebody pushes to a
# different branch there's no need to expire anything.
diff --git a/app/models/user.rb b/app/models/user.rb
index 2e458329cb9..04b220ee13c 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -308,7 +308,7 @@ class User < ActiveRecord::Base
def generate_password
if self.force_random_password
- self.password = self.password_confirmation = Devise.friendly_token.first(8)
+ self.password = self.password_confirmation = Devise.friendly_token.first(Devise.password_length.min)
end
end
@@ -487,9 +487,8 @@ class User < ActiveRecord::Base
events.recent.find do |event|
project = Project.find_by_id(event.project_id)
next unless project
- repo = project.repository
- if repo.branch_names.include?(event.branch_name)
+ if project.repository.branch_exists?(event.branch_name)
merge_requests = MergeRequest.where("created_at >= ?", event.created_at).
where(source_project_id: project.id,
source_branch: event.branch_name)
diff --git a/app/services/ci/register_build_service.rb b/app/services/ci/register_build_service.rb
index f0ed09a629a..9a187f5d694 100644
--- a/app/services/ci/register_build_service.rb
+++ b/app/services/ci/register_build_service.rb
@@ -21,7 +21,7 @@ module Ci
end
build = builds.find do |build|
- build.can_be_served?(current_runner)
+ current_runner.can_pick?(build)
end
if build
diff --git a/app/services/members/destroy_service.rb b/app/services/members/destroy_service.rb
new file mode 100644
index 00000000000..15358f80208
--- /dev/null
+++ b/app/services/members/destroy_service.rb
@@ -0,0 +1,21 @@
+module Members
+ class DestroyService < BaseService
+ attr_accessor :member, :current_user
+
+ def initialize(member, user)
+ @member, @current_user = member, user
+ end
+
+ def execute
+ unless member && can?(current_user, "destroy_#{member.type.underscore}".to_sym, member)
+ raise Gitlab::Access::AccessDeniedError
+ end
+
+ member.destroy
+
+ if member.request? && member.user != current_user
+ notification_service.decline_access_request(member)
+ end
+ end
+ end
+end
diff --git a/app/services/merge_requests/build_service.rb b/app/services/merge_requests/build_service.rb
index 1b48899bb0a..7fe57747265 100644
--- a/app/services/merge_requests/build_service.rb
+++ b/app/services/merge_requests/build_service.rb
@@ -83,7 +83,7 @@ module MergeRequests
closes_issue = "Closes ##{iid}"
if merge_request.description.present?
- merge_request.description << closes_issue.prepend("\n")
+ merge_request.description += closes_issue.prepend("\n")
else
merge_request.description = closes_issue
end
diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb
index 19832a19b2b..590350a11e5 100644
--- a/app/services/notification_service.rb
+++ b/app/services/notification_service.rb
@@ -181,15 +181,16 @@ class NotificationService
end
end
- # Project access request
- def new_project_access_request(project_member)
- mailer.member_access_requested_email(project_member.real_source_type, project_member.id).deliver_later
+ # Members
+ def new_access_request(member)
+ mailer.member_access_requested_email(member.real_source_type, member.id).deliver_later
end
- def decline_project_access_request(project_member)
- mailer.member_access_denied_email(project_member.real_source_type, project_member.project.id, project_member.user.id).deliver_later
+ def decline_access_request(member)
+ mailer.member_access_denied_email(member.real_source_type, member.source_id, member.user_id).deliver_later
end
+ # Project invite
def invite_project_member(project_member, token)
mailer.member_invited_email(project_member.real_source_type, project_member.id, token).deliver_later
end
@@ -216,15 +217,7 @@ class NotificationService
mailer.member_access_granted_email(project_member.real_source_type, project_member.id).deliver_later
end
- # Group access request
- def new_group_access_request(group_member)
- mailer.member_access_requested_email(group_member.real_source_type, group_member.id).deliver_later
- end
-
- def decline_group_access_request(group_member)
- mailer.member_access_denied_email(group_member.real_source_type, group_member.group.id, group_member.user.id).deliver_later
- end
-
+ # Group invite
def invite_group_member(group_member, token)
mailer.member_invited_email(group_member.real_source_type, group_member.id, token).deliver_later
end
diff --git a/app/services/todo_service.rb b/app/services/todo_service.rb
index 540bf54b920..239bd17a035 100644
--- a/app/services/todo_service.rb
+++ b/app/services/todo_service.rb
@@ -159,8 +159,9 @@ class TodoService
def create_todos(users, attributes)
Array(users).map do |user|
next if pending_todos(user, attributes).exists?
- Todo.create(attributes.merge(user_id: user.id))
+ todo = Todo.create(attributes.merge(user_id: user.id))
user.update_todos_count_cache
+ todo
end
end
diff --git a/app/views/admin/appearances/_form.html.haml b/app/views/admin/appearances/_form.html.haml
index d88f3ad314d..dc083e50178 100644
--- a/app/views/admin/appearances/_form.html.haml
+++ b/app/views/admin/appearances/_form.html.haml
@@ -46,7 +46,7 @@
Maximum file size is 1MB. Pages are optimized for a 72x72 px header logo
.form-actions
- = f.submit 'Save', class: 'btn btn-save'
+ = f.submit 'Save', class: 'btn btn-save append-right-10'
- if @appearance.persisted?
= link_to 'Preview last save', preview_admin_appearances_path, class: 'btn', target: '_blank'
diff --git a/app/views/admin/appearances/preview.html.haml b/app/views/admin/appearances/preview.html.haml
index dd4a64e80bc..6c51639b840 100644
--- a/app/views/admin/appearances/preview.html.haml
+++ b/app/views/admin/appearances/preview.html.haml
@@ -1,29 +1,9 @@
- page_title "Preview | Appearance"
-%h3.page-title
- Appearance settings - Preview
-%hr
+.login-box
+ .login-heading
+ %h3 Existing user? Sign in
+ %form
+ = text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email"
+ = password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password"
+ = button_tag "Sign in", class: "btn-create btn"
-.ui-box
- .title
- Sign-in page
- %div
- .login-page
- .container
- .content
- .login-title
- %h1= brand_title
- %hr
- .container
- .content
- .row
- .col-sm-7
- .brand-image
- = brand_image
- .brand_text
- = brand_text
- .col-sm-4
- .login-box
- %h3.page-title Sign in
- = text_field_tag :login, nil, class: "form-control top", placeholder: "Username or Email"
- = password_field_tag :password, nil, class: "form-control bottom", placeholder: "Password"
- = button_tag "Sign in", class: "btn-create btn"
diff --git a/app/views/admin/appearances/show.html.haml b/app/views/admin/appearances/show.html.haml
index 089e8e4cb7a..454b779842c 100644
--- a/app/views/admin/appearances/show.html.haml
+++ b/app/views/admin/appearances/show.html.haml
@@ -1,7 +1,9 @@
- page_title "Appearance"
+
%h3.page-title
Appearance settings
%p.light
You can modify the look and feel of GitLab here
+%hr
= render 'form'
diff --git a/app/views/admin/application_settings/show.html.haml b/app/views/admin/application_settings/show.html.haml
index e9c7ca9d5aa..ecc46d86afe 100644
--- a/app/views/admin/application_settings/show.html.haml
+++ b/app/views/admin/application_settings/show.html.haml
@@ -1,4 +1,5 @@
- page_title "Settings"
+
%h3.page-title Settings
%hr
= render 'form'
diff --git a/app/views/admin/groups/show.html.haml b/app/views/admin/groups/show.html.haml
index 5b8a0262ea0..50770465f07 100644
--- a/app/views/admin/groups/show.html.haml
+++ b/app/views/admin/groups/show.html.haml
@@ -88,28 +88,17 @@
= select_tag :access_level, options_for_select(GroupMember.access_level_roles), class: "project-access-select select2"
%hr
= button_tag 'Add users to group', class: "btn btn-create"
+
+ = render 'shared/members/requests', membership_source: @group, members: @members.request
+
.panel.panel-default
.panel-heading
- %h3.panel-title
- Members
- %span.badge
- #{@group.group_members.count}
- %ul.well-list.group-users-list
- - @members.each do |member|
- - user = member.user
- %li{class: dom_class(member), id: (dom_id(user) if user)}
- .list-item-name
- - if user
- %strong
- = link_to user.name, admin_user_path(user)
- - else
- %strong
- = member.invite_email
- (invited)
- %span.pull-right.light
- = member.human_access
- - if can?(current_user, :destroy_group_member, member)
- = link_to group_group_member_path(@group, member), data: { confirm: remove_member_message(member) }, method: :delete, remote: true, class: "btn-xs btn btn-remove", title: 'Remove user from group' do
- %i.fa.fa-minus.fa-inverse
+ %strong= @group.name
+ group members
+ %span.badge= @group.members.non_request.size
+ .pull-right
+ = link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@group, :members]), class: "btn btn-xs"
+ %ul.well-list.group-users-list.content-list
+ = render partial: 'shared/members/member', collection: @members.non_request, as: :member, locals: { show_controls: false }
.panel-footer
- = paginate @members, param_name: 'members_page', theme: 'gitlab'
+ = paginate @members.non_request, param_name: 'members_page', theme: 'gitlab'
diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml
index 9e55a562e18..461d588415d 100644
--- a/app/views/admin/projects/show.html.haml
+++ b/app/views/admin/projects/show.html.haml
@@ -135,44 +135,27 @@
- if @group
.panel.panel-default
.panel-heading
- %strong #{@group.name}
- group members (#{@group.group_members.count})
+ %strong= @group.name
+ group members
+ %span.badge= @group_members.non_request.size
.pull-right
= link_to admin_group_path(@group), class: 'btn btn-xs' do
- %i.fa.fa-pencil-square-o
- %ul.well-list
- - @group_members.each do |member|
- = render 'shared/members/member', member: member, show_controls: false
+ = icon('pencil-square-o', text: 'Manage Access')
+ %ul.well-list.content-list
+ = render partial: 'shared/members/member', collection: @group_members.non_request, as: :member, locals: { show_controls: false }
.panel-footer
- = paginate @group_members, param_name: 'group_members_page', theme: 'gitlab'
+ = paginate @group_members.non_request, param_name: 'group_members_page', theme: 'gitlab'
+
+ = render 'shared/members/requests', membership_source: @project, members: @project_members.request
.panel.panel-default
.panel-heading
- Project members
- %small
- (#{@project.users.count})
+ %strong= @project.name
+ project members
+ %span.badge= @project.users.size
.pull-right
- = link_to namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-xs" do
- %i.fa.fa-pencil-square-o
- Manage Access
- %ul.well-list.project_members
- - @project_members.each do |project_member|
- - user = project_member.user
- %li.project_member
- .list-item-name
- - if user
- %strong
- = link_to user.name, admin_user_path(user)
- - else
- %strong
- = project_member.invite_email
- (invited)
- .pull-right
- - if project_member.owner?
- %span.light Owner
- - else
- %span.light= project_member.human_access
- = link_to namespace_project_project_member_path(@project.namespace, @project, project_member), data: { confirm: remove_member_message(project_member)}, method: :delete, remote: true, class: "btn btn-sm btn-remove" do
- %i.fa.fa-times
+ = link_to icon('pencil-square-o', text: 'Manage Access'), polymorphic_url([@project, :members]), class: "btn btn-xs"
+ %ul.well-list.project_members.content-list
+ = render partial: 'shared/members/member', collection: @project_members.non_request, as: :member, locals: { show_controls: false }
.panel-footer
- = paginate @project_members, param_name: 'project_members_page', theme: 'gitlab'
+ = paginate @project_members.non_request, param_name: 'project_members_page', theme: 'gitlab'
diff --git a/app/views/admin/runners/show.html.haml b/app/views/admin/runners/show.html.haml
index e049b40bfab..61abfc6ecbe 100644
--- a/app/views/admin/runners/show.html.haml
+++ b/app/views/admin/runners/show.html.haml
@@ -28,7 +28,7 @@
.col-md-6
%h4 Restrict projects for this runner
- if @runner.projects.any?
- %table.table
+ %table.table.assigned-projects
%thead
%tr
%th Assigned projects
@@ -44,7 +44,7 @@
.pull-right
= link_to 'Disable', [:admin, project.namespace.becomes(Namespace), project, runner_project], method: :delete, class: 'btn btn-danger btn-xs'
- %table.table
+ %table.table.unassigned-projects
%thead
%tr
%th Project
diff --git a/app/views/ci/errors/show.haml b/app/views/ci/errors/show.haml
deleted file mode 100644
index 2788112c835..00000000000
--- a/app/views/ci/errors/show.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-%h3.error Error
-= @error
diff --git a/app/views/ci/shared/_guide.html.haml b/app/views/ci/shared/_guide.html.haml
deleted file mode 100644
index 09e7e653521..00000000000
--- a/app/views/ci/shared/_guide.html.haml
+++ /dev/null
@@ -1,13 +0,0 @@
-.bs-callout.help-callout
- %h4 How to setup CI for this project
-
- %ol
- %li
- Add at least one runner to the project.
- Go to #{link_to 'Runners page', runners_path(@project), target: :blank} for instructions.
- %li
- Put the .gitlab-ci.yml in the root of your repository. Examples can be found in
- #{link_to "Configuring project (.gitlab-ci.yml)", "http://doc.gitlab.com/ci/yaml/README.html", target: :blank}.
- You can also test your .gitlab-ci.yml in the #{link_to "Lint", ci_lint_path}
- %li
- Return to this page and refresh it, it should show a new build.
diff --git a/app/views/ci/shared/_no_runners.html.haml b/app/views/ci/shared/_no_runners.html.haml
deleted file mode 100644
index f56c37d9b37..00000000000
--- a/app/views/ci/shared/_no_runners.html.haml
+++ /dev/null
@@ -1,7 +0,0 @@
-.alert.alert-danger
- %p
- Now you need Runners to process your builds.
- %span
- Checkout the #{link_to 'GitLab Runner section', 'https://about.gitlab.com/gitlab-ci/#gitlab-runner', target: '_blank'} to install it
-
-
diff --git a/app/views/dashboard/_projects_head.html.haml b/app/views/dashboard/_projects_head.html.haml
index d35f332e1e0..f7abad54286 100644
--- a/app/views/dashboard/_projects_head.html.haml
+++ b/app/views/dashboard/_projects_head.html.haml
@@ -13,7 +13,7 @@
Explore Projects
.nav-controls
- = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
+ = form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
= search_field_tag :filter_projects, params[:filter_projects], placeholder: 'Filter by name...', class: 'project-filter-form-field form-control input-short projects-list-filter', spellcheck: false, id: 'project-filter-form-field', tabindex: "2"
= render 'shared/projects/dropdown'
- if current_user.can_create_project?
diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml
index a36531e095a..d6acade84f1 100644
--- a/app/views/groups/group_members/index.html.haml
+++ b/app/views/groups/group_members/index.html.haml
@@ -17,8 +17,7 @@
.panel-heading
%strong #{@group.name}
group members
- %small
- (#{@members.total_count})
+ %span.badge= @members.non_request.size
.controls
= form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do
.form-group
diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml
index 62ebd69485c..aecefbc6e8f 100644
--- a/app/views/groups/show.html.haml
+++ b/app/views/groups/show.html.haml
@@ -33,7 +33,7 @@
= link_to "#shared", 'data-toggle' => 'tab' do
Shared Projects
.nav-controls
- = form_tag request.original_url, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
+ = form_tag request.path, method: :get, class: 'project-filter-form', id: 'project-filter-form' do |f|
= search_field_tag :filter_projects, nil, placeholder: 'Filter by name', class: 'projects-list-filter form-control', spellcheck: false
= render 'shared/projects/dropdown'
- if can? current_user, :create_projects, @group
diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml
index 01648047ce2..8cc0b59edeb 100644
--- a/app/views/help/_shortcuts.html.haml
+++ b/app/views/help/_shortcuts.html.haml
@@ -28,8 +28,12 @@
.key &#8984; shift p
- else
.key ctrl shift p
-
%td Toggle Markdown preview
+ %tr
+ %td.shortcut
+ .key
+ %i.fa.fa-arrow-up
+ %td Edit last comment (when focused on an empty textarea)
%tbody
%tr
%th
diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml
index e0ed657919e..757de92d6d4 100644
--- a/app/views/layouts/_head.html.haml
+++ b/app/views/layouts/_head.html.haml
@@ -30,8 +30,8 @@
= javascript_include_tag "application"
- - if page_specific_javascripts
- = javascript_include_tag page_specific_javascripts, {"data-turbolinks-track" => true}
+ - if content_for?(:page_specific_javascripts)
+ = yield :page_specific_javascripts
= csrf_meta_tags
diff --git a/app/views/layouts/_page.html.haml b/app/views/layouts/_page.html.haml
index 199ab3c38c3..2234bf79c87 100644
--- a/app/views/layouts/_page.html.haml
+++ b/app/views/layouts/_page.html.haml
@@ -13,7 +13,7 @@
= image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
.username
= current_user.username
- = link_to '#', class: "nav-header-btn text-center pin-nav-btn #{'is-active' if pinned_nav?} js-nav-pin", title: 'Pin/Unpin navigation' do
+ = 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
diff --git a/app/views/layouts/ci/_info.html.haml b/app/views/layouts/ci/_info.html.haml
deleted file mode 100644
index 24c68a6dbf5..00000000000
--- a/app/views/layouts/ci/_info.html.haml
+++ /dev/null
@@ -1,2 +0,0 @@
-- if current_user && current_user.is_admin? && Ci::Runner.count.zero?
- = render 'ci/shared/no_runners'
diff --git a/app/views/layouts/ci/_page.html.haml b/app/views/layouts/ci/_page.html.haml
deleted file mode 100644
index 2e56d0ac6a3..00000000000
--- a/app/views/layouts/ci/_page.html.haml
+++ /dev/null
@@ -1,22 +0,0 @@
-.page-with-sidebar{ class: page_sidebar_class }
- = render "layouts/broadcast"
- .sidebar-wrapper.nicescroll{ class: nav_sidebar_class }
-
- - if defined?(sidebar) && sidebar
- = render "layouts/ci/#{sidebar}"
- - elsif current_user
- = render 'layouts/nav/dashboard'
- .collapse-nav
- = render partial: 'layouts/collapse_button'
- - if current_user
- = link_to current_user, class: 'sidebar-user', title: "Profile" do
- = image_tag avatar_icon(current_user, 60), alt: 'Profile', class: 'avatar avatar s36'
- .username
- = current_user.username
- .content-wrapper
- = render "layouts/flash"
- = render 'layouts/ci/info'
- %div{ class: container_class }
- .content
- .clearfix
- = yield
diff --git a/app/views/layouts/ci/notify.html.haml b/app/views/layouts/ci/notify.html.haml
deleted file mode 100644
index 270b206df5e..00000000000
--- a/app/views/layouts/ci/notify.html.haml
+++ /dev/null
@@ -1,19 +0,0 @@
-%html{lang: "en"}
- %head
- %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"}
- %title
- GitLab CI
-
- %body
- = yield :header
-
- %table{align: "left", border: "0", cellpadding: "0", cellspacing: "0", style: "padding: 10px 0;", width: "100%"}
- %tr
- %td{align: "left", style: "margin: 0; padding: 10px;"}
- = yield
- %br
- %tr
- %td{align: "left", style: "margin: 0; padding: 10px;"}
- %p{style: "font-size:small;color:#777"}
- - if @project
- You're receiving this notification because you are the one who triggered a build on the #{@project.name} project.
diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml
index f02ac949699..0f264cd2e06 100644
--- a/app/views/layouts/nav/_admin.html.haml
+++ b/app/views/layouts/nav/_admin.html.haml
@@ -1,60 +1,40 @@
-%ul.nav-links.scrolling-tabs
+.scrolling-tabs-container{ class: nav_control_class }
+ = render 'layouts/nav/admin_settings'
.fade-left
- = nav_link(controller: %w(dashboard admin projects users groups builds runners), html_options: {class: 'home'}) do
- = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
- %span
- Overview
- = nav_link(controller: %w(background_jobs logs health_check)) do
- = link_to admin_background_jobs_path, title: 'Monitoring' do
- %span
- Monitoring
- = nav_link(controller: :deploy_keys) do
- = link_to admin_deploy_keys_path, title: 'Deploy Keys' do
- %span
- Deploy Keys
- = nav_link(controller: :broadcast_messages) do
- = link_to admin_broadcast_messages_path, title: 'Messages' do
- %span
- Messages
- = nav_link(controller: :hooks) do
- = link_to admin_hooks_path, title: 'Hooks' do
- %span
- Hooks
-
- = nav_link(controller: :appearances) do
- = link_to admin_appearances_path, title: 'Appearances' do
- %span
- Appearance
-
- = nav_link(controller: :applications) do
- = link_to admin_applications_path, title: 'Applications' do
- %span
- Applications
-
- = nav_link(controller: :services) do
- = link_to admin_application_settings_services_path, title: 'Service Templates' do
- %span
- Service Templates
-
- = nav_link(controller: :labels) do
- = link_to admin_labels_path, title: 'Labels' do
- %span
- Labels
+ = icon('arrow-left')
+ .fade-right
+ = icon('arrow-right')
+ %ul.nav-links.scrolling-tabs
+ = nav_link(controller: %w(dashboard admin projects users groups builds runners), html_options: {class: 'home'}) do
+ = link_to admin_root_path, title: 'Overview', class: 'shortcuts-tree' do
+ %span
+ Overview
+ = nav_link(controller: %w(background_jobs logs health_check)) do
+ = link_to admin_background_jobs_path, title: 'Monitoring' do
+ %span
+ Monitoring
+ = nav_link(controller: :broadcast_messages) do
+ = link_to admin_broadcast_messages_path, title: 'Messages' do
+ %span
+ Messages
+ = nav_link(controller: :hooks) do
+ = link_to admin_hooks_path, title: 'Hooks' do
+ %span
+ System Hooks
- = nav_link(controller: :abuse_reports) do
- = link_to admin_abuse_reports_path, title: "Abuse Reports" do
- %span
- Abuse Reports
- %span.badge.count= number_with_delimiter(AbuseReport.count(:all))
+ = nav_link(controller: :applications) do
+ = link_to admin_applications_path, title: 'Applications' do
+ %span
+ Applications
- - if askimet_enabled?
- = nav_link(controller: :spam_logs) do
- = link_to admin_spam_logs_path, title: "Spam Logs" do
+ = nav_link(controller: :abuse_reports) do
+ = link_to admin_abuse_reports_path, title: "Abuse Reports" do
%span
- Spam Logs
+ Abuse Reports
+ %span.badge.count= number_with_delimiter(AbuseReport.count(:all))
- = nav_link(controller: :application_settings, html_options: { class: 'separate-item'}) do
- = link_to admin_application_settings_path, title: 'Settings' do
- %span
- Settings
- .fade-right
+ - if askimet_enabled?
+ = nav_link(controller: :spam_logs) do
+ = link_to admin_spam_logs_path, title: "Spam Logs" do
+ %span
+ Spam Logs
diff --git a/app/views/layouts/nav/_admin_settings.html.haml b/app/views/layouts/nav/_admin_settings.html.haml
new file mode 100644
index 00000000000..38e9b80d129
--- /dev/null
+++ b/app/views/layouts/nav/_admin_settings.html.haml
@@ -0,0 +1,31 @@
+.controls
+ .dropdown.admin-settings-dropdown
+ %a.dropdown-new.btn.btn-default{href: '#', 'data-toggle' => 'dropdown'}
+ = icon('cog')
+ = icon('caret-down')
+ %ul.dropdown-menu.dropdown-menu-align-right
+ = nav_link(controller: :deploy_keys) do
+ = link_to admin_deploy_keys_path, title: 'Deploy Keys' do
+ %span
+ Deploy Keys
+
+ = nav_link(controller: :services) do
+ = link_to admin_application_settings_services_path, title: 'Service Templates' do
+ %span
+ Service Templates
+
+ = nav_link(controller: :labels) do
+ = link_to admin_labels_path, title: 'Labels' do
+ %span
+ Labels
+
+ = nav_link(controller: :appearances) do
+ = link_to admin_appearances_path, title: 'Appearances' do
+ %span
+ Appearance
+
+ %li.divider
+ = nav_link(controller: :application_settings) do
+ = link_to admin_application_settings_path, title: 'Settings' do
+ %span
+ Settings
diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml
index 66361a644dd..5d657a9ac84 100644
--- a/app/views/layouts/nav/_group.html.haml
+++ b/app/views/layouts/nav/_group.html.haml
@@ -1,8 +1,10 @@
-%div{ class: nav_control_class }
+.scrolling-tabs-container{ class: nav_control_class }
= render 'layouts/nav/group_settings'
-
+ .fade-left
+ = icon('arrow-left')
+ .fade-right
+ = icon('arrow-right')
%ul.nav-links.scrolling-tabs
- .fade-left
= nav_link(path: 'groups#show', html_options: {class: 'home'}) do
= link_to group_path(@group), title: 'Home' do
%span
@@ -31,4 +33,3 @@
= link_to group_group_members_path(@group), title: 'Members' do
%span
Members
- .fade-right
diff --git a/app/views/layouts/nav/_group_settings.html.haml b/app/views/layouts/nav/_group_settings.html.haml
index dac46648b9f..3a24b09ab7e 100644
--- a/app/views/layouts/nav/_group_settings.html.haml
+++ b/app/views/layouts/nav/_group_settings.html.haml
@@ -1,16 +1,22 @@
- if current_user
- - if access = @group.users.find_by(id: current_user.id)
- .controls
- .dropdown.group-settings-dropdown
- %a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
- = icon('cog')
- = icon('caret-down')
- %ul.dropdown-menu.dropdown-menu-align-right
- - if can?(current_user, :admin_group, @group)
- = nav_link(path: 'groups#projects') do
- = link_to projects_group_path(@group), title: 'Projects' do
- Projects
- %li.divider
- %li
- = link_to edit_group_path(@group) do
- Edit Group
+ - can_edit = can?(current_user, :admin_group, @group)
+ - member = @group.members.non_request.find_by(user_id: current_user.id)
+ - can_leave = member && can?(current_user, :destroy_group_member, member)
+
+ .controls
+ .dropdown.group-settings-dropdown
+ %a.dropdown-new.btn.btn-default#group-settings-button{href: '#', 'data-toggle' => 'dropdown'}
+ = icon('cog')
+ = icon('caret-down')
+ %ul.dropdown-menu.dropdown-menu-align-right
+ = nav_link(path: 'groups#projects') do
+ = link_to 'Projects', projects_group_path(@group), title: 'Projects'
+ %li.divider
+ - if can_edit
+ %li
+ = link_to 'Edit Group', edit_group_path(@group)
+ - if can_leave
+ %li
+ = link_to polymorphic_path([:leave, @group, :members]),
+ data: { confirm: leave_confirmation_message(@group) }, method: :delete, title: 'Leave group' do
+ Leave Group
diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml
index bb6f14a6225..f37f9b0f5a3 100644
--- a/app/views/layouts/nav/_profile.html.haml
+++ b/app/views/layouts/nav/_profile.html.haml
@@ -1,46 +1,49 @@
-%ul.nav-links.scrolling-tabs
+.scrolling-tabs-container
.fade-left
- = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
- = link_to profile_path, title: 'Profile Settings' do
- %span
- Profile
- = nav_link(controller: [:accounts, :two_factor_auths]) do
- = link_to profile_account_path, title: 'Account' do
- %span
- Account
- - if current_application_settings.user_oauth_applications?
- = nav_link(controller: 'oauth/applications') do
- = link_to applications_profile_path, title: 'Applications' do
- %span
- Applications
- = nav_link(controller: :personal_access_tokens) do
- = link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do
- %span
- Personal Access Tokens
- = nav_link(controller: :emails) do
- = link_to profile_emails_path, title: 'Emails' do
- %span
- Emails
- - unless current_user.ldap_user?
- = nav_link(controller: :passwords) do
- = link_to edit_profile_password_path, title: 'Password' do
- %span
- Password
- = nav_link(controller: :notifications) do
- = link_to profile_notifications_path, title: 'Notifications' do
- %span
- Notifications
-
- = nav_link(controller: :keys) do
- = link_to profile_keys_path, title: 'SSH Keys' do
- %span
- SSH Keys
- = nav_link(controller: :preferences) do
- = link_to profile_preferences_path, title: 'Preferences' do
- %span
- Preferences
- = nav_link(path: 'profiles#audit_log') do
- = link_to audit_log_profile_path, title: 'Audit Log' do
- %span
- Audit Log
+ = icon('arrow-left')
.fade-right
+ = icon('arrow-right')
+ %ul.nav-links.scrolling-tabs
+ = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do
+ = link_to profile_path, title: 'Profile Settings' do
+ %span
+ Profile
+ = nav_link(controller: [:accounts, :two_factor_auths]) do
+ = link_to profile_account_path, title: 'Account' do
+ %span
+ Account
+ - if current_application_settings.user_oauth_applications?
+ = nav_link(controller: 'oauth/applications') do
+ = link_to applications_profile_path, title: 'Applications' do
+ %span
+ Applications
+ = nav_link(controller: :personal_access_tokens) do
+ = link_to profile_personal_access_tokens_path, title: 'Personal Access Tokens' do
+ %span
+ Personal Access Tokens
+ = nav_link(controller: :emails) do
+ = link_to profile_emails_path, title: 'Emails' do
+ %span
+ Emails
+ - unless current_user.ldap_user?
+ = nav_link(controller: :passwords) do
+ = link_to edit_profile_password_path, title: 'Password' do
+ %span
+ Password
+ = nav_link(controller: :notifications) do
+ = link_to profile_notifications_path, title: 'Notifications' do
+ %span
+ Notifications
+
+ = nav_link(controller: :keys) do
+ = link_to profile_keys_path, title: 'SSH Keys' do
+ %span
+ SSH Keys
+ = nav_link(controller: :preferences) do
+ = link_to profile_preferences_path, title: 'Preferences' do
+ %span
+ Preferences
+ = nav_link(path: 'profiles#audit_log') do
+ = link_to audit_log_profile_path, title: 'Audit Log' do
+ %span
+ Audit Log
diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml
index 39ea4920ccc..a4bb56aa56f 100644
--- a/app/views/layouts/nav/_project.html.haml
+++ b/app/views/layouts/nav/_project.html.haml
@@ -5,27 +5,31 @@
= icon('cog')
= icon('caret-down')
%ul.dropdown-menu.dropdown-menu-align-right
- - is_project_member = @project.users.exists?(current_user.id)
- - access = @project.team.max_member_access(current_user.id)
- can_edit = can?(current_user, :admin_project, @project)
+ -# We don't use @project.team.find_member because it searches for group members too...
+ - member = @project.members.non_request.find_by(user_id: current_user.id)
+ - can_leave = member && can?(current_user, :destroy_project_member, member)
- = render 'layouts/nav/project_settings', access: access, can_edit: can_edit
+ = render 'layouts/nav/project_settings', can_edit: can_edit
- - if can_edit || is_project_member
+ - if can_edit || can_leave
%li.divider
- if can_edit
%li
= link_to edit_project_path(@project) do
Edit Project
- - if is_project_member
+ - if can_leave
%li
= link_to polymorphic_path([:leave, @project, :members]),
data: { confirm: leave_confirmation_message(@project) }, method: :delete, title: 'Leave project' do
Leave Project
-%div{ class: nav_control_class }
+.scrolling-tabs-container{ class: nav_control_class }
+ .fade-left
+ = icon('arrow-left')
+ .fade-right
+ = icon('arrow-right')
%ul.nav-links.scrolling-tabs
- .fade-left
= nav_link(path: 'projects#show', html_options: {class: 'home'}) do
= link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do
%span
@@ -38,9 +42,9 @@
- if project_nav_tab? :files
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file commit commits compare repositories tags branches releases network)) do
- = link_to project_files_path(@project), title: 'Code', class: 'shortcuts-tree' do
+ = link_to project_files_path(@project), title: 'Repository', class: 'shortcuts-tree' do
%span
- Code
+ Repository
- if project_nav_tab? :pipelines
= nav_link(controller: [:pipelines, :builds, :environments]) do
@@ -109,4 +113,3 @@
%li.hidden
= link_to project_commits_path(@project), title: 'Commits', class: 'shortcuts-commits' do
Commits
- .fade-right
diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml
index 13d32bd1354..51a54b4f262 100644
--- a/app/views/layouts/nav/_project_settings.html.haml
+++ b/app/views/layouts/nav/_project_settings.html.haml
@@ -3,7 +3,7 @@
= link_to namespace_project_project_members_path(@project.namespace, @project), title: 'Members', class: 'team-tab tab' do
%span
Members
-- if access && can_edit
+- if can_edit
- if @project.allowed_to_share_with_group?
= nav_link(controller: :group_links) do
= link_to namespace_project_group_links_path(@project.namespace, @project), title: "Groups" do
diff --git a/app/views/notify/project_was_not_exported_email.html.haml b/app/views/notify/project_was_not_exported_email.html.haml
index c9e9ade2cf1..c888da29c17 100644
--- a/app/views/notify/project_was_not_exported_email.html.haml
+++ b/app/views/notify/project_was_not_exported_email.html.haml
@@ -6,4 +6,4 @@
%ul
- @errors.each do |error|
%li
- error
+ #{error}
diff --git a/app/views/notify/project_was_not_exported_email.text.erb b/app/views/notify/project_was_not_exported_email.text.erb
deleted file mode 100644
index a07f6edacf7..00000000000
--- a/app/views/notify/project_was_not_exported_email.text.erb
+++ /dev/null
@@ -1,6 +0,0 @@
-Project <%= @project.name %> couldn't be exported.
-
-The errors we encountered were:
-
-- @errors.each do |error|
-<%= error %> \ No newline at end of file
diff --git a/app/views/notify/project_was_not_exported_email.text.haml b/app/views/notify/project_was_not_exported_email.text.haml
new file mode 100644
index 00000000000..b27cb620b9e
--- /dev/null
+++ b/app/views/notify/project_was_not_exported_email.text.haml
@@ -0,0 +1,6 @@
+= "Project #{@project.name} couldn't be exported."
+
+= "The errors we encountered were:"
+
+- @errors.each do |error|
+ #{error} \ No newline at end of file
diff --git a/app/views/profiles/notifications/show.html.haml b/app/views/profiles/notifications/show.html.haml
index 5afd83a522e..f77738f97f5 100644
--- a/app/views/profiles/notifications/show.html.haml
+++ b/app/views/profiles/notifications/show.html.haml
@@ -28,7 +28,7 @@
= label_tag :global_notification_level, "Global notification level", class: "label-light"
%br
.clearfix
- .form-group.pull-left
+ .form-group.pull-left.global-notification-setting
= render 'shared/notifications/button', notification_setting: @global_notification_setting, left_align: true
.clearfix
diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml
index 28a28282fd3..ca6714ef42b 100644
--- a/app/views/projects/_md_preview.html.haml
+++ b/app/views/projects/_md_preview.html.haml
@@ -14,8 +14,17 @@
%span This is a confidential issue. Your comment will not be visible to the public.
%li.pull-right
- %button.zen-control.zen-control-full.js-zen-enter{ type: 'button', tabindex: -1 }
- Go full screen
+ .toolbar-group
+ = markdown_toolbar_button({icon: "bold fw", data: { "md-tag" => "**" }, title: "Add bold text" })
+ = markdown_toolbar_button({icon: "italic fw", data: { "md-tag" => "*" }, title: "Add italic text" })
+ = markdown_toolbar_button({icon: "quote-right fw", data: { "md-tag" => "> ", "md-prepend" => true }, title: "Insert a quote" })
+ = markdown_toolbar_button({icon: "code fw", data: { "md-tag" => "`" }, title: "Insert code" })
+ = markdown_toolbar_button({icon: "list-ul fw", data: { "md-tag" => "* ", "md-prepend" => true }, title: "Add a bullet list" })
+ = markdown_toolbar_button({icon: "list-ol fw", data: { "md-tag" => "1. ", "md-prepend" => true }, title: "Add a numbered list" })
+ = markdown_toolbar_button({icon: "check-square-o fw", data: { "md-tag" => "* [ ] ", "md-prepend" => true }, title: "Add a task list" })
+ .toolbar-group
+ %button.toolbar-btn.js-zen-enter.has-tooltip.hidden-xs{ type: "button", tabindex: -1, aria: { label: "Go full screen" }, title: "Go full screen", data: { container: "body" } }
+ =icon("arrows-alt fw")
.md-write-holder
= yield
@@ -24,7 +33,7 @@
- if defined?(referenced_users) && referenced_users
%div.referenced-users.hide
%span
- = icon('exclamation-triangle')
+ = icon("exclamation-triangle")
You are about to add
%strong
%span.js-referenced-users-count 0
diff --git a/app/views/projects/_merge_request_settings.html.haml b/app/views/projects/_merge_request_settings.html.haml
index da522b53417..771a2e0df7d 100644
--- a/app/views/projects/_merge_request_settings.html.haml
+++ b/app/views/projects/_merge_request_settings.html.haml
@@ -8,4 +8,4 @@
%strong Only allow merge requests to be merged if the build succeeds
.help-block
Builds need to be configured to enable this feature.
- = link_to icon('question-circle'), help_page_path('workflow', 'merge_requests#only-allow-merge-requests-to-be-merged-if-the-build-succeeds')
+ = link_to icon('question-circle'), help_page_path('workflow', 'merge_requests', anchor: 'only-allow-merge-requests-to-be-merged-if-the-build-succeeds')
diff --git a/app/views/projects/badges/index.html.haml b/app/views/projects/badges/index.html.haml
index ee63bc55a30..ac80951dd4f 100644
--- a/app/views/projects/badges/index.html.haml
+++ b/app/views/projects/badges/index.html.haml
@@ -7,7 +7,7 @@
%b Builds badge &middot;
= @build_badge.to_html
.pull-right
- = render 'shared/ref_switcher', destination: 'badges'
+ = render 'shared/ref_switcher', destination: 'badges', align_right: true
.panel-body
.row
.col-md-2.text-center
diff --git a/app/views/projects/blob/_editor.html.haml b/app/views/projects/blob/_editor.html.haml
index ae89637df60..29c7d45074a 100644
--- a/app/views/projects/blob/_editor.html.haml
+++ b/app/views/projects/blob/_editor.html.haml
@@ -17,6 +17,8 @@
= dropdown_tag("Choose a License template", options: { toggle_class: 'js-license-selector', title: "Choose a license", filter: true, placeholder: "Filter", data: { data: licenses_for_select, project: @project.name, fullname: @project.namespace.human_name } } )
.gitignore-selector.js-gitignore-selector-wrap.hidden
= dropdown_tag("Choose a .gitignore template", options: { toggle_class: 'js-gitignore-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitignore_names } } )
+ .gitlab-ci-yml-selector.js-gitlab-ci-yml-selector-wrap.hidden
+ = dropdown_tag("Choose a GitLab CI Yaml template", options: { toggle_class: 'js-gitlab-ci-yml-selector', title: "Choose a template", filter: true, placeholder: "Filter", data: { data: gitlab_ci_ymls } } )
.encoding-selector
= select_tag :encoding, options_for_select([ "base64", "text" ], "text"), class: 'select2'
diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml
index b8d8758fd2b..e38d1ff5ff0 100644
--- a/app/views/projects/ci/pipelines/_pipeline.html.haml
+++ b/app/views/projects/ci/pipelines/_pipeline.html.haml
@@ -24,8 +24,8 @@
%span.label.label-warning stuck
%p.commit-title
- - if commit_data = pipeline.commit_data
- = link_to_gfm truncate(commit_data.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit_data.id), class: "commit-row-message"
+ - if commit = pipeline.commit
+ = link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message"
- else
Cant find HEAD commit for this branch
diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml
index a959b34a539..929496f81d8 100644
--- a/app/views/projects/commits/_commit.html.haml
+++ b/app/views/projects/commits/_commit.html.haml
@@ -10,29 +10,30 @@
= cache(cache_key) do
%li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" }
= commit_author_avatar(commit, size: 36)
- .commit-row-title
- %span.item-title
- = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
- %span.commit-row-message.visible-xs-inline
- &middot;
- = commit.short_id
- - if commit.status
- = render_commit_status(commit, cssclass: 'visible-xs-inline')
- - if commit.description?
- %a.text-expander.hidden-xs.js-toggle-button ...
+ .commit-info-block
+ .commit-row-title
+ %span.item-title
+ = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message"
+ %span.commit-row-message.visible-xs-inline
+ &middot;
+ = commit.short_id
+ - if commit.status
+ = render_commit_status(commit, cssclass: 'visible-xs-inline')
+ - if commit.description?
+ %a.text-expander.hidden-xs.js-toggle-button ...
- .commit-actions.hidden-xs
- - if commit.status
- = render_commit_status(commit, cssclass: 'btn btn-transparent')
- = clipboard_button_with_class({ clipboard_text: commit.id }, css_class: 'btn-transparent')
- = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
- = link_to_browse_code(project, commit)
+ .commit-actions.hidden-xs
+ - if commit.status
+ = render_commit_status(commit, cssclass: 'btn btn-transparent')
+ = clipboard_button_with_class({ clipboard_text: commit.id }, css_class: 'btn-transparent')
+ = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent"
+ = link_to_browse_code(project, commit)
- - if commit.description?
- %pre.commit-row-description.js-toggle-content
- = preserve(markdown(escape_once(commit.description), pipeline: :single_line, author: commit.author))
+ - if commit.description?
+ %pre.commit-row-description.js-toggle-content
+ = preserve(markdown(escape_once(commit.description), pipeline: :single_line, author: commit.author))
- .commit-row-info
- = commit_author_link(commit, avatar: false, size: 24)
- authored
- #{time_ago_with_tooltip(commit.committed_date)}
+ .commit-row-info
+ = commit_author_link(commit, avatar: false, size: 24)
+ authored
+ #{time_ago_with_tooltip(commit.committed_date)}
diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml
index c8aa849c217..b11b6c24ccd 100644
--- a/app/views/projects/commits/_head.html.haml
+++ b/app/views/projects/commits/_head.html.haml
@@ -1,7 +1,10 @@
-.scrolling-tabs-container
+.scrolling-tabs-container.sub-nav-scroll
+ .fade-left
+ = icon('arrow-left')
+ .fade-right
+ = icon('arrow-right')
.nav-links.sub-nav.scrolling-tabs
%ul{ class: (container_class) }
- .fade-left
= nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do
= link_to project_files_path(@project) do
Files
@@ -25,4 +28,3 @@
= nav_link(controller: [:tags, :releases]) do
= link_to namespace_project_tags_path(@project.namespace, @project) do
Tags
- .fade-right
diff --git a/app/views/projects/compare/index.html.haml b/app/views/projects/compare/index.html.haml
index c322942aeba..b22285c11e0 100644
--- a/app/views/projects/compare/index.html.haml
+++ b/app/views/projects/compare/index.html.haml
@@ -3,7 +3,7 @@
= render "projects/commits/head"
%div{ class: (container_class) }
- .row-content-block.second-block.content-component-block
+ .sub-header-block
Compare branches, tags or commit ranges.
%br
Fill input field with commit id like
diff --git a/app/views/projects/compare/show.html.haml b/app/views/projects/compare/show.html.haml
index cdc34f51d6d..f4ec7b767f6 100644
--- a/app/views/projects/compare/show.html.haml
+++ b/app/views/projects/compare/show.html.haml
@@ -1,24 +1,24 @@
+- @no_container = true
- page_title "#{params[:from]}...#{params[:to]}"
= render "projects/commits/head"
+%div{ class: (container_class) }
+ .sub-header-block.no-bottom-space
+ = render "form"
-.row-content-block
- = render "form"
-
-- if @commits.present?
- .prepend-top-default
+ - if @commits.present?
= render "projects/commits/commit_list"
= render "projects/diffs/diffs", diffs: @diffs, project: @project, diff_refs: @diff_refs
-- else
- .light-well.prepend-top-default
- .center
- %h4
- There isn't anything to compare.
- %p.slead
- - if params[:to] == params[:from]
- %span.label-branch #{params[:from]}
- and
- %span.label-branch #{params[:to]}
- are the same.
- - else
- You'll need to use different branch names to get a valid comparison.
+ - else
+ .light-well
+ .center
+ %h4
+ There isn't anything to compare.
+ %p.slead
+ - if params[:to] == params[:from]
+ %span.label-branch #{params[:from]}
+ and
+ %span.label-branch #{params[:to]}
+ are the same.
+ - else
+ You'll need to use different branch names to get a valid comparison.
diff --git a/app/views/projects/container_registry/_tag.html.haml b/app/views/projects/container_registry/_tag.html.haml
index f35faa6afb5..10822b6184c 100644
--- a/app/views/projects/container_registry/_tag.html.haml
+++ b/app/views/projects/container_registry/_tag.html.haml
@@ -3,9 +3,9 @@
= escape_once(tag.name)
= clipboard_button(clipboard_text: "docker pull #{tag.path}")
%td
- - if layer = tag.layers.first
- %span.has-tooltip{ title: "#{layer.revision}" }
- = layer.short_revision
+ - if tag.revision
+ %span.has-tooltip{ title: "#{tag.revision}" }
+ = tag.short_revision
- else
\-
%td
diff --git a/app/views/projects/environments/index.html.haml b/app/views/projects/environments/index.html.haml
index ae9e77e7d89..a03f117291f 100644
--- a/app/views/projects/environments/index.html.haml
+++ b/app/views/projects/environments/index.html.haml
@@ -3,16 +3,24 @@
= render "projects/pipelines/head"
%div{ class: (container_class) }
- - if can?(current_user, :create_environment, @project)
+ - if can?(current_user, :create_environment, @project) && !@environments.blank?
.top-area
.nav-controls
= link_to new_namespace_project_environment_path(@project.namespace, @project), class: 'btn btn-create' do
New environment
- if @environments.blank?
- %ul.content-list.environments
- %li.nothing-here-block
- No environments to show
+ .blank-state.blank-state-no-icon
+ %h2.blank-state-title
+ You don't have any environments right now.
+ %p.blank-state-text
+ Environments are places where code gets deployed, such as staging or production.
+ %br
+ = succeed "." do
+ = link_to "Read more about environments", help_page_path("ci", "environments")
+ - if can?(current_user, :create_environment, @project)
+ = link_to new_namespace_project_environment_path(@project.namespace, @project), class: 'btn btn-create' do
+ New environment
- else
.table-holder
%table.table.environments
diff --git a/app/views/projects/environments/new.html.haml b/app/views/projects/environments/new.html.haml
index 54465828ba9..da325efecd2 100644
--- a/app/views/projects/environments/new.html.haml
+++ b/app/views/projects/environments/new.html.haml
@@ -4,6 +4,9 @@
.col-lg-3
%h4.prepend-top-0
New Environment
- %p Environments allow you to track deployments of your application
+ %p
+ Environments allow you to track deployments of your application
+ = succeed "." do
+ = link_to "Read more about environments", help_page_path("ci", "environments")
= render 'form'
diff --git a/app/views/projects/environments/show.html.haml b/app/views/projects/environments/show.html.haml
index 069b77b5adf..4c15e2759d6 100644
--- a/app/views/projects/environments/show.html.haml
+++ b/app/views/projects/environments/show.html.haml
@@ -13,10 +13,14 @@
= link_to 'Destroy', namespace_project_environment_path(@project.namespace, @project, @environment), data: { confirm: 'Are you sure you want to delete this environment?' }, class: 'btn btn-danger', method: :delete
- if @deployments.blank?
- %ul.content-list.environments
- %li.nothing-here-block
- No deployments for
- %strong= @environment.name
+ .blank-state.blank-state-no-icon
+ %h2.blank-state-title
+ You don't have any deployments right now.
+ %p.blank-state-text
+ Define environments in the deploy stage(s) in
+ %code .gitlab-ci.yml
+ to track deployments here.
+ = link_to "Read more", help_page_path("ci", "environments"), class: "btn btn-success"
- else
.table-holder
%table.table.environments
diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml
index 4bcf2d9d533..dbe9ddfde2f 100644
--- a/app/views/projects/forks/index.html.haml
+++ b/app/views/projects/forks/index.html.haml
@@ -40,9 +40,3 @@
= render 'projects', projects: @forks
-
-- if @private_forks_count > 0
- .private-forks-notice
- = icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
- %strong= pluralize(@private_forks_count, 'private fork')
- %span you have no access to.
diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml
index 8becaea246f..ca347406dfe 100644
--- a/app/views/projects/graphs/_head.html.haml
+++ b/app/views/projects/graphs/_head.html.haml
@@ -1,12 +1,16 @@
-- page_specific_javascripts asset_path("graphs/application.js")
-%ul.nav-links
- = nav_link(action: :show) do
- = link_to 'Contributors', namespace_project_graph_path
- = nav_link(action: :commits) do
- = link_to 'Commits', commits_namespace_project_graph_path
- = nav_link(action: :languages) do
- = link_to 'Languages', languages_namespace_project_graph_path
- - if @project.builds_enabled?
- = nav_link(action: :ci) do
- = link_to ci_namespace_project_graph_path do
- Continuous Integration
+.nav-links.sub-nav
+ %ul{ class: (container_class) }
+
+ - content_for :page_specific_javascripts do
+ = page_specific_javascript_tag('lib/chart.js')
+ = page_specific_javascript_tag('graphs/application.js')
+ = nav_link(action: :show) do
+ = link_to 'Contributors', namespace_project_graph_path
+ = nav_link(action: :commits) do
+ = link_to 'Commits', commits_namespace_project_graph_path
+ = nav_link(action: :languages) do
+ = link_to 'Languages', languages_namespace_project_graph_path
+ - if @project.builds_enabled?
+ = nav_link(action: :ci) do
+ = link_to ci_namespace_project_graph_path do
+ Continuous Integration
diff --git a/app/views/projects/graphs/ci.html.haml b/app/views/projects/graphs/ci.html.haml
index 19ccc125ea8..e695d3ae369 100644
--- a/app/views/projects/graphs/ci.html.haml
+++ b/app/views/projects/graphs/ci.html.haml
@@ -1,15 +1,18 @@
+- @no_container = true
- page_title "Continuous Integration", "Graphs"
= render 'head'
-.row-content-block.append-bottom-default
- .oneline
- A collection of graphs for Continuous Integration
-#charts.ci-charts
- .row
- .col-md-6
- = render 'projects/graphs/ci/overall'
- .col-md-6
- = render 'projects/graphs/ci/build_times'
+%div{ class: (container_class) }
+ .sub-header-block
+ .oneline
+ A collection of graphs for Continuous Integration
- %hr
- = render 'projects/graphs/ci/builds'
+ #charts.ci-charts
+ .row
+ .col-md-6
+ = render 'projects/graphs/ci/overall'
+ .col-md-6
+ = render 'projects/graphs/ci/build_times'
+
+ %hr
+ = render 'projects/graphs/ci/builds'
diff --git a/app/views/projects/graphs/commits.html.haml b/app/views/projects/graphs/commits.html.haml
index d9b2fb6c065..0daffe68f6f 100644
--- a/app/views/projects/graphs/commits.html.haml
+++ b/app/views/projects/graphs/commits.html.haml
@@ -1,52 +1,54 @@
+- @no_container = true
- page_title "Commits", "Graphs"
= render 'head'
-.row-content-block.append-bottom-default
- .tree-ref-holder
- = render 'shared/ref_switcher', destination: 'graphs_commits'
- %ul.breadcrumb.repo-breadcrumb
- = commits_breadcrumbs
+%div{ class: (container_class) }
+ .sub-header-block
+ .tree-ref-holder
+ = render 'shared/ref_switcher', destination: 'graphs_commits'
+ %ul.breadcrumb.repo-breadcrumb
+ = commits_breadcrumbs
-%p.lead
- Commit statistics for
- %strong #{@ref}
- #{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')}
+ %p.lead
+ Commit statistics for
+ %strong #{@ref}
+ #{@commits_graph.start_date.strftime('%b %d')} - #{@commits_graph.end_date.strftime('%b %d')}
-.row
- .col-md-6
- %ul
- %li
- %p.lead
- %strong #{@commits_graph.commits.size}
- commits during
- %strong #{@commits_graph.duration}
- days
- %li
- %p.lead
- Average
- %strong #{@commits_graph.commit_per_day}
- commits per day
- %li
- %p.lead
- Contributed by
- %strong #{@commits_graph.authors}
- authors
- .col-md-6
- %div
- %p.slead
- Commits per day of month
- %canvas#month-chart
-.row
- .col-md-6
- %div
- %p.slead
- Commits per day hour (UTC)
- %canvas#hour-chart
- .col-md-6
- %div
- %p.slead
- Commits per weekday
- %canvas#weekday-chart
+ .row
+ .col-md-6
+ %ul
+ %li
+ %p.lead
+ %strong #{@commits_graph.commits.size}
+ commits during
+ %strong #{@commits_graph.duration}
+ days
+ %li
+ %p.lead
+ Average
+ %strong #{@commits_graph.commit_per_day}
+ commits per day
+ %li
+ %p.lead
+ Contributed by
+ %strong #{@commits_graph.authors}
+ authors
+ .col-md-6
+ %div
+ %p.slead
+ Commits per day of month
+ %canvas#month-chart
+ .row
+ .col-md-6
+ %div
+ %p.slead
+ Commits per day hour (UTC)
+ %canvas#hour-chart
+ .col-md-6
+ %div
+ %p.slead
+ Commits per weekday
+ %canvas#weekday-chart
:javascript
var responsiveChart = function (selector, data) {
diff --git a/app/views/projects/graphs/languages.html.haml b/app/views/projects/graphs/languages.html.haml
index 249c16f4709..6d97f552a8e 100644
--- a/app/views/projects/graphs/languages.html.haml
+++ b/app/views/projects/graphs/languages.html.haml
@@ -1,24 +1,26 @@
+- @no_container = true
- page_title "Languages", "Graphs"
= render 'head'
-.row-content-block.append-bottom-default
- .oneline
- Programming languages used in this repository
+%div{ class: (container_class) }
+ .sub-header-block
+ .oneline
+ Programming languages used in this repository
-.row
- .col-md-8
- %canvas#languages-chart{ height: 400 }
- .col-md-4
- %ul.bordered-list
- - @languages.each do |language|
- %li
- %span{ style: "color: #{language[:color]}" }
- = icon('circle')
- &nbsp;
- = language[:label]
- .pull-right
- = language[:value]
- \%
+ .row
+ .col-md-8
+ %canvas#languages-chart{ height: 400 }
+ .col-md-4
+ %ul.bordered-list
+ - @languages.each do |language|
+ %li
+ %span{ style: "color: #{language[:color]}" }
+ = icon('circle')
+ &nbsp;
+ = language[:label]
+ .pull-right
+ = language[:value]
+ \%
:javascript
var data = #{@languages.to_json};
diff --git a/app/views/projects/graphs/show.html.haml b/app/views/projects/graphs/show.html.haml
index 33970e7b909..9f7e2a361ff 100644
--- a/app/views/projects/graphs/show.html.haml
+++ b/app/views/projects/graphs/show.html.haml
@@ -1,29 +1,31 @@
+- @no_container = true
- page_title "Contributors", "Graphs"
= render 'head'
-.row-content-block.append-bottom-default
- .tree-ref-holder
- = render 'shared/ref_switcher', destination: 'graphs'
- %ul.breadcrumb.repo-breadcrumb
- = commits_breadcrumbs
-
-.loading-graph
- .center
- %h3.page-title
- %i.fa.fa-spinner.fa-spin
- Building repository graph.
- %p.slead Please wait a moment, this page will automatically refresh when ready.
-
-.stat-graph.hide
- .header.clearfix
- %h3#date_header.page-title
- %p.light
- Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits.
- %input#brush_change{:type => "hidden"}
- .graphs
- #contributors-master
- #contributors.clearfix
- %ol.contributors-list.clearfix
+%div{ class: (container_class) }
+ .sub-header-block
+ .tree-ref-holder
+ = render 'shared/ref_switcher', destination: 'graphs'
+ %ul.breadcrumb.repo-breadcrumb
+ = commits_breadcrumbs
+
+ .loading-graph
+ .center
+ %h3.page-title
+ %i.fa.fa-spinner.fa-spin
+ Building repository graph.
+ %p.slead Please wait a moment, this page will automatically refresh when ready.
+
+ .stat-graph.hide
+ .header.clearfix
+ %h3#date_header.page-title
+ %p.light
+ Commits to #{@ref}, excluding merge commits. Limited to 6,000 commits.
+ %input#brush_change{:type => "hidden"}
+ .graphs.row
+ #contributors-master
+ #contributors.clearfix
+ %ol.contributors-list.clearfix
diff --git a/app/views/projects/merge_requests/_discussion.html.haml b/app/views/projects/merge_requests/_discussion.html.haml
index 393998f15b9..53dd300c35c 100644
--- a/app/views/projects/merge_requests/_discussion.html.haml
+++ b/app/views/projects/merge_requests/_discussion.html.haml
@@ -1,8 +1,8 @@
- content_for :note_actions do
- if can?(current_user, :update_merge_request, @merge_request)
- if @merge_request.open?
- = link_to 'Close merge request', merge_request_path(@merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-nr btn-comment btn-grouped btn-close close-mr-link js-note-target-close", title: "Close merge request", data: {original_text: "Close merge request", alternative_text: "Comment & close merge request"}
+ = link_to 'Close merge request', merge_request_path(@merge_request, merge_request: {state_event: :close }), method: :put, class: "btn btn-nr btn-comment btn-close close-mr-link js-note-target-close", title: "Close merge request", data: {original_text: "Close merge request", alternative_text: "Comment & close merge request"}
- if @merge_request.closed?
- = link_to 'Reopen merge request', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-nr btn-comment btn-grouped btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request", data: {original_text: "Reopen merge request", alternative_text: "Comment & reopen merge request"}
+ = link_to 'Reopen merge request', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: "btn btn-nr btn-comment btn-reopen reopen-mr-link js-note-target-reopen", title: "Reopen merge request", data: {original_text: "Reopen merge request", alternative_text: "Comment & reopen merge request"}
#notes= render "projects/notes/notes_with_form"
diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml
index e4ab064eda8..3ca30b4ba6b 100644
--- a/app/views/projects/network/show.html.haml
+++ b/app/views/projects/network/show.html.haml
@@ -1,5 +1,7 @@
- page_title "Network", @ref
-- page_specific_javascripts asset_path("network/application.js")
+- content_for :page_specific_javascripts do
+ = page_specific_javascript_tag('lib/raphael.js')
+ = page_specific_javascript_tag('network/application.js')
= render "projects/commits/head"
= render "head"
%div{ class: (container_class) }
@@ -15,5 +17,5 @@
= check_box_tag :filter_ref, 1, @options[:filter_ref]
%span Begin with the selected commit
- .network-graph{ data: { url: '#{escape_javascript(@url)}', commit_url: '#{escape_javascript(@commit_url)}', ref: '#{escape_javascript(@ref)}', commit_id: '#{escape_javascript(@commit.id)}' } }
+ .network-graph{ data: { url: @url, commit_url: @commit_url, ref: @ref, commit_id: @commit.id } }
= spinner nil, true
diff --git a/app/views/projects/notes/_hints.html.haml b/app/views/projects/notes/_hints.html.haml
index 0b002043408..7d1cbc62e86 100644
--- a/app/views/projects/notes/_hints.html.haml
+++ b/app/views/projects/notes/_hints.html.haml
@@ -5,4 +5,4 @@
is supported
%button.toolbar-button.markdown-selector{ type: 'button', tabindex: '-1' }
= icon('file-image-o', class: 'toolbar-button-icon')
- Attach a file
+ Attach a file \ No newline at end of file
diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml
index bcdbff08011..a5e163b91e9 100644
--- a/app/views/projects/notes/_note.html.haml
+++ b/app/views/projects/notes/_note.html.haml
@@ -18,9 +18,9 @@
= time_ago_with_tooltip(note.created_at, placement: 'bottom', html_class: 'note-created-ago')
.note-actions
- access = note.project.team.human_max_access(note.author.id)
- - if access
+ - if access and not note.system
%span.note-role.hidden-xs= access
- - if current_user
+ - if current_user and not note.system
= link_to '#', title: 'Award Emoji', class: 'note-action-button note-emoji-button js-add-award js-note-emoji', data: { position: 'right' } do
= icon('spinner spin')
= icon('smile-o')
@@ -32,7 +32,7 @@
.note-body{class: note_editable ? 'js-task-list-container' : ''}
.note-text
= preserve do
- = markdown(note.note, pipeline: :note, cache_key: [note, "note"], author: note.author)
+ = note.note_html
= edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true)
- if note_editable
= render 'projects/notes/edit_form', note: note
diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml
index cb6136c215a..e783d8c72c5 100644
--- a/app/views/projects/project_members/_group_members.html.haml
+++ b/app/views/projects/project_members/_group_members.html.haml
@@ -2,8 +2,7 @@
.panel-heading
%strong #{@group.name}
group members
- %small
- (#{members.count})
+ %span.badge= members.size
- if can?(current_user, :admin_group_member, @group)
.controls
= link_to 'Manage group members',
diff --git a/app/views/projects/project_members/_shared_group_members.html.haml b/app/views/projects/project_members/_shared_group_members.html.haml
index 952844acefc..840b57c2e63 100644
--- a/app/views/projects/project_members/_shared_group_members.html.haml
+++ b/app/views/projects/project_members/_shared_group_members.html.haml
@@ -1,6 +1,7 @@
- @project_group_links.each do |group_links|
- shared_group = group_links.group
- - shared_group_users_count = group_links.group.group_members.count
+ - shared_group_members = shared_group.members.non_request
+ - shared_group_users_count = shared_group_members.size
.panel.panel-default
.panel-heading
Shared with
@@ -15,7 +16,7 @@
Edit group members
%ul.content-list
= render partial: 'shared/members/member',
- collection: shared_group.group_members.order(access_level: :desc).limit(20),
+ collection: shared_group_members.order(access_level: :desc).limit(20),
as: :member,
locals: { show_controls: false, show_roles: false }
- if shared_group_users_count > 20
diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml
index 03207614258..b0bfdd235f7 100644
--- a/app/views/projects/project_members/_team.html.haml
+++ b/app/views/projects/project_members/_team.html.haml
@@ -2,8 +2,7 @@
.panel-heading
%strong #{@project.name}
project members
- %small
- (#{members.count})
+ %span.badge= members.size
.controls
= form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do
.form-group
diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml
index 357ccccaf1d..a2026c41d01 100644
--- a/app/views/projects/project_members/index.html.haml
+++ b/app/views/projects/project_members/index.html.haml
@@ -18,7 +18,7 @@
= render 'team', members: @project_members.non_request
- if @group
- = render "group_members", members: @group_members
+ = render "group_members", members: @group_members.non_request
- if @project_group_links.any? && @project.allowed_to_share_with_group?
= render "shared_group_members"
diff --git a/app/views/projects/runners/_form.html.haml b/app/views/projects/runners/_form.html.haml
index d62f5c8f131..c45a9d4f81f 100644
--- a/app/views/projects/runners/_form.html.haml
+++ b/app/views/projects/runners/_form.html.haml
@@ -13,6 +13,12 @@
= f.check_box :run_untagged
%span.light Indicates whether this runner can pick jobs without tags
.form-group
+ = label :locked, 'Lock to current projects', class: 'control-label'
+ .col-sm-10
+ .checkbox
+ = f.check_box :locked
+ %span.light When a runner is locked, it cannot be assigned to other projects
+ .form-group
= label_tag :token, class: 'control-label' do
Token
.col-sm-10
diff --git a/app/views/projects/runners/_runner.html.haml b/app/views/projects/runners/_runner.html.haml
index 96e2aac451f..85225857758 100644
--- a/app/views/projects/runners/_runner.html.haml
+++ b/app/views/projects/runners/_runner.html.haml
@@ -2,8 +2,10 @@
%h4
= runner_status_icon(runner)
%span.monospace
- - if @runners.include?(runner)
+ - if @project_runners.include?(runner)
= link_to runner.short_sha, runner_path(runner)
+ - if runner.locked?
+ = icon('lock', class: 'has-tooltip', title: 'Locked to current projects')
%small
= link_to edit_namespace_project_runner_path(@project.namespace, @project, runner) do
%i.fa.fa-edit.btn
@@ -11,7 +13,7 @@
= runner.short_sha
.pull-right
- - if @runners.include?(runner)
+ - if @project_runners.include?(runner)
- if runner.belongs_to_one_project?
= link_to 'Remove runner', runner_path(runner), data: { confirm: "Are you sure?" }, method: :delete, class: 'btn btn-danger btn-sm'
- else
diff --git a/app/views/projects/runners/_specific_runners.html.haml b/app/views/projects/runners/_specific_runners.html.haml
index 8ae9f0d95f7..d469dda5b81 100644
--- a/app/views/projects/runners/_specific_runners.html.haml
+++ b/app/views/projects/runners/_specific_runners.html.haml
@@ -17,13 +17,13 @@
Start runner!
-- if @runners.any?
+- if @project_runners.any?
%h4.underlined-title Runners activated for this project
%ul.bordered-list.activated-specific-runners
- = render partial: 'runner', collection: @runners, as: :runner
+ = render partial: 'runner', collection: @project_runners, as: :runner
-- if @specific_runners.any?
+- if @assignable_runners.any?
%h4.underlined-title Available specific runners
%ul.bordered-list.available-specific-runners
- = render partial: 'runner', collection: @specific_runners, as: :runner
- = paginate @specific_runners
+ = render partial: 'runner', collection: @assignable_runners, as: :runner
+ = paginate @assignable_runners
diff --git a/app/views/projects/runners/show.html.haml b/app/views/projects/runners/show.html.haml
index f24e1b9144e..61b99f35d74 100644
--- a/app/views/projects/runners/show.html.haml
+++ b/app/views/projects/runners/show.html.haml
@@ -23,6 +23,9 @@
%td Can run untagged jobs
%td= @runner.run_untagged? ? 'Yes' : 'No'
%tr
+ %td Locked to this project
+ %td= @runner.locked? ? 'Yes' : 'No'
+ %tr
%td Tags
%td
- @runner.tag_list.each do |tag|
diff --git a/app/views/projects/show.html.haml b/app/views/projects/show.html.haml
index e9ca46a74bf..15f0d85194b 100644
--- a/app/views/projects/show.html.haml
+++ b/app/views/projects/show.html.haml
@@ -57,6 +57,10 @@
%li.missing
= link_to add_special_file_path(@project, file_name: 'CONTRIBUTING.md', commit_message: 'Add contribution guide') do
Add Contribution guide
+ - unless @repository.gitlab_ci_yml
+ %li.missing
+ = link_to add_special_file_path(@project, file_name: '.gitlab-ci.yml') do
+ Set Up CI
- if @repository.commit
.content-block.second-block.white
diff --git a/app/views/projects/wikis/_main_links.html.haml b/app/views/projects/wikis/_main_links.html.haml
index 4faa547769b..4ea75dbbf0c 100644
--- a/app/views/projects/wikis/_main_links.html.haml
+++ b/app/views/projects/wikis/_main_links.html.haml
@@ -1,4 +1,7 @@
- if (@page && @page.persisted?)
+ - if can?(current_user, :create_wiki, @project)
+ = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
+ New Page
= link_to namespace_project_wiki_history_path(@project.namespace, @project, @page), class: "btn" do
Page History
- if can?(current_user, :create_wiki, @project)
diff --git a/app/views/projects/wikis/_nav.html.haml b/app/views/projects/wikis/_nav.html.haml
index 988fe024e28..f8ea479e0b1 100644
--- a/app/views/projects/wikis/_nav.html.haml
+++ b/app/views/projects/wikis/_nav.html.haml
@@ -1,5 +1,5 @@
-.top-area
- %ul.nav-links
+.nav-links.sub-nav
+ %ul{ class: (container_class) }
= nav_link(html_options: {class: params[:id] == 'home' ? 'active' : '' }) do
= link_to 'Home', namespace_project_wiki_path(@project.namespace, @project, :home)
@@ -10,9 +10,4 @@
= link_to namespace_project_wikis_git_access_path(@project.namespace, @project) do
Git Access
- .nav-controls
- - if can?(current_user, :create_wiki, @project)
- = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
- New Page
-
-= render 'projects/wikis/new'
+ = render 'projects/wikis/new'
diff --git a/app/views/projects/wikis/_new.html.haml b/app/views/projects/wikis/_new.html.haml
index 919daf0a7b2..4f8abcdc8e1 100644
--- a/app/views/projects/wikis/_new.html.haml
+++ b/app/views/projects/wikis/_new.html.haml
@@ -1,14 +1,17 @@
-%div#modal-new-wiki.modal
- .modal-dialog
- .modal-content
- .modal-header
- %a.close{href: "#", "data-dismiss" => "modal"} ×
- %h3.page-title New Wiki Page
- .modal-body
- %form.new-wiki-page
- .form-group
- = label_tag :new_wiki_path do
- %span Page slug
- = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true
- .form-actions
- = button_tag 'Create Page', class: 'build-new-wiki btn btn-create'
+- @no_container = true
+
+%div{ class: (container_class) }
+ %div#modal-new-wiki.modal
+ .modal-dialog
+ .modal-content
+ .modal-header
+ %a.close{href: "#", "data-dismiss" => "modal"} ×
+ %h3.page-title New Wiki Page
+ .modal-body
+ %form.new-wiki-page
+ .form-group
+ = label_tag :new_wiki_path do
+ %span Page slug
+ = text_field_tag :new_wiki_path, nil, placeholder: 'how-to-setup', class: 'form-control', required: true, :'data-wikis-path' => namespace_project_wikis_path(@project.namespace, @project), autofocus: true
+ .form-actions
+ = button_tag 'Create Page', class: 'build-new-wiki btn btn-create'
diff --git a/app/views/projects/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml
index cbd69ee1a73..817bf9b3f69 100644
--- a/app/views/projects/wikis/edit.html.haml
+++ b/app/views/projects/wikis/edit.html.haml
@@ -1,19 +1,25 @@
+- @no_container = true
- page_title "Edit", @page.title.capitalize, "Wiki"
= render 'nav'
-.top-area
- .nav-text.wiki-page
- %strong
- - if @page.persisted?
- = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
- - else
- = @page.title.capitalize
- %span.light
- &middot;
- Edit Page
+%div{ class: (container_class) }
+ .top-area
+ .nav-text
+ %strong
+ - if @page.persisted?
+ = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
+ - else
+ = @page.title.capitalize
+ %span.light
+ &middot;
+ Edit Page
- .nav-controls
- = render 'main_links'
+ .nav-controls
+ - if !(@page && @page.persisted?)
+ - if can?(current_user, :create_wiki, @project)
+ = link_to '#modal-new-wiki', class: "add-new-wiki btn btn-new", "data-toggle" => "modal" do
+ New Page
+ = render 'main_links'
-= render 'form'
+ = render 'form'
diff --git a/app/views/projects/wikis/git_access.html.haml b/app/views/projects/wikis/git_access.html.haml
index ccceab6155e..6caf7230f35 100644
--- a/app/views/projects/wikis/git_access.html.haml
+++ b/app/views/projects/wikis/git_access.html.haml
@@ -1,32 +1,34 @@
+- @no_container = true
- page_title "Git Access", "Wiki"
= render 'nav'
-.row-content-block
- %span.oneline
- Git access for
- %strong= @project_wiki.path_with_namespace
+%div{ class: (container_class) }
+ .sub-header-block
+ %span.oneline
+ Git access for
+ %strong= @project_wiki.path_with_namespace
- .pull-right
- = render "shared/clone_panel", project: @project_wiki
+ .pull-right
+ = render "shared/clone_panel", project: @project_wiki
-.git-empty.prepend-top-default
- %fieldset
- %legend Install Gollum:
- %pre.dark
- :preserve
- gem install gollum
+ .prepend-top-default
+ %fieldset
+ %legend Install Gollum:
+ %pre.dark
+ :preserve
+ gem install gollum
- %legend Clone Your Wiki:
- %pre.dark
- :preserve
- git clone #{ content_tag(:span, default_url_to_repo(@project_wiki), class: 'clone')}
- cd #{h @project_wiki.path}
+ %legend Clone Your Wiki:
+ %pre.dark
+ :preserve
+ git clone #{ content_tag(:span, default_url_to_repo(@project_wiki), class: 'clone')}
+ cd #{h @project_wiki.path}
- %legend Start Gollum And Edit Locally:
- %pre.dark
- :preserve
- gollum
- == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
- >> Thin web server (v1.5.0 codename Knife)
- >> Maximum connections set to 1024
- >> Listening on 0.0.0.0:4567, CTRL+C to stop
+ %legend Start Gollum And Edit Locally:
+ %pre.dark
+ :preserve
+ gollum
+ == Sinatra/1.3.5 has taken the stage on 4567 for development with backup from Thin
+ >> Thin web server (v1.5.0 codename Knife)
+ >> Maximum connections set to 1024
+ >> Listening on 0.0.0.0:4567, CTRL+C to stop
diff --git a/app/views/projects/wikis/history.html.haml b/app/views/projects/wikis/history.html.haml
index 45460ed9f41..630ee35b70b 100644
--- a/app/views/projects/wikis/history.html.haml
+++ b/app/views/projects/wikis/history.html.haml
@@ -1,37 +1,37 @@
- page_title "History", @page.title.capitalize, "Wiki"
= render 'nav'
+%div{ class: (container_class) }
+ .top-area
+ .nav-text
+ %strong
+ = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
+ %span.light
+ &middot;
+ History
-.top-area
- .nav-text
- %strong
- = link_to @page.title.capitalize, namespace_project_wiki_path(@project.namespace, @project, @page)
- %span.light
- &middot;
- History
-
-.table-holder
- %table.table
- %thead
- %tr
- %th Page version
- %th Author
- %th Commit Message
- %th Last updated
- %th Format
- %tbody
- - @page.versions.each_with_index do |version, index|
- - commit = version
+ .table-holder
+ %table.table
+ %thead
%tr
- %td
- = link_to project_wiki_path_with_version(@project, @page,
- commit.id, index == 0) do
- = truncate_sha(commit.id)
- %td
- = commit.author.name
- %td
- = commit.message
- %td
- #{time_ago_with_tooltip(version.authored_date)}
- %td
- %strong
- = @page.page.wiki.page(@page.page.name, commit.id).try(:format)
+ %th Page version
+ %th Author
+ %th Commit Message
+ %th Last updated
+ %th Format
+ %tbody
+ - @page.versions.each_with_index do |version, index|
+ - commit = version
+ %tr
+ %td
+ = link_to project_wiki_path_with_version(@project, @page,
+ commit.id, index == 0) do
+ = truncate_sha(commit.id)
+ %td
+ = commit.author.name
+ %td
+ = commit.message
+ %td
+ #{time_ago_with_tooltip(version.authored_date)}
+ %td
+ %strong
+ = @page.page.wiki.page(@page.page.name, commit.id).try(:format)
diff --git a/app/views/projects/wikis/pages.html.haml b/app/views/projects/wikis/pages.html.haml
index 2f6162fa3c5..81d9f391c1c 100644
--- a/app/views/projects/wikis/pages.html.haml
+++ b/app/views/projects/wikis/pages.html.haml
@@ -1,12 +1,14 @@
+- @no_container = true
- page_title "Pages", "Wiki"
= render 'nav'
-%ul.content-list
- - @wiki_pages.each do |wiki_page|
- %li
- = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page)
- %small (#{wiki_page.format})
- .pull-right
- %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)}
-= paginate @wiki_pages, theme: 'gitlab'
+%div{ class: (container_class) }
+ %ul.content-list
+ - @wiki_pages.each do |wiki_page|
+ %li
+ = link_to wiki_page.title, namespace_project_wiki_path(@project.namespace, @project, wiki_page)
+ %small (#{wiki_page.format})
+ .pull-right
+ %small Last edited #{time_ago_with_tooltip(wiki_page.commit.authored_date)}
+ = paginate @wiki_pages, theme: 'gitlab'
diff --git a/app/views/projects/wikis/show.html.haml b/app/views/projects/wikis/show.html.haml
index 9166c0edb3b..76f9b1ecd76 100644
--- a/app/views/projects/wikis/show.html.haml
+++ b/app/views/projects/wikis/show.html.haml
@@ -1,24 +1,26 @@
+- @no_container = true
- page_title @page.title.capitalize, "Wiki"
= render 'nav'
-.top-area
- .nav-text
- %strong= @page.title.capitalize
+%div{ class: (container_class) }
+ .top-area
+ .nav-text
+ %strong= @page.title.capitalize
- %span.wiki-last-edit-by
- &middot;
- last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
+ %span.wiki-last-edit-by
+ &middot;
+ last edited by #{@page.commit.author.name} #{time_ago_with_tooltip(@page.commit.authored_date)}
- .nav-controls
- = render 'main_links'
+ .nav-controls
+ = render 'main_links'
-- if @page.historical?
- .warning_message
- This is an old version of this page.
- You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", namespace_project_wiki_history_path(@project.namespace, @project, @page)}.
+ - if @page.historical?
+ .warning_message
+ This is an old version of this page.
+ You can view the #{link_to "most recent version", namespace_project_wiki_path(@project.namespace, @project, @page)} or browse the #{link_to "history", namespace_project_wiki_history_path(@project.namespace, @project, @page)}.
-.wiki-holder.prepend-top-default.append-bottom-default
- .wiki
- = preserve do
- = render_wiki_content(@page)
+ .wiki-holder.prepend-top-default.append-bottom-default
+ .wiki
+ = preserve do
+ = render_wiki_content(@page)
diff --git a/app/views/search/results/_blob.html.haml b/app/views/search/results/_blob.html.haml
index 0fe8a3b490a..290743feb4a 100644
--- a/app/views/search/results/_blob.html.haml
+++ b/app/views/search/results/_blob.html.haml
@@ -2,9 +2,10 @@
.blob-result
.file-holder
.file-title
- = link_to namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename), :anchor => "L" + blob.startline.to_s) do
+ - blob_link = namespace_project_blob_path(@project.namespace, @project, tree_join(blob.ref, blob.filename))
+ = link_to blob_link do
%i.fa.fa-file
%strong
= blob.filename
.file-content.code.term
- = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline
+ = render 'shared/file_highlight', blob: blob, first_line_number: blob.startline, blob_link: blob_link
diff --git a/app/views/shared/_event_filter.html.haml b/app/views/shared/_event_filter.html.haml
index 30055002213..8824bcc158e 100644
--- a/app/views/shared/_event_filter.html.haml
+++ b/app/views/shared/_event_filter.html.haml
@@ -1,7 +1,5 @@
%ul.nav-links.event-filter.scrolling-tabs
- .fade-left
= event_filter_link EventFilter.push, 'Push events'
= event_filter_link EventFilter.merged, 'Merge events'
= event_filter_link EventFilter.comments, 'Comments'
= event_filter_link EventFilter.team, 'Team'
- .fade-right
diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml
index 37dcf39c062..ad944a19ca1 100644
--- a/app/views/shared/_file_highlight.html.haml
+++ b/app/views/shared/_file_highlight.html.haml
@@ -2,11 +2,12 @@
.line-numbers
- if blob.data.present?
- link_icon = icon('link')
+ - link = blob_link if defined?(blob_link)
- blob.data.each_line.each_with_index do |_, index|
- offset = defined?(first_line_number) ? first_line_number : 1
- i = index + offset
-# We're not using `link_to` because it is too slow once we get to thousands of lines.
- %a.diff-line-num{href: "#L#{i}", id: "L#{i}", 'data-line-number' => i}
+ %a.diff-line-num{href: "#{link}#L#{i}", id: "L#{i}", 'data-line-number' => i}
= link_icon
= i
.blob-content{data: {blob_id: blob.id}}
diff --git a/app/views/shared/_ref_switcher.html.haml b/app/views/shared/_ref_switcher.html.haml
index eb2e1919e19..ea7162d4d63 100644
--- a/app/views/shared/_ref_switcher.html.haml
+++ b/app/views/shared/_ref_switcher.html.haml
@@ -1,7 +1,14 @@
+- dropdown_toggle_text = @ref || @project.default_branch
= form_tag switch_namespace_project_refs_path(@project.namespace, @project), method: :get, class: "project-refs-form" do
- = select_tag "ref", grouped_options_refs, class: "project-refs-select select2 select2-sm"
= hidden_field_tag :destination, destination
- if defined?(path)
= hidden_field_tag :path, path
- @options && @options.each do |key, value|
= hidden_field_tag key, value, id: nil
+ .dropdown
+ = dropdown_toggle dropdown_toggle_text, { toggle: "dropdown", selected: dropdown_toggle_text, ref: @ref, refs_url: refs_namespace_project_path(@project.namespace, @project) }, { toggle_class: "js-project-refs-dropdown" }
+ .dropdown-menu.dropdown-menu-selectable{ class: ("dropdown-menu-align-right" if local_assigns[:align_right]) }
+ = dropdown_title "Switch branch/tag"
+ = dropdown_filter "Search branches and tags"
+ = dropdown_content
+ = dropdown_loading
diff --git a/app/views/shared/members/_requests.html.haml b/app/views/shared/members/_requests.html.haml
index b5963876034..e4bd2bdc265 100644
--- a/app/views/shared/members/_requests.html.haml
+++ b/app/views/shared/members/_requests.html.haml
@@ -3,6 +3,6 @@
.panel-heading
%strong= membership_source.name
access requests
- %small= "(#{members.size})"
+ %span.badge= members.size
%ul.content-list
= render partial: 'shared/members/member', collection: members, as: :member
diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml
index 47b66d44e43..3c03c220ddd 100644
--- a/app/views/shared/milestones/_issuable.html.haml
+++ b/app/views/shared/milestones/_issuable.html.haml
@@ -21,7 +21,8 @@
= link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, label_name: label.title, state: 'all' }) do
- render_colored_label(label)
- - if assignee
- = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }),
- class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do
- - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '')
+ %span{ class: "assignee-icon" }
+ - if assignee
+ = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }),
+ class: 'has-tooltip', title: "Assigned to #{assignee.name}", data: { container: 'body' } do
+ - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '')
diff --git a/app/views/shared/projects/_list.html.haml b/app/views/shared/projects/_list.html.haml
index 2e08bb2ac08..3a9dd37dc7d 100644
--- a/app/views/shared/projects/_list.html.haml
+++ b/app/views/shared/projects/_list.html.haml
@@ -16,6 +16,12 @@
= render "shared/projects/project", project: project, skip_namespace: skip_namespace,
avatar: avatar, stars: stars, css_class: css_class, ci: ci, use_creator_avatar: use_creator_avatar,
forks: forks, show_last_commit_as_description: show_last_commit_as_description
+
+ - if @private_forks_count && @private_forks_count > 0
+ %li.project-row.private-forks-notice
+ = icon('lock fw', base: 'circle', class: 'fa-lg private-fork-icon')
+ %strong= pluralize(@private_forks_count, 'private fork')
+ %span you have no access to.
= paginate(projects, remote: remote, theme: "gitlab") if projects.respond_to? :total_pages
- else
.nothing-here-block No projects found
diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml
index 92305594a81..68665858c3e 100644
--- a/app/views/users/show.html.haml
+++ b/app/views/users/show.html.haml
@@ -1,6 +1,8 @@
- page_title @user.name
- page_description @user.bio
-- page_specific_javascripts asset_path("users/application.js")
+- content_for :page_specific_javascripts do
+ = page_specific_javascript_tag('lib/d3.js')
+ = page_specific_javascript_tag('users/application.js')
- header_title @user.name, user_path(@user)
- @no_container = true