diff options
Diffstat (limited to 'app')
134 files changed, 660 insertions, 407 deletions
diff --git a/app/assets/images/auth_buttons/azure_64.png b/app/assets/images/auth_buttons/azure_64.png Binary files differindex a82c751e001..85de7793440 100644 --- a/app/assets/images/auth_buttons/azure_64.png +++ b/app/assets/images/auth_buttons/azure_64.png diff --git a/app/assets/images/auth_buttons/bitbucket_64.png b/app/assets/images/auth_buttons/bitbucket_64.png Binary files differindex 4b90a57bc7d..b3d022a5a70 100644 --- a/app/assets/images/auth_buttons/bitbucket_64.png +++ b/app/assets/images/auth_buttons/bitbucket_64.png diff --git a/app/assets/images/auth_buttons/facebook_64.png b/app/assets/images/auth_buttons/facebook_64.png Binary files differindex 1f1a80d7368..71ffb1c6a1f 100644 --- a/app/assets/images/auth_buttons/facebook_64.png +++ b/app/assets/images/auth_buttons/facebook_64.png diff --git a/app/assets/images/auth_buttons/github_64.png b/app/assets/images/auth_buttons/github_64.png Binary files differindex 182a1a3f734..1fa19c55d2f 100644 --- a/app/assets/images/auth_buttons/github_64.png +++ b/app/assets/images/auth_buttons/github_64.png diff --git a/app/assets/images/auth_buttons/gitlab_64.png b/app/assets/images/auth_buttons/gitlab_64.png Binary files differindex 99a40583b3a..f675678dc9d 100644 --- a/app/assets/images/auth_buttons/gitlab_64.png +++ b/app/assets/images/auth_buttons/gitlab_64.png diff --git a/app/assets/images/auth_buttons/google_64.png b/app/assets/images/auth_buttons/google_64.png Binary files differindex fb64f8bee68..720824230a5 100644 --- a/app/assets/images/auth_buttons/google_64.png +++ b/app/assets/images/auth_buttons/google_64.png diff --git a/app/assets/images/auth_buttons/twitter_64.png b/app/assets/images/auth_buttons/twitter_64.png Binary files differindex e3bd9169a34..a4f14de57ae 100644 --- a/app/assets/images/auth_buttons/twitter_64.png +++ b/app/assets/images/auth_buttons/twitter_64.png diff --git a/app/assets/images/bg_fallback.png b/app/assets/images/bg_fallback.png Binary files differindex e5fe659ba63..5c55bc79dec 100644 --- a/app/assets/images/bg_fallback.png +++ b/app/assets/images/bg_fallback.png diff --git a/app/assets/images/dark-scheme-preview.png b/app/assets/images/dark-scheme-preview.png Binary files differindex 2ef58e52549..8855babf147 100644 --- a/app/assets/images/dark-scheme-preview.png +++ b/app/assets/images/dark-scheme-preview.png diff --git a/app/assets/images/emoji.png b/app/assets/images/emoji.png Binary files differindex 1e7cf79ea45..99093d4725a 100644 --- a/app/assets/images/emoji.png +++ b/app/assets/images/emoji.png diff --git a/app/assets/images/emoji@2x.png b/app/assets/images/emoji@2x.png Binary files differindex 74d67f7520d..48989942a9f 100644 --- a/app/assets/images/emoji@2x.png +++ b/app/assets/images/emoji@2x.png diff --git a/app/assets/images/gitlab_logo.png b/app/assets/images/gitlab_logo.png Binary files differindex 0c157546b9c..ca30b459019 100644 --- a/app/assets/images/gitlab_logo.png +++ b/app/assets/images/gitlab_logo.png diff --git a/app/assets/images/gitorious-logo-black.png b/app/assets/images/gitorious-logo-black.png Binary files differindex 78f17a9af79..4a55fdc225a 100644 --- a/app/assets/images/gitorious-logo-black.png +++ b/app/assets/images/gitorious-logo-black.png diff --git a/app/assets/images/gitorious-logo-blue.png b/app/assets/images/gitorious-logo-blue.png Binary files differindex 4962cffba31..5eaa327d3df 100644 --- a/app/assets/images/gitorious-logo-blue.png +++ b/app/assets/images/gitorious-logo-blue.png diff --git a/app/assets/images/icon-link.png b/app/assets/images/icon-link.png Binary files differindex 7d89da97e11..5b55e12571c 100644 --- a/app/assets/images/icon-link.png +++ b/app/assets/images/icon-link.png diff --git a/app/assets/images/images.png b/app/assets/images/images.png Binary files differindex ad146246caf..bd60de994c4 100644 --- a/app/assets/images/images.png +++ b/app/assets/images/images.png diff --git a/app/assets/images/monokai-scheme-preview.png b/app/assets/images/monokai-scheme-preview.png Binary files differindex fbb339c6a91..d9c7d2d8041 100644 --- a/app/assets/images/monokai-scheme-preview.png +++ b/app/assets/images/monokai-scheme-preview.png diff --git a/app/assets/images/msapplication-tile.png b/app/assets/images/msapplication-tile.png Binary files differindex 58bbf2b20cb..1e0e2ed73ce 100644 --- a/app/assets/images/msapplication-tile.png +++ b/app/assets/images/msapplication-tile.png diff --git a/app/assets/images/no_avatar.png b/app/assets/images/no_avatar.png Binary files differindex 8287acbce13..5383d687b53 100644 --- a/app/assets/images/no_avatar.png +++ b/app/assets/images/no_avatar.png diff --git a/app/assets/images/no_group_avatar.png b/app/assets/images/no_group_avatar.png Binary files differindex bfb31bb2184..71612d55286 100644 --- a/app/assets/images/no_group_avatar.png +++ b/app/assets/images/no_group_avatar.png diff --git a/app/assets/images/slider_handles.png b/app/assets/images/slider_handles.png Binary files differindex 884378ec96a..52ad11ab7a1 100644 --- a/app/assets/images/slider_handles.png +++ b/app/assets/images/slider_handles.png diff --git a/app/assets/images/touch-icon-ipad-retina.png b/app/assets/images/touch-icon-ipad-retina.png Binary files differindex feb32b48ec9..516dc2f4710 100644 --- a/app/assets/images/touch-icon-ipad-retina.png +++ b/app/assets/images/touch-icon-ipad-retina.png diff --git a/app/assets/images/touch-icon-ipad.png b/app/assets/images/touch-icon-ipad.png Binary files differindex a6ddc543509..b2093d015b8 100644 --- a/app/assets/images/touch-icon-ipad.png +++ b/app/assets/images/touch-icon-ipad.png diff --git a/app/assets/images/touch-icon-iphone-retina.png b/app/assets/images/touch-icon-iphone-retina.png Binary files differindex 8bf7ccb7534..438654e0d20 100644 --- a/app/assets/images/touch-icon-iphone-retina.png +++ b/app/assets/images/touch-icon-iphone-retina.png diff --git a/app/assets/images/touch-icon-iphone.png b/app/assets/images/touch-icon-iphone.png Binary files differindex 87da550f8be..e5f87fbbcf6 100644 --- a/app/assets/images/touch-icon-iphone.png +++ b/app/assets/images/touch-icon-iphone.png diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index 0206db461da..b6dbf2d0cc1 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 @@ -199,7 +199,6 @@ $ -> $('.header-content .header-logo').toggle() $('.header-content .navbar-collapse').toggle() $('.navbar-toggle').toggleClass('active') - $('.navbar-toggle i').toggleClass("fa-angle-right fa-angle-left") # Show/hide comments on diff $body.on "click", ".js-toggle-diff-comments", (e) -> @@ -261,7 +260,7 @@ $ -> new Aside() # Sidenav pinning - if $window.width() < 1440 and $.cookie('pin_nav') is 'true' + if $window.width() < 1280 and $.cookie('pin_nav') is 'true' $.cookie('pin_nav', 'false', { path: '/' }) $('.page-with-sidebar') .toggleClass('page-sidebar-collapsed page-sidebar-expanded') diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 030f1564862..37d0adaa625 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -341,7 +341,9 @@ class @AwardsHandler for emoji in frequentlyUsedEmojis $(".emoji-menu-content [data-emoji='#{emoji}']").closest('li').clone().appendTo(ul) - $('input.emoji-search').after(ul).after($('<h5>').text('Frequently used')) + $('.emoji-menu-content') + .prepend(ul) + .prepend($('<h5>').text('Frequently used')) @frequentEmojiBlockRendered = true @@ -356,7 +358,7 @@ class @AwardsHandler if term # Generate a search result block - h5 = $('<h5>').text('Search results').addClass('emoji-search') + h5 = $('<h5>').text('Search results') found_emojis = @searchEmojis(term).show() ul = $('<ul>').addClass('emoji-menu-list emoji-menu-search').append(found_emojis) $('.emoji-menu-content ul, .emoji-menu-content h5').hide() diff --git a/app/assets/javascripts/dispatcher.js.coffee b/app/assets/javascripts/dispatcher.js.coffee index 7fbff9214cf..9493a575801 100644 --- a/app/assets/javascripts/dispatcher.js.coffee +++ b/app/assets/javascripts/dispatcher.js.coffee @@ -84,6 +84,8 @@ class Dispatcher new Activities() when 'groups:show' shortcut_handler = new ShortcutsNavigation() + new NotificationsForm() + new NotificationsDropdown() when 'groups:group_members:index' new GroupMembers() new UsersSelect() diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index 703128fecb3..ed9dfcc917e 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -186,6 +186,8 @@ class GitLabDropdown @fullData = data @parseData @fullData + + @filter.input.trigger('keyup') if @options.filterable and @filter and @filter.input } # Init filterable @@ -218,6 +220,13 @@ class GitLabDropdown @dropdown.on 'keyup', (e) => if e.which is 27 # Escape key $('.dropdown-menu-close', @dropdown).trigger 'click' + @dropdown.on 'blur', 'a', (e) => + if e.relatedTarget? + $relatedTarget = $(e.relatedTarget) + $dropdownMenu = $relatedTarget.closest('.dropdown-menu') + + if $dropdownMenu.length is 0 + @dropdown.removeClass('open') if @dropdown.find(".dropdown-toggle-page").length @dropdown.find(".dropdown-toggle-page, .dropdown-menu-back").on "click", (e) => 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/issuable.js.coffee b/app/assets/javascripts/issuable.js.coffee index 6a108c033ea..0527c66461c 100644 --- a/app/assets/javascripts/issuable.js.coffee +++ b/app/assets/javascripts/issuable.js.coffee @@ -59,13 +59,12 @@ issuable_created = false filterResults: (form) => formData = form.serialize() - $('.issues-holder, .merge-requests-holder').css('opacity', '0.5') formAction = form.attr('action') issuesUrl = formAction issuesUrl += ("#{if formAction.indexOf('?') < 0 then '?' else '&'}") issuesUrl += formData - Turbolinks.visit(issuesUrl); + Turbolinks.visit(issuesUrl) initChecks: -> @issuableBulkActions = $('.bulk-update').data('bulkActions') 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/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/text_utility.js.coffee b/app/assets/javascripts/lib/utils/text_utility.js.coffee index bb2772dfed2..7bcb876d056 100644 --- a/app/assets/javascripts/lib/text_utility.js.coffee +++ b/app/assets/javascripts/lib/utils/text_utility.js.coffee @@ -10,17 +10,41 @@ gl.text.selectedText = (text, textarea) -> text.substring(textarea.selectionStart, textarea.selectionEnd) - gl.text.insertText = (textArea, text, tag, selected, wrap) -> + gl.text.lineBefore = (text, textarea) -> + split = text.substring(0, textarea.selectionStart).trim().split('\n') + split[split.length - 1] + + gl.text.lineAfter = (text, textarea) -> + text.substring(textarea.selectionEnd).trim().split('\n')[0] + + gl.text.blockTagText = (text, textArea, blockTag, selected) -> + lineBefore = @lineBefore(text, textArea) + lineAfter = @lineAfter(text, textArea) + + if lineBefore is blockTag and lineAfter is blockTag + # To remove the block tag we have to select the line before & after + if blockTag? + textArea.selectionStart = textArea.selectionStart - (blockTag.length + 1) + textArea.selectionEnd = textArea.selectionEnd + (blockTag.length + 1) + + selected + else + "#{blockTag}\n#{selected}\n#{blockTag}" + + gl.text.insertText = (textArea, text, tag, blockTag, 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') + if selectedSplit.length > 1 and (not wrap or blockTag?) + if blockTag? + insertText = @blockTagText(text, textArea, blockTag, selected) + else + 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 ' '}" @@ -51,7 +75,7 @@ textArea.setSelectionRange pos, pos - gl.text.updateText = (textArea, tag, wrap) -> + gl.text.updateText = (textArea, tag, blockTag, wrap) -> $textArea = $(textArea) oldVal = $textArea.val() textArea = $textArea.get(0) @@ -59,7 +83,7 @@ selected = @selectedText(text, textArea) $textArea.focus() - @insertText(textArea, text, tag, selected, wrap) + @insertText(textArea, text, tag, blockTag, selected, wrap) gl.text.init = (form) -> self = @ @@ -70,6 +94,7 @@ self.updateText( $this.closest('.md-area').find('textarea'), $this.data('md-tag'), + $this.data('md-block'), not $this.data('md-prepend') ) 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/search_autocomplete.js.coffee b/app/assets/javascripts/search_autocomplete.js.coffee index 421328554b8..72b1d3dfb1e 100644 --- a/app/assets/javascripts/search_autocomplete.js.coffee +++ b/app/assets/javascripts/search_autocomplete.js.coffee @@ -171,22 +171,15 @@ class @SearchAutocomplete } bindEvents: -> - $(document).on 'click', @onDocumentClick @searchInput.on 'keydown', @onSearchInputKeyDown @searchInput.on 'keyup', @onSearchInputKeyUp @searchInput.on 'click', @onSearchInputClick @searchInput.on 'focus', @onSearchInputFocus + @searchInput.on 'blur', @onSearchInputBlur @clearInput.on 'click', @onClearInputClick @locationBadgeEl.on 'click', => @searchInput.focus() - onDocumentClick: (e) => - # If clicking outside the search box - # And search input is not focused - # And we are not clicking inside a suggestion - if not $.contains(@dropdown[0], e.target) and @isFocused and not $(e.target).closest('.search-form').length - @onSearchInputBlur() - enableAutocomplete: -> # No need to enable anything if user is not logged in return if !gon.current_user_id @@ -287,8 +280,6 @@ class @SearchAutocomplete value: @originalState._location ) - @dropdown.removeClass 'open' - badgePresent: -> @locationBadgeEl.length 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/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 38023818709..41e77a4ac68 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -137,7 +137,7 @@ margin: 0; font-size: 24px; font-weight: normal; - margin-bottom: 5px; + margin-bottom: 10px; color: #4c4e54; font-size: 23px; line-height: 1.1; diff --git a/app/assets/stylesheets/framework/nav.scss b/app/assets/stylesheets/framework/nav.scss index 6211bc04597..6e5f216c894 100644 --- a/app/assets/stylesheets/framework/nav.scss +++ b/app/assets/stylesheets/framework/nav.scss @@ -21,9 +21,8 @@ .fa { position: relative; - top: 3px; - font-size: 13px; - color: $btn-placeholder-gray; + top: 5px; + font-size: 18px; } } diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index 98f917ce69b..e8d6a7f2775 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -1,5 +1,6 @@ .page-with-sidebar { padding-top: $header-height; + padding-bottom: 25px; transition: padding $sidebar-transition-duration; .sidebar-wrapper { diff --git a/app/assets/stylesheets/framework/variables.scss b/app/assets/stylesheets/framework/variables.scss index c37574ca7a1..87f8a17659f 100644 --- a/app/assets/stylesheets/framework/variables.scss +++ b/app/assets/stylesheets/framework/variables.scss @@ -7,7 +7,7 @@ $gutter_collapsed_width: 62px; $gutter_width: 290px; $gutter_inner_width: 258px; $sidebar-transition-duration: .15s; -$sidebar-breakpoint: 1440px; +$sidebar-breakpoint: 1280px; /* * UI elements diff --git a/app/assets/stylesheets/pages/awards.scss b/app/assets/stylesheets/pages/awards.scss index 6211f3a52eb..5faedfedd66 100644 --- a/app/assets/stylesheets/pages/awards.scss +++ b/app/assets/stylesheets/pages/awards.scss @@ -8,8 +8,9 @@ .emoji-menu { position: absolute; margin-top: 3px; - z-index: 1000; - min-width: 160px; + padding: $gl-padding; + z-index: 9; + width: 300px; font-size: 14px; background-color: $award-emoji-menu-bg; border: 1px solid $award-emoji-menu-border; @@ -33,20 +34,18 @@ } .emoji-menu-content { - padding: $gl-padding; - width: 300px; height: 300px; overflow-y: scroll; - - input.emoji-search { - background-image: url("data:image/png;base64,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"); - background-repeat: no-repeat; - background-position: right 5px center; - background-size: 16px; - } } } +.emoji-search { + background-image: url("data:image/png;base64,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"); + background-repeat: no-repeat; + background-position: right 5px center; + background-size: 16px; +} + .emoji-menu-list { list-style: none; padding-left: 0; diff --git a/app/assets/stylesheets/pages/commits.scss b/app/assets/stylesheets/pages/commits.scss index de534d28421..85bbf70e188 100644 --- a/app/assets/stylesheets/pages/commits.scss +++ b/app/assets/stylesheets/pages/commits.scss @@ -83,11 +83,7 @@ position: relative; @media (min-width: $screen-sm-min) { - padding-left: 20px; - - .commit-info-block { - padding-left: 44px; - } + padding-left: 46px; } &:not(:last-child) { @@ -102,9 +98,7 @@ .avatar { - position: absolute; - top: 10px; - left: 16px; + margin-left: -46px; } .item-title { diff --git a/app/assets/stylesheets/pages/groups.scss b/app/assets/stylesheets/pages/groups.scss index ac7721cbe15..101faf59174 100644 --- a/app/assets/stylesheets/pages/groups.scss +++ b/app/assets/stylesheets/pages/groups.scss @@ -48,11 +48,7 @@ .access-request-button { @include btn-gray; - position: absolute; - right: 16px; - bottom: 32px; - padding: 3px 10px; + margin-right: 10px; text-transform: none; - background-color: $background-color; } } diff --git a/app/assets/stylesheets/pages/help.scss b/app/assets/stylesheets/pages/help.scss index 0b710ef168b..00ab42bec5c 100644 --- a/app/assets/stylesheets/pages/help.scss +++ b/app/assets/stylesheets/pages/help.scss @@ -63,5 +63,6 @@ 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 21ff6ab71f0..542fa244689 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -10,6 +10,7 @@ border: 1px solid $table-border-gray; padding: 5px; margin: 5px; + max-height: calc(100vh - 100px); } } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index aca82f7f7bf..124f4afaa0d 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -264,8 +264,15 @@ margin-bottom: 4px; } + .item-title { + @media (min-width: $screen-sm-min) { + width: 49%; + } + } + .avatar { - margin-left: 0; + left: 0; + top: 2px; } .commit-row-info { diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index ffba3dc5bc6..ac8c02b59dc 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -41,6 +41,10 @@ ul.notes { .timeline-icon { .avatar { visibility: hidden; + + .discussion-body & { + visibility: visible; + } } } } @@ -113,6 +117,7 @@ ul.notes { 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 d3e59d7fdb9..89ce1b2df20 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -13,10 +13,53 @@ .new_project, .edit-project { - fieldset.features { - .control-label { + fieldset { + &.features .control-label { font-weight: normal; } + .form-group { + margin-bottom: 5px; + } + &> .form-group { + padding-left: 0; + } + } + .help-block { + margin-bottom: 10px; + } + .project-path { + padding-right: 0; + .form-control { + border-radius: $border-radius-base; + } + } + .input-group > div { + &:last-child { + padding-right: 0; + } + } + @media (max-width: $screen-xs-max) { + .input-group > div { + margin-bottom: 14px; + &:last-child { + margin-bottom: 0; + } + } + fieldset > .form-group:first-child { + padding-right: 0; + } + } + + .input-group-addon { + &.static-namespace { + height: 35px; + border-radius: 3px; + border: 1px solid #e5e5e5; + } + &+ .select2 a { + border-top-left-radius: 0; + border-bottom-left-radius: 0; + } } } @@ -365,10 +408,28 @@ a.deploy-project-label { } } -.project-import .btn { - float: left; - margin-bottom: 10px; - margin-right: 10px; +.project-import { + .form-group { + margin-bottom: 0; + } + .import-buttons { + padding-left: 0; + display: -webkit-flex; + display: flex; + -webkit-flex-wrap: wrap; + flex-wrap: wrap; + .btn { + margin-right: 10px; + padding: 8px 12px; + } + &> div { + margin-bottom: 14px; + padding-left: 0; + &:last-child { + margin-bottom: 0; + } + } + } } .project-stats { diff --git a/app/assets/stylesheets/pages/tree.scss b/app/assets/stylesheets/pages/tree.scss index 99c9e81ddb9..5b61270daa8 100644 --- a/app/assets/stylesheets/pages/tree.scss +++ b/app/assets/stylesheets/pages/tree.scss @@ -101,7 +101,8 @@ margin: 0; .commit { - padding: 0 0 0 55px; + padding-top: 0; + padding-bottom: 0; .commit-row-title { .commit-row-message { diff --git a/app/controllers/admin/system_info_controller.rb b/app/controllers/admin/system_info_controller.rb new file mode 100644 index 00000000000..3c67370b667 --- /dev/null +++ b/app/controllers/admin/system_info_controller.rb @@ -0,0 +1,13 @@ +class Admin::SystemInfoController < Admin::ApplicationController + def show + system_info = Vmstat.snapshot + + @cpus = system_info.cpus.length + + @mem_used = system_info.memory.active_bytes + @mem_total = system_info.memory.total_bytes + + @disk_used = system_info.disks[0].used_bytes + @disk_total = system_info.disks[0].total_bytes + end +end diff --git a/app/controllers/ci/projects_controller.rb b/app/controllers/ci/projects_controller.rb index 8bf71a1adbb..aa894fde36b 100644 --- a/app/controllers/ci/projects_controller.rb +++ b/app/controllers/ci/projects_controller.rb @@ -25,7 +25,7 @@ module Ci return render_404 unless @project image = Ci::ImageForBuildService.new.execute(@project, params) - send_file image.path, filename: image.name, disposition: 'inline', type:"image/svg+xml" + send_file image.path, filename: image.name, disposition: 'inline', type: "image/svg+xml" end protected diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb index 71ba6153021..de6bc689bb7 100644 --- a/app/controllers/dashboard/groups_controller.rb +++ b/app/controllers/dashboard/groups_controller.rb @@ -1,5 +1,5 @@ class Dashboard::GroupsController < Dashboard::ApplicationController def index - @group_members = current_user.group_members.page(params[:page]) + @group_members = current_user.group_members.includes(:source).page(params[:page]) end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index ee4fcc4e360..a04bf7df722 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -37,15 +37,12 @@ class GroupsController < Groups::ApplicationController end def show - @last_push = current_user.recent_push if current_user - - @projects = @projects.includes(:namespace) - @projects = @projects.sorted_by_activity - @projects = filter_projects(@projects) - @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.page(params[:page]) if params[:filter_projects].blank? + if current_user + @last_push = current_user.recent_push + @notification_setting = current_user.notification_settings_for(group) + end - @shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user) + setup_projects respond_to do |format| format.html @@ -97,6 +94,16 @@ class GroupsController < Groups::ApplicationController protected + def setup_projects + @projects = @projects.includes(:namespace) + @projects = @projects.sorted_by_activity + @projects = filter_projects(@projects) + @projects = @projects.sort(@sort = params[:sort]) + @projects = @projects.page(params[:page]) if params[:filter_projects].blank? + + @shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user) + end + def authorize_create_group! unless can?(current_user, :create_group, nil) return render_404 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/notification_settings_controller.rb b/app/controllers/notification_settings_controller.rb index eddd03cc229..8ec4bb1233f 100644 --- a/app/controllers/notification_settings_controller.rb +++ b/app/controllers/notification_settings_controller.rb @@ -2,11 +2,9 @@ class NotificationSettingsController < ApplicationController before_action :authenticate_user! def create - project = Project.find(params[:project][:id]) + return render_404 unless can_read?(resource) - return render_404 unless can?(current_user, :read_project, project) - - @notification_setting = current_user.notification_settings_for(project) + @notification_setting = current_user.notification_settings_for(resource) @saved = @notification_setting.update_attributes(notification_setting_params) render_response @@ -21,6 +19,22 @@ class NotificationSettingsController < ApplicationController private + def resource + @resource ||= + if params[:project_id].present? + Project.find(params[:project_id]) + elsif params[:namespace_id].present? + Group.find(params[:namespace_id]) + end + end + + def can_read?(resource) + ability_name = resource.class.name.downcase + ability_name = "read_#{ability_name}".to_sym + + can?(current_user, ability_name, resource) + end + def render_response render json: { html: view_to_html_string("shared/notifications/_button", notification_setting: @notification_setting), 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/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 6751737d15e..d162a5a3165 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -18,9 +18,16 @@ class Projects::CommitController < Projects::ApplicationController apply_diff_view_cookie! @grouped_diff_notes = commit.notes.grouped_diff_notes + @notes = commit.notes.non_diff_notes.fresh + + Banzai::NoteRenderer.render( + @grouped_diff_notes.values.flatten + @notes, + @project, + current_user, + ) @note = @project.build_commit_note(commit) - @notes = commit.notes.non_diff_notes.fresh + @noteable = @commit @comments_target = { noteable_type: 'Commit', 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 089669841d3..dd86b940a08 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -59,7 +59,13 @@ class Projects::MergeRequestsController < Projects::ApplicationController respond_to do |format| format.html format.json { render json: @merge_request } - format.patch { render text: @merge_request.to_patch } + format.patch do + headers.store(*Gitlab::Workhorse.send_git_patch(@project.repository, + @merge_request.diff_base_commit.id, + @merge_request.last_commit.id)) + headers['Content-Disposition'] = 'inline' + head :ok + end format.diff do return render_404 unless @merge_request.diff_refs @@ -85,6 +91,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 +205,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 @@ -325,8 +342,21 @@ class Projects::MergeRequestsController < Projects::ApplicationController def define_show_vars # Build a note object for comment form @note = @project.notes.new(noteable: @merge_request) - @discussions = @merge_request.mr_and_commit_notes.inc_author_project_award_emoji.fresh.discussions - @notes = @discussions.flatten + + @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 @@ -373,4 +403,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/finders/pipelines_finder.rb b/app/finders/pipelines_finder.rb index c19a795d467..641fbf838f1 100644 --- a/app/finders/pipelines_finder.rb +++ b/app/finders/pipelines_finder.rb @@ -29,10 +29,10 @@ class PipelinesFinder end def branches - project.repository.branches.map(&:name) + project.repository.branch_names end def tags - project.repository.tags.map(&:name) + project.repository.tag_names end end diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 4b4bc3d4276..428a42266d0 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -1,10 +1,10 @@ module BlobHelper - def highlighter(blob_name, blob_content, nowrap: false) - Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap) + def highlighter(blob_name, blob_content, repository: nil, nowrap: false) + Gitlab::Highlight.new(blob_name, blob_content, nowrap: nowrap, repository: repository) end - def highlight(blob_name, blob_content, nowrap: false, plain: false) - Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain) + def highlight(blob_name, blob_content, repository: nil, nowrap: false, plain: false) + Gitlab::Highlight.highlight(blob_name, blob_content, nowrap: nowrap, plain: plain, repository: repository) end def no_highlight_files diff --git a/app/helpers/dropdowns_helper.rb b/app/helpers/dropdowns_helper.rb index 6b617e1730a..7c140538012 100644 --- a/app/helpers/dropdowns_helper.rb +++ b/app/helpers/dropdowns_helper.rb @@ -69,7 +69,7 @@ module DropdownsHelper def dropdown_filter(placeholder, search_id: nil) content_tag :div, class: "dropdown-input" do - filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder + filter_output = search_field_tag search_id, nil, class: "dropdown-input-field", placeholder: placeholder, autocomplete: 'off' filter_output << icon('search', class: "dropdown-input-search") filter_output << icon('times', class: "dropdown-input-clear js-dropdown-input-clear", role: "button") 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/helpers/notifications_helper.rb b/app/helpers/notifications_helper.rb index 77783cd7640..7e8369d0a05 100644 --- a/app/helpers/notifications_helper.rb +++ b/app/helpers/notifications_helper.rb @@ -72,6 +72,6 @@ module NotificationsHelper # Create hidden field to send notification setting source to controller def hidden_setting_source_input(notification_setting) return unless notification_setting.source_type - hidden_field_tag "#{notification_setting.source_type.downcase}[id]", notification_setting.source_id + hidden_field_tag "#{notification_setting.source_type.downcase}_id", notification_setting.source_id end end diff --git a/app/helpers/page_layout_helper.rb b/app/helpers/page_layout_helper.rb index e4e8b934bc8..22387d66451 100644 --- a/app/helpers/page_layout_helper.rb +++ b/app/helpers/page_layout_helper.rb @@ -52,7 +52,7 @@ module PageLayoutHelper raise ArgumentError, 'cannot provide more than two attributes' if map.length > 2 @page_card_attributes ||= {} - @page_card_attributes = map.reject { |_,v| v.blank? } if map.present? + @page_card_attributes = map.reject { |_, v| v.blank? } if map.present? @page_card_attributes end diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index d91e3332e48..26796b08fac 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -15,7 +15,7 @@ module ProjectsHelper def link_to_member_avatar(author, opts = {}) default_opts = { avatar: true, name: true, size: 16, author_class: 'author', title: ":name" } opts = default_opts.merge(opts) - image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] + image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt: '') if opts[:avatar] end def link_to_member(project, author, opts = {}, &block) @@ -27,7 +27,7 @@ module ProjectsHelper author_html = "" # Build avatar image tag - author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt:'') if opts[:avatar] + author_html << image_tag(avatar_icon(author, opts[:size]), width: opts[:size], class: "avatar avatar-inline #{"s#{opts[:size]}" if opts[:size]}", alt: '') if opts[:avatar] # Build name span tag if opts[:by_username] diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 0c9a5e42eec..10324bf2257 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -163,7 +163,7 @@ 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 diff --git a/app/models/ci/variable.rb b/app/models/ci/variable.rb index f8d5d4486fd..c9c47ec7419 100644 --- a/app/models/ci/variable.rb +++ b/app/models/ci/variable.rb @@ -13,6 +13,7 @@ module Ci attr_encrypted :value, mode: :per_attribute_iv_and_salt, + insecure_mode: true, key: Gitlab::Application.secrets.db_key_base, algorithm: 'aes-256-cbc' end diff --git a/app/models/event.rb b/app/models/event.rb index 716039fb54b..d7d23c7ae6d 100644 --- a/app/models/event.rb +++ b/app/models/event.rb @@ -315,7 +315,7 @@ class Event < ActiveRecord::Base def body? if push? - push_with_commits? + push_with_commits? || rm_ref? elsif note? true else diff --git a/app/models/group.rb b/app/models/group.rb index e66e04371b2..c70c719e338 100644 --- a/app/models/group.rb +++ b/app/models/group.rb @@ -11,7 +11,7 @@ class Group < Namespace has_many :users, -> { where(members: { requested_at: nil }) }, through: :group_members has_many :owners, - -> { where(members: { access_level: Gitlab::Access::OWNER }) }, + -> { where(members: { requested_at: nil, access_level: Gitlab::Access::OWNER }) }, through: :group_members, source: :user diff --git a/app/models/member.rb b/app/models/member.rb index c74a16367db..57161397e2b 100644 --- a/app/models/member.rb +++ b/app/models/member.rb @@ -32,6 +32,7 @@ class Member < ActiveRecord::Base scope :request, -> { where.not(requested_at: nil) } scope :non_request, -> { where(requested_at: nil) } scope :non_pending, -> { non_request.non_invite } + scope :has_access, -> { where('access_level > 0') } scope :guests, -> { where(access_level: GUEST) } scope :reporters, -> { where(access_level: REPORTER) } diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 36bc98bdb1e..53d9aa588af 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 @@ -319,13 +319,6 @@ class MergeRequest < ActiveRecord::Base ) end - # Returns the commit as a series of email patches. - # - # see "git format-patch" - def to_patch - target_project.repository.format_patch(diff_base_commit.sha, source_sha) - end - def hook_attrs attrs = { source: source_project.try(:hook_attrs), diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index aca377cc600..86331a33c05 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -108,44 +108,46 @@ class MergeRequestDiff < ActiveRecord::Base # Reload all commits related to current merge request from repo # and save it as array of hashes in st_commits db field def reload_commits + new_attributes = {} + commit_objects = unmerged_commits if commit_objects.present? - self.st_commits = dump_commits(commit_objects) + new_attributes[:st_commits] = dump_commits(commit_objects) end - save + update_columns_serialized(new_attributes) end # Reload diffs between branches related to current merge request from repo # and save it as array of hashes in st_diffs db field def reload_diffs + new_attributes = {} new_diffs = [] if commits.size.zero? - self.state = :empty + new_attributes[:state] = :empty else diff_collection = unmerged_diffs if diff_collection.overflow? # Set our state to 'overflow' to make the #empty? and #collected? # methods (generated by StateMachine) return false. - self.state = :overflow + new_attributes[:state] = :overflow end - self.real_size = diff_collection.real_size + new_attributes[:real_size] = diff_collection.real_size if diff_collection.any? new_diffs = dump_diffs(diff_collection) - self.state = :collected + new_attributes[:state] = :collected end end - self.st_diffs = new_diffs - - self.base_commit_sha = self.repository.merge_base(self.head, self.base) + new_attributes[:st_diffs] = new_diffs + new_attributes[:base_commit_sha] = self.repository.merge_base(self.head, self.base) - self.save + update_columns_serialized(new_attributes) end # Collect array of Git::Diff objects @@ -190,4 +192,29 @@ class MergeRequestDiff < ActiveRecord::Base ) end end + + private + + # + # #save or #update_attributes providing changes on serialized attributes do a lot of + # serialization and deserialization calls resulting in bad performance. + # Using #update_columns solves the problem with just one YAML.dump per serialized attribute that we provide. + # As a tradeoff we need to reload the current instance to properly manage time objects on those serialized + # attributes. So to keep the same behaviour as the attribute assignment we reload the instance. + # The difference is in the usage of + # #write_attribute= (#update_attributes) and #raw_write_attribute= (#update_columns) + # + # Ex: + # + # new_attributes[:st_commits].first.slice(:committed_date) + # => {:committed_date=>2014-02-27 11:01:38 +0200} + # YAML.load(YAML.dump(new_attributes[:st_commits].first.slice(:committed_date))) + # => {:committed_date=>2014-02-27 10:01:38 +0100} + # + def update_columns_serialized(new_attributes) + return unless new_attributes.any? + + update_columns(new_attributes.merge(updated_at: current_time_from_proper_timezone)) + reload + end end diff --git a/app/models/network/graph.rb b/app/models/network/graph.rb index a2aee2f925b..345041a6ad1 100644 --- a/app/models/network/graph.rb +++ b/app/models/network/graph.rb @@ -54,7 +54,7 @@ module Network @map = {} @reserved = {} - @commits.each_with_index do |c,i| + @commits.each_with_index do |c, i| c.time = i days[i] = c.committed_date @map[c.id] = c @@ -116,7 +116,7 @@ module Network end def commits_sort_by_ref - @commits.sort do |a,b| + @commits.sort do |a, b| if include_ref?(a) -1 elsif include_ref?(b) diff --git a/app/models/note.rb b/app/models/note.rb index e510525b89d..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 diff --git a/app/models/project.rb b/app/models/project.rb index ca3bc04e2dd..73ded09c162 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -81,6 +81,7 @@ class Project < ActiveRecord::Base has_one :jira_service, dependent: :destroy has_one :redmine_service, dependent: :destroy has_one :custom_issue_tracker_service, dependent: :destroy + has_one :bugzilla_service, dependent: :destroy has_one :gitlab_issue_tracker_service, dependent: :destroy, inverse_of: :project has_one :external_wiki_service, dependent: :destroy @@ -163,6 +164,7 @@ class Project < ActiveRecord::Base validates :avatar, file_size: { maximum: 200.kilobytes.to_i } validate :visibility_level_allowed_by_group validate :visibility_level_allowed_as_fork + validate :check_wiki_path_conflict add_authentication_token_field :runners_token before_save :ensure_runners_token @@ -539,6 +541,16 @@ class Project < ActiveRecord::Base self.errors.add(:visibility_level, "#{level_name} is not allowed since the fork source project has lower visibility.") end + def check_wiki_path_conflict + return if path.blank? + + path_to_check = path.ends_with?('.wiki') ? path.chomp('.wiki') : "#{path}.wiki" + + if Project.where(namespace_id: namespace_id, path: path_to_check).exists? + errors.add(:name, 'has already been taken') + end + end + def to_param path end diff --git a/app/models/project_import_data.rb b/app/models/project_import_data.rb index ca8a9b4217b..331123a5a5b 100644 --- a/app/models/project_import_data.rb +++ b/app/models/project_import_data.rb @@ -7,6 +7,7 @@ class ProjectImportData < ActiveRecord::Base marshal: true, encode: true, mode: :per_attribute_iv_and_salt, + insecure_mode: true, algorithm: 'aes-256-cbc' serialize :data, JSON diff --git a/app/models/project_services/bugzilla_service.rb b/app/models/project_services/bugzilla_service.rb new file mode 100644 index 00000000000..260f6030957 --- /dev/null +++ b/app/models/project_services/bugzilla_service.rb @@ -0,0 +1,25 @@ +class BugzillaService < IssueTrackerService + + prop_accessor :title, :description, :project_url, :issues_url, :new_issue_url + + def title + if self.properties && self.properties['title'].present? + self.properties['title'] + else + 'Bugzilla' + end + end + + def description + if self.properties && self.properties['description'].present? + self.properties['description'] + else + 'Bugzilla issue tracker' + end + end + + def to_param + 'bugzilla' + end + +end diff --git a/app/models/project_services/custom_issue_tracker_service.rb b/app/models/project_services/custom_issue_tracker_service.rb index 6b2b1daa724..8f2db46a7ba 100644 --- a/app/models/project_services/custom_issue_tracker_service.rb +++ b/app/models/project_services/custom_issue_tracker_service.rb @@ -32,7 +32,4 @@ class CustomIssueTrackerService < IssueTrackerService ] end - def initialize_properties - self.properties = {} if properties.nil? - end end diff --git a/app/models/project_services/hipchat_service.rb b/app/models/project_services/hipchat_service.rb index 0ff4f4c8dd2..23e5b16221b 100644 --- a/app/models/project_services/hipchat_service.rb +++ b/app/models/project_services/hipchat_service.rb @@ -106,7 +106,7 @@ class HipchatService < Service else message << "pushed to #{ref_type} <a href=\""\ "#{project.web_url}/commits/#{CGI.escape(ref)}\">#{ref}</a> " - message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/,'')}</a> " + message << "of <a href=\"#{project.web_url}\">#{project.name_with_namespace.gsub!(/\s/, '')}</a> " message << "(<a href=\"#{project.web_url}/compare/#{before}...#{after}\">Compare changes</a>)" push[:commits].take(MAX_COMMITS).each do |commit| diff --git a/app/models/project_services/jira_service.rb b/app/models/project_services/jira_service.rb index beda89d3963..8b3296c712f 100644 --- a/app/models/project_services/jira_service.rb +++ b/app/models/project_services/jira_service.rb @@ -124,7 +124,7 @@ class JiraService < IssueTrackerService def build_api_url_from_project_url server = URI(project_url) - default_ports = [["http",80],["https",443]].include?([server.scheme,server.port]) + default_ports = [["http", 80], ["https", 443]].include?([server.scheme, server.port]) server_url = "#{server.scheme}://#{server.host}" server_url.concat(":#{server.port}") unless default_ports "#{server_url}/rest/api/#{DEFAULT_API_VERSION}" diff --git a/app/models/project_team.rb b/app/models/project_team.rb index 73e736820af..0865b979ce0 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -137,20 +137,10 @@ class ProjectTeam def max_member_access(user_id) access = [] - project.members.non_request.each do |member| - if member.user_id == user_id - access << member.access_field if member.access_field - break - end - end + access += project.members.non_request.where(user_id: user_id).has_access.pluck(:access_level) if group - group.members.non_request.each do |member| - if member.user_id == user_id - access << member.access_field if member.access_field - break - end - end + access += group.members.non_request.where(user_id: user_id).has_access.pluck(:access_level) end if project.invited_groups.any? && project.allowed_to_share_with_group? diff --git a/app/models/repository.rb b/app/models/repository.rb index acc720ccfa3..2a6a3b086c2 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -978,6 +978,10 @@ class Repository raw_repository.ls_files(actual_ref) end + def gitattribute(path, name) + raw_repository.attributes(path)[name] + end + def copy_gitattributes(ref) actual_ref = ref || root_ref begin diff --git a/app/models/service.rb b/app/models/service.rb index 40d39933ad8..d7a32c28267 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -170,6 +170,7 @@ class Service < ActiveRecord::Base bamboo buildkite builds_email + bugzilla campfire custom_issue_tracker drone_ci diff --git a/app/models/snippet.rb b/app/models/snippet.rb index f8034cb5e6b..5ec933601ac 100644 --- a/app/models/snippet.rb +++ b/app/models/snippet.rb @@ -20,6 +20,7 @@ class Snippet < ActiveRecord::Base length: { within: 0..255 }, format: { with: Gitlab::Regex.file_name_regex, message: Gitlab::Regex.file_name_regex_message } + validates :content, presence: true validates :visibility_level, inclusion: { in: Gitlab::VisibilityLevel.values } @@ -81,6 +82,11 @@ class Snippet < ActiveRecord::Base 0 end + # alias for compatibility with blobs and highlighting + def path + file_name + end + def name file_name end @@ -135,7 +141,16 @@ class Snippet < ActiveRecord::Base end def accessible_to(user) - where('visibility_level IN (?) OR author_id = ?', [Snippet::INTERNAL, Snippet::PUBLIC], user) + return are_public unless user.present? + return all if user.admin? + + where( + 'visibility_level IN (:visibility_levels) + OR author_id = :author_id + OR project_id IN (:project_ids)', + visibility_levels: [Snippet::PUBLIC, Snippet::INTERNAL], + author_id: user.id, + project_ids: user.authorized_projects.select(:id)) end end end diff --git a/app/models/user.rb b/app/models/user.rb index 04b220ee13c..eac716b120b 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -25,6 +25,7 @@ class User < ActiveRecord::Base attr_encrypted :otp_secret, key: Gitlab::Application.config.secret_key_base, mode: :per_attribute_iv_and_salt, + insecure_mode: true, algorithm: 'aes-256-cbc' devise :two_factor_authenticatable, @@ -57,7 +58,7 @@ class User < ActiveRecord::Base # Groups has_many :members, dependent: :destroy - has_many :group_members, dependent: :destroy, source: 'GroupMember' + has_many :group_members, -> { where(requested_at: nil) }, dependent: :destroy, source: 'GroupMember' has_many :groups, through: :group_members has_many :owned_groups, -> { where members: { access_level: Gitlab::Access::OWNER } }, through: :group_members, source: :group has_many :masters_groups, -> { where members: { access_level: Gitlab::Access::MASTER } }, through: :group_members, source: :group @@ -65,7 +66,7 @@ class User < ActiveRecord::Base # Projects has_many :groups_projects, through: :groups, source: :projects has_many :personal_projects, through: :namespace, source: :projects - has_many :project_members, dependent: :destroy, class_name: 'ProjectMember' + has_many :project_members, -> { where(requested_at: nil) }, dependent: :destroy, class_name: 'ProjectMember' has_many :projects, through: :project_members has_many :created_projects, foreign_key: :creator_id, class_name: 'Project' has_many :users_star_projects, dependent: :destroy @@ -763,7 +764,7 @@ class User < ActiveRecord::Base unless email_domains.blank? match_found = email_domains.any? do |domain| - escaped = Regexp.escape(domain).gsub('\*','.*?') + escaped = Regexp.escape(domain).gsub('\*', '.*?') regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE email_domain = Mail::Address.new(self.email).domain email_domain =~ regexp 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/uploaders/lfs_object_uploader.rb b/app/uploaders/lfs_object_uploader.rb index 28085b31083..046a1d641a9 100644 --- a/app/uploaders/lfs_object_uploader.rb +++ b/app/uploaders/lfs_object_uploader.rb @@ -4,7 +4,7 @@ class LfsObjectUploader < CarrierWave::Uploader::Base storage :file def store_dir - "#{Gitlab.config.lfs.storage_path}/#{model.oid[0,2]}/#{model.oid[2,2]}" + "#{Gitlab.config.lfs.storage_path}/#{model.oid[0, 2]}/#{model.oid[2, 2]}" end def cache_dir diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index c883e8f97da..30ab0717164 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -15,7 +15,7 @@ = f.label :default_snippet_visibility, class: 'control-label col-sm-2' .col-sm-10 = render('shared/visibility_radios', model_method: :default_snippet_visibility, form: f, selected_level: @application_setting.default_snippet_visibility, form_model: ProjectSnippet.new) - .form-group.group-visibility-level-holder + .form-group.project-visibility-level-holder = f.label :default_group_visibility, class: 'control-label col-sm-2' .col-sm-10 = render('shared/visibility_radios', model_method: :default_group_visibility, form: f, selected_level: @application_setting.default_group_visibility, form_model: Group.new) diff --git a/app/views/admin/background_jobs/_head.html.haml b/app/views/admin/background_jobs/_head.html.haml index d78682532ed..9d722bd7382 100644 --- a/app/views/admin/background_jobs/_head.html.haml +++ b/app/views/admin/background_jobs/_head.html.haml @@ -1,5 +1,9 @@ .nav-links.sub-nav %ul{ class: (container_class) } + = nav_link(controller: :system_info) do + = link_to admin_system_info_path, title: 'System Info' do + %span + System Info = nav_link(controller: :background_jobs) do = link_to admin_background_jobs_path, title: 'Background Jobs' do %span diff --git a/app/views/admin/system_info/show.html.haml b/app/views/admin/system_info/show.html.haml new file mode 100644 index 00000000000..3ef2f20b589 --- /dev/null +++ b/app/views/admin/system_info/show.html.haml @@ -0,0 +1,22 @@ +- @no_container = true +- page_title "System Info" += render 'admin/background_jobs/head' + +%div{ class: (container_class) } + .prepend-top-default + .row + .col-sm-4 + .light-well + %h4 CPU + .data + %h1= "#{@cpus} cores" + .col-sm-4 + .light-well + %h4 Memory + .data + %h1= "#{number_to_human_size(@mem_used)} / #{number_to_human_size(@mem_total)}" + .col-sm-4 + .light-well + %h4 Disk + .data + %h1= "#{number_to_human_size(@disk_used)} / #{number_to_human_size(@disk_total)}" diff --git a/app/views/admin/users/groups.html.haml b/app/views/admin/users/groups.html.haml index b0a709a568a..8f6d13b881a 100644 --- a/app/views/admin/users/groups.html.haml +++ b/app/views/admin/users/groups.html.haml @@ -1,11 +1,12 @@ - page_title "Groups", @user.name, "Users" = render 'admin/users/head' -- if @user.group_members.present? +- group_members = @user.group_members.includes(:source) +- if group_members.any? .panel.panel-default .panel-heading Groups: %ul.well-list - - @user.group_members.each do |group_member| + - group_members.each do |group_member| - group = group_member.group %li.group_member %span{class: ("list-item-name" unless group_member.owner?)} 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/emojis/index.html.haml b/app/views/emojis/index.html.haml index 97401a2e618..8b38b4c2bd4 100644 --- a/app/views/emojis/index.html.haml +++ b/app/views/emojis/index.html.haml @@ -1,6 +1,6 @@ .emoji-menu + = text_field_tag :emoji_search, "", class: "emoji-search search-input form-control", placeholder: "Seach emojis" .emoji-menu-content - = text_field_tag :emoji_search, "", class: "emoji-search search-input form-control" - Gitlab::AwardEmoji.emoji_by_category.each do |category, emojis| %h5.emoji-menu-title = Gitlab::AwardEmoji::CATEGORIES[category] diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index dc4ff17e31a..ea54ef226ec 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -1,3 +1,5 @@ +- project = event.project + .event-title %span.author_name= link_to_author event %span.event_label.pushed #{event.action_name} #{event.ref_type} @@ -5,19 +7,18 @@ %strong= event.ref_name - else %strong - = link_to event.ref_name, namespace_project_commits_path(event.project.namespace, event.project, event.ref_name), title: h(event.target_title) + = link_to event.ref_name, namespace_project_commits_path(project.namespace, project, event.ref_name), title: h(event.target_title) at - = link_to_project event.project + = link_to_project project - if event.push_with_commits? - - project = event.project .event-body %ul.well-list.event_commits - few_commits = event.commits[0...2] - few_commits.each do |commit| = render "events/commit", commit: commit, project: project, event: event - - create_mr = event.new_ref? && create_mr_button?(event.project.default_branch, event.ref_name, event.project) + - create_mr = event.new_ref? && create_mr_button?(project.default_branch, event.ref_name, project) - if event.commits_count > 1 %li.commits-stat - if event.commits_count > 2 @@ -27,18 +28,26 @@ - from = event.commit_from - from_label = truncate_sha(from) - else - - from = event.project.default_branch + - from = project.default_branch - from_label = from - = link_to namespace_project_compare_path(event.project.namespace, event.project, from: from, to: event.commit_to) do + = link_to namespace_project_compare_path(project.namespace, project, from: from, to: event.commit_to) do Compare #{from_label}...#{truncate_sha(event.commit_to)} - if create_mr %span{"data-user-is" => event.author_id, "data-display" => "inline"} or - = link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do + = link_to create_mr_path(project.default_branch, event.ref_name, project) do create a merge request - elsif create_mr %li.commits-stat{"data-user-is" => event.author_id} - = link_to create_mr_path(event.project.default_branch, event.ref_name, event.project) do + = link_to create_mr_path(project.default_branch, event.ref_name, project) do Create Merge Request +- elsif event.rm_ref? + - repository = project.repository + - last_commit = repository.commit(event.commit_from) + - if last_commit + .event-body + %ul.well-list.event_commits + = render "events/commit", commit: last_commit, project: project, event: event + diff --git a/app/views/groups/show.html.haml b/app/views/groups/show.html.haml index aecefbc6e8f..a0a6762edcf 100644 --- a/app/views/groups/show.html.haml +++ b/app/views/groups/show.html.haml @@ -15,12 +15,17 @@ %span.visibility-icon.has-tooltip{ data: { container: 'body' }, title: visibility_icon_description(@group) } = visibility_level_icon(@group.visibility_level, fw: false) + %span.hidden-xs + = render 'shared/notifications/button', notification_setting: @notification_setting + + - if current_user + .pull-right + = render 'shared/members/access_request_buttons', source: @group + - if @group.description.present? .cover-desc.description = markdown(@group.description, pipeline: :description) - - if current_user - = render 'shared/members/access_request_buttons', source: @group %div{ class: container_class } .top-area diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index d5965a6ec99..757de92d6d4 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -25,16 +25,13 @@ = favicon_link_tag 'favicon.ico' - = stylesheet_link_tag "application", media: "all", integrity: true - = stylesheet_link_tag "print", media: "print", integrity: true + = stylesheet_link_tag "application", media: "all" + = stylesheet_link_tag "print", media: "print" - = javascript_include_tag "application", integrity: true + = javascript_include_tag "application" - -# FIXME: SRI doesn't apply to the dynamically-generated per-page - -# JavaScript due to a bug in sprockets-rails. - -# See https://github.com/rails/sprockets-rails/issues/359 - - 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/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/header/_default.html.haml b/app/views/layouts/header/_default.html.haml index 40a2c81eebd..1a39572ac3c 100644 --- a/app/views/layouts/header/_default.html.haml +++ b/app/views/layouts/header/_default.html.haml @@ -6,7 +6,7 @@ = icon('bars') %button.navbar-toggle{type: 'button'} %span.sr-only Toggle navigation - = icon('angle-left') + = icon('ellipsis-v') .navbar-collapse.collapse %ul.nav.navbar-nav diff --git a/app/views/layouts/nav/_admin.html.haml b/app/views/layouts/nav/_admin.html.haml index 0f264cd2e06..5ee8772882e 100644 --- a/app/views/layouts/nav/_admin.html.haml +++ b/app/views/layouts/nav/_admin.html.haml @@ -1,16 +1,16 @@ .scrolling-tabs-container{ class: nav_control_class } = render 'layouts/nav/admin_settings' .fade-left - = icon('arrow-left') + = icon('angle-left') .fade-right - = icon('arrow-right') + = icon('angle-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 + = nav_link(controller: %w(system_info background_jobs logs health_check)) do + = link_to admin_system_info_path, title: 'Monitoring' do %span Monitoring = nav_link(controller: :broadcast_messages) do diff --git a/app/views/layouts/nav/_group.html.haml b/app/views/layouts/nav/_group.html.haml index 5d657a9ac84..d7d36c84b6c 100644 --- a/app/views/layouts/nav/_group.html.haml +++ b/app/views/layouts/nav/_group.html.haml @@ -1,9 +1,9 @@ .scrolling-tabs-container{ class: nav_control_class } = render 'layouts/nav/group_settings' .fade-left - = icon('arrow-left') + = icon('angle-left') .fade-right - = icon('arrow-right') + = icon('angle-right') %ul.nav-links.scrolling-tabs = nav_link(path: 'groups#show', html_options: {class: 'home'}) do = link_to group_path(@group), title: 'Home' do diff --git a/app/views/layouts/nav/_profile.html.haml b/app/views/layouts/nav/_profile.html.haml index f37f9b0f5a3..96fe62c39c3 100644 --- a/app/views/layouts/nav/_profile.html.haml +++ b/app/views/layouts/nav/_profile.html.haml @@ -1,8 +1,8 @@ .scrolling-tabs-container .fade-left - = icon('arrow-left') + = icon('angle-left') .fade-right - = icon('arrow-right') + = icon('angle-right') %ul.nav-links.scrolling-tabs = nav_link(path: 'profiles#show', html_options: {class: 'home'}) do = link_to profile_path, title: 'Profile Settings' do diff --git a/app/views/layouts/nav/_project.html.haml b/app/views/layouts/nav/_project.html.haml index a4bb56aa56f..dcef427cda3 100644 --- a/app/views/layouts/nav/_project.html.haml +++ b/app/views/layouts/nav/_project.html.haml @@ -26,9 +26,9 @@ .scrolling-tabs-container{ class: nav_control_class } .fade-left - = icon('arrow-left') + = icon('angle-left') .fade-right - = icon('arrow-right') + = icon('angle-right') %ul.nav-links.scrolling-tabs = nav_link(path: 'projects#show', html_options: {class: 'home'}) do = link_to project_path(@project), title: 'Project', class: 'shortcuts-project' do diff --git a/app/views/projects/_md_preview.html.haml b/app/views/projects/_md_preview.html.haml index ca6714ef42b..58d961d93ca 100644 --- a/app/views/projects/_md_preview.html.haml +++ b/app/views/projects/_md_preview.html.haml @@ -12,13 +12,13 @@ %li.confidential-issue-warning = icon('warning') %span This is a confidential issue. Your comment will not be visible to the public. - + %li.pull-right .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: "code fw", data: { "md-tag" => "`", "md-block" => "```" }, 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" }) diff --git a/app/views/projects/blob/_text.html.haml b/app/views/projects/blob/_text.html.haml index b1769759dce..58524418a67 100644 --- a/app/views/projects/blob/_text.html.haml +++ b/app/views/projects/blob/_text.html.haml @@ -16,4 +16,4 @@ .file-content.code .nothing-here-block Empty file - else - = render 'shared/file_highlight', blob: blob + = render 'shared/file_highlight', blob: blob, repository: @repository diff --git a/app/views/projects/commits/_head.html.haml b/app/views/projects/commits/_head.html.haml index b11b6c24ccd..61152649907 100644 --- a/app/views/projects/commits/_head.html.haml +++ b/app/views/projects/commits/_head.html.haml @@ -1,8 +1,8 @@ .scrolling-tabs-container.sub-nav-scroll .fade-left - = icon('arrow-left') + = icon('angle-left') .fade-right - = icon('arrow-right') + = icon('angle-right') .nav-links.sub-nav.scrolling-tabs %ul{ class: (container_class) } = nav_link(controller: %w(tree blob blame edit_tree new_tree find_file)) do diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml index a388d9a0a61..ca347406dfe 100644 --- a/app/views/projects/graphs/_head.html.haml +++ b/app/views/projects/graphs/_head.html.haml @@ -1,7 +1,9 @@ .nav-links.sub-nav %ul{ class: (container_class) } - - page_specific_javascripts asset_path("graphs/application.js") + - 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 diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 593af319a47..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) } diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 3c1c6060504..8a73b077357 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -1,110 +1,121 @@ - page_title 'New Project' - header_title "Projects", dashboard_projects_path -%h3.page-title - New Project -%hr - .project-edit-container .project-edit-errors = render 'projects/errors' - .project-edit-content - - = form_for @project, html: { class: 'new_project form-horizontal js-requires-input' } do |f| - .form-group - = f.label :path, class: 'control-label' do - Project owner - .col-sm-10 - = f.select :namespace_id, namespaces_options(:current_user), {}, {class: 'select2 js-select-namespace', tabindex: 1} - - - if current_user.can_create_group? - .help-block - Want to house several dependent projects under the same namespace? - = link_to "Create a group", new_group_path - - .form-group - = f.label :path, class: 'control-label' do - Project name - .col-sm-10 - = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true - - - if import_sources_enabled? - .project-import.js-toggle-container - .form-group - %label.control-label Import project from - .col-sm-10 - - if github_import_enabled? - - if github_import_configured? - = link_to status_import_github_path, class: 'btn import_github' do - %i.fa.fa-github - GitHub - - else - = link_to '#', class: 'how_to_import_link btn import_github' do - %i.fa.fa-github - GitHub - = render 'github_import_modal' - - - if bitbucket_import_enabled? - - if bitbucket_import_configured? - = link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do - %i.fa.fa-bitbucket - Bitbucket - - else - = link_to status_import_bitbucket_path, class: 'how_to_import_link btn import_bitbucket', "data-no-turbolink" => "true" do - %i.fa.fa-bitbucket - Bitbucket - = render 'bitbucket_import_modal' - - - if gitlab_import_enabled? - - if gitlab_import_configured? - = link_to status_import_gitlab_path, class: 'btn import_gitlab' do - %i.fa.fa-heart - GitLab.com + .row.prepend-top-default + .col-lg-3.profile-settings-sidebar + %h4.prepend-top-0 + New project + %p + Create or Import your project from popular Git services + .col-lg-9 + = form_for @project, html: { class: 'new_project' } do |f| + %fieldset.append-bottom-0 + .form-group.col-xs-12.col-sm-6 + = f.label :namespace_id, class: 'label-light' do + %span + Project path + .form-group + .input-group + - if current_user.can_select_namespace? + .input-group-addon + = root_url + = f.select :namespace_id, namespaces_options(params[:namespace_id] || :current_user, display_path: true), {}, {class: 'select2 js-select-namespace', tabindex: 1} - else - = link_to status_import_gitlab_path, class: 'how_to_import_link btn import_gitlab' do - %i.fa.fa-heart - GitLab.com - = render 'gitlab_import_modal' - - - if gitorious_import_enabled? - = link_to new_import_gitorious_path, class: 'btn import_gitorious' do - %i.icon-gitorious.icon-gitorious-small - Gitorious.org - - - if google_code_import_enabled? - = link_to new_import_google_code_path, class: 'btn import_google_code' do - %i.fa.fa-google - Google Code - - - if fogbugz_import_enabled? - = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do - %i.fa.fa-bug - Fogbugz - - - if git_import_enabled? - = link_to "#", class: 'btn js-toggle-button import_git' do - %i.fa.fa-git - %span Repo by URL - - - if gitlab_project_import_enabled? - = link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do - %i.fa.fa-gitlab - %span GitLab export - - .js-toggle-content.hide - = render "shared/import_form", f: f - - .prepend-botton-10 - - .form-group - = f.label :description, class: 'control-label' do - Description - %span.light (optional) - .col-sm-10 - = f.text_area :description, class: "form-control", rows: 3, maxlength: 250, tabindex: 3 - = render 'shared/visibility_level', f: f, visibility_level: default_project_visibility, can_change_visibility_level: true, form_model: @project + .input-group-addon.static-namespace + #{root_url}#{current_user.username}/ + .form-group.col-xs-12.col-sm-6.project-path + = f.label :namespace_id, class: 'label-light' do + %span + Project name + = f.text_field :path, placeholder: "my-awesome-project", class: "form-control", tabindex: 2, autofocus: true, required: true + - if current_user.can_create_group? + .help-block + Want to house several dependent projects under the same namespace? + = link_to "Create a group", new_group_path + + - if import_sources_enabled? + .project-import.js-toggle-container + .form-group.clearfix + = f.label :visibility_level, class: 'label-light' do + Import project from + .col-sm-12.import-buttons + %div + - if github_import_enabled? + - if github_import_configured? + = link_to status_import_github_path, class: 'btn import_github' do + %i.fa.fa-github + GitHub + - else + = link_to '#', class: 'how_to_import_link btn import_github' do + %i.fa.fa-github + GitHub + = render 'github_import_modal' + %div + - if bitbucket_import_enabled? + - if bitbucket_import_configured? + = link_to status_import_bitbucket_path, class: 'btn import_bitbucket', "data-no-turbolink" => "true" do + %i.fa.fa-bitbucket + Bitbucket + - else + = link_to status_import_bitbucket_path, class: 'how_to_import_link btn import_bitbucket', "data-no-turbolink" => "true" do + %i.fa.fa-bitbucket + Bitbucket + = render 'bitbucket_import_modal' + %div + - if gitlab_import_enabled? + - if gitlab_import_configured? + = link_to status_import_gitlab_path, class: 'btn import_gitlab' do + %i.fa.fa-heart + GitLab.com + - else + = link_to status_import_gitlab_path, class: 'how_to_import_link btn import_gitlab' do + %i.fa.fa-heart + GitLab.com + = render 'gitlab_import_modal' + %div + - if gitorious_import_enabled? + = link_to new_import_gitorious_path, class: 'btn import_gitorious' do + %i.icon-gitorious.icon-gitorious-small + Gitorious.org + %div + - if google_code_import_enabled? + = link_to new_import_google_code_path, class: 'btn import_google_code' do + %i.fa.fa-google + Google Code + %div + - if fogbugz_import_enabled? + = link_to new_import_fogbugz_path, class: 'btn import_fogbugz' do + %i.fa.fa-bug + Fogbugz + %div + - if git_import_enabled? + = link_to "#", class: 'btn js-toggle-button import_git' do + %i.fa.fa-git + %span Repo by URL + %div + - if gitlab_project_import_enabled? + = link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do + %i.fa.fa-gitlab + %span GitLab export + + .js-toggle-content.hide + = render "shared/import_form", f: f + + .form-group + = f.label :description, class: 'label-light' do + Project description + %span.light (optional) + = f.text_area :description, placeholder: 'Description format', class: "form-control", rows: 3, maxlength: 250 + + .form-group.project-visibility-level-holder + = f.label :visibility_level, class: 'label-light' do + Visibility Level + = link_to "(?)", help_page_path("public_access", "public_access") + = render('shared/visibility_radios', model_method: :visibility_level, form: f, selected_level: @project.visibility_level, form_model: @project) - .form-actions = f.submit 'Create project', class: "btn btn-create project-submit", tabindex: 4 = link_to 'Cancel', dashboard_projects_path, class: 'btn btn-cancel' diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index c04d291412c..a5e163b91e9 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -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/wikis/edit.html.haml b/app/views/projects/wikis/edit.html.haml index bf5d09d50c2..817bf9b3f69 100644 --- a/app/views/projects/wikis/edit.html.haml +++ b/app/views/projects/wikis/edit.html.haml @@ -15,9 +15,10 @@ Edit Page .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 + - 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' diff --git a/app/views/shared/_file_highlight.html.haml b/app/views/shared/_file_highlight.html.haml index ad944a19ca1..e26693bf5b9 100644 --- a/app/views/shared/_file_highlight.html.haml +++ b/app/views/shared/_file_highlight.html.haml @@ -1,3 +1,5 @@ +- repository = nil unless local_assigns.key?(:repository) + .file-content.code.js-syntax-highlight .line-numbers - if blob.data.present? @@ -11,4 +13,4 @@ = link_icon = i .blob-content{data: {blob_id: blob.id}} - = highlight(blob.name, blob.data, plain: blob.no_highlighting?) + = highlight(blob.path, blob.data, repository: repository, plain: blob.no_highlighting?) 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/users/calendar_activities.html.haml b/app/views/users/calendar_activities.html.haml index 630d97e339d..f51599212db 100644 --- a/app/views/users/calendar_activities.html.haml +++ b/app/views/users/calendar_activities.html.haml @@ -14,7 +14,7 @@ - else = event_action_name(event) - if event.target - %strong= link_to "##{event.target_iid}", [event.project.namespace.becomes(Namespace), event.project, event.target] + %strong= link_to "#{event.target.to_reference}", [event.project.namespace.becomes(Namespace), event.project, event.target] at %strong 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 |