diff options
Diffstat (limited to 'app')
50 files changed, 345 insertions, 225 deletions
diff --git a/app/assets/javascripts/gfm_auto_complete.js b/app/assets/javascripts/gfm_auto_complete.js index 9ac4c49d697..b62b2cec4d8 100644 --- a/app/assets/javascripts/gfm_auto_complete.js +++ b/app/assets/javascripts/gfm_auto_complete.js @@ -50,7 +50,7 @@ window.gl.GfmAutoComplete = { template: '<li>${title}</li>' }, Loading: { - template: '<li style="pointer-events: none;"><i class="fa fa-refresh fa-spin"></i> Loading...</li>' + template: '<li style="pointer-events: none;"><i class="fa fa-spinner fa-spin"></i> Loading...</li>' }, DefaultOptions: { sorter: function(query, items, searchKey) { diff --git a/app/assets/javascripts/issue.js b/app/assets/javascripts/issue.js index 47e675f537e..011043e992f 100644 --- a/app/assets/javascripts/issue.js +++ b/app/assets/javascripts/issue.js @@ -20,57 +20,60 @@ class Issue { }); Issue.initIssueBtnEventListeners(); } + + Issue.$btnNewBranch = $('#new-branch'); + Issue.initMergeRequests(); Issue.initRelatedBranches(); Issue.initCanCreateBranch(); } static initIssueBtnEventListeners() { - var issueFailMessage; - issueFailMessage = 'Unable to update this issue at this time.'; - return $('a.btn-close, a.btn-reopen').on('click', function(e) { - var $this, isClose, shouldSubmit, url; + const issueFailMessage = 'Unable to update this issue at this time.'; + + const closeButtons = $('a.btn-close'); + const isClosedBadge = $('div.status-box-closed'); + const isOpenBadge = $('div.status-box-open'); + const projectIssuesCounter = $('.issue_counter'); + const reopenButtons = $('a.btn-reopen'); + + return closeButtons.add(reopenButtons).on('click', function(e) { + var $this, shouldSubmit, url; e.preventDefault(); e.stopImmediatePropagation(); $this = $(this); - isClose = $this.hasClass('btn-close'); shouldSubmit = $this.hasClass('btn-comment'); if (shouldSubmit) { Issue.submitNoteForm($this.closest('form')); } $this.prop('disabled', true); + Issue.setNewBranchButtonState(true, null); url = $this.attr('href'); return $.ajax({ type: 'PUT', - url: url, - error: function(jqXHR, textStatus, errorThrown) { - var issueStatus; - issueStatus = isClose ? 'close' : 'open'; - return new Flash(issueFailMessage, 'alert'); - }, - success: function(data, textStatus, jqXHR) { - if ('id' in data) { - $(document).trigger('issuable:change'); - let total = Number($('.issue_counter').text().replace(/[^\d]/, '')); - if (isClose) { - $('a.btn-close').addClass('hidden'); - $('a.btn-reopen').removeClass('hidden'); - $('div.status-box-closed').removeClass('hidden'); - $('div.status-box-open').addClass('hidden'); - total -= 1; - } else { - $('a.btn-reopen').addClass('hidden'); - $('a.btn-close').removeClass('hidden'); - $('div.status-box-closed').addClass('hidden'); - $('div.status-box-open').removeClass('hidden'); - total += 1; - } - $('.issue_counter').text(gl.text.addDelimiter(total)); - } else { - new Flash(issueFailMessage, 'alert'); - } - return $this.prop('disabled', false); + url: url + }).fail(function(jqXHR, textStatus, errorThrown) { + new Flash(issueFailMessage); + Issue.initCanCreateBranch(); + }).done(function(data, textStatus, jqXHR) { + if ('id' in data) { + $(document).trigger('issuable:change'); + + const isClosed = $this.hasClass('btn-close'); + closeButtons.toggleClass('hidden', isClosed); + reopenButtons.toggleClass('hidden', !isClosed); + isClosedBadge.toggleClass('hidden', !isClosed); + isOpenBadge.toggleClass('hidden', isClosed); + + let numProjectIssues = Number(projectIssuesCounter.text().replace(/[^\d]/, '')); + numProjectIssues = isClosed ? numProjectIssues - 1 : numProjectIssues + 1; + projectIssuesCounter.text(gl.text.addDelimiter(numProjectIssues)); + } else { + new Flash(issueFailMessage); } + + $this.prop('disabled', false); + Issue.initCanCreateBranch(); }); }); } @@ -86,9 +89,9 @@ class Issue { static initMergeRequests() { var $container; $container = $('#merge-requests'); - return $.getJSON($container.data('url')).error(function() { - return new Flash('Failed to load referenced merge requests', 'alert'); - }).success(function(data) { + return $.getJSON($container.data('url')).fail(function() { + return new Flash('Failed to load referenced merge requests'); + }).done(function(data) { if ('html' in data) { return $container.html(data.html); } @@ -98,9 +101,9 @@ class Issue { static initRelatedBranches() { var $container; $container = $('#related-branches'); - return $.getJSON($container.data('url')).error(function() { - return new Flash('Failed to load related branches', 'alert'); - }).success(function(data) { + return $.getJSON($container.data('url')).fail(function() { + return new Flash('Failed to load related branches'); + }).done(function(data) { if ('html' in data) { return $container.html(data.html); } @@ -108,24 +111,27 @@ class Issue { } static initCanCreateBranch() { - var $container; - $container = $('#new-branch'); // If the user doesn't have the required permissions the container isn't // rendered at all. - if ($container.length === 0) { + if (Issue.$btnNewBranch.length === 0) { return; } - return $.getJSON($container.data('path')).error(function() { - $container.find('.unavailable').show(); - return new Flash('Failed to check if a new branch can be created.', 'alert'); - }).success(function(data) { - if (data.can_create_branch) { - $container.find('.available').show(); - } else { - return $container.find('.unavailable').show(); - } + return $.getJSON(Issue.$btnNewBranch.data('path')).fail(function() { + Issue.setNewBranchButtonState(false, false); + new Flash('Failed to check if a new branch can be created.'); + }).done(function(data) { + Issue.setNewBranchButtonState(false, data.can_create_branch); }); } + + static setNewBranchButtonState(isPending, canCreate) { + if (Issue.$btnNewBranch.length === 0) { + return; + } + + Issue.$btnNewBranch.find('.available').toggle(!isPending && canCreate); + Issue.$btnNewBranch.find('.unavailable').toggle(!isPending && !canCreate); + } } export default Issue; diff --git a/app/assets/stylesheets/framework/calendar.scss b/app/assets/stylesheets/framework/calendar.scss index 9a0f7a14e57..759401a7806 100644 --- a/app/assets/stylesheets/framework/calendar.scss +++ b/app/assets/stylesheets/framework/calendar.scss @@ -5,7 +5,7 @@ direction: rtl; @media (min-width: $screen-sm-min) and (max-width: $screen-md-max) { - overflow-x: scroll; + overflow-x: auto; } } diff --git a/app/assets/stylesheets/framework/filters.scss b/app/assets/stylesheets/framework/filters.scss index 12465d4a70b..11d44df4867 100644 --- a/app/assets/stylesheets/framework/filters.scss +++ b/app/assets/stylesheets/framework/filters.scss @@ -82,7 +82,7 @@ .input-token:last-child { flex: 1; -webkit-flex: 1; - max-width: initial; + max-width: inherit; } } @@ -246,17 +246,17 @@ } } -.filtered-search-history-dropdown-toggle-button { +.filtered-search-history-dropdown-wrapper { + position: static; display: flex; - align-items: center; + flex-direction: column; +} + +.filtered-search-history-dropdown-toggle-button { + flex: 1; width: auto; - height: 100%; - padding-top: 0; - padding-left: 0.75em; - padding-bottom: 0; - padding-right: 0.5em; + padding-right: 10px; - background-color: transparent; border-radius: 0; border-top: 0; border-left: 0; @@ -264,6 +264,7 @@ border-right: 1px solid $border-color; color: $gl-text-color-secondary; + line-height: 1; transition: color 0.1s linear; @@ -275,24 +276,21 @@ } .dropdown-toggle-text { + display: inline-block; color: inherit; .fa { + vertical-align: middle; color: inherit; } } .fa { - position: initial; + position: static; } } -.filtered-search-history-dropdown-wrapper { - position: initial; - flex-shrink: 0; -} - .filtered-search-history-dropdown { width: 40%; diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 03fddaeb163..144adbcdaef 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -39,7 +39,7 @@ overflow-y: hidden; font-size: 12px; - .fa-refresh { + .fa-spinner { font-size: 24px; margin-left: 20px; } @@ -219,7 +219,7 @@ font-size: 12px; position: relative; - .fa-refresh { + .fa-spinner { font-size: 24px; } @@ -366,7 +366,7 @@ background-color: $row-hover; } - .fa-refresh { + .fa-spinner { font-size: 13px; margin-left: 3px; } diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index 79da490675a..5b723f7c722 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -10,10 +10,14 @@ position: relative; &.event-inline { - .profile-icon { + .system-note-image { top: 20px; } + .user-avatar { + top: 14px; + } + .event-title, .event-item-timestamp { line-height: 40px; @@ -24,7 +28,7 @@ color: $gl-text-color; } - .profile-icon { + .system-note-image { position: absolute; left: 0; top: 14px; @@ -35,15 +39,18 @@ fill: $gl-text-color-secondary; } - &.open-icon svg { - fill: $green-300; + &.opened-icon, + &.created-icon { + svg { + fill: $green-300; + } } &.closed-icon svg { fill: $red-300; } - &.fork-icon svg { + &.accepted-icon svg { fill: $blue-300; } } @@ -128,8 +135,7 @@ li { &.commit { background: transparent; - padding: 3px; - padding-left: 0; + padding: 0; border: none; .commit-row-title { @@ -183,7 +189,7 @@ max-width: 100%; } - .profile-icon { + .system-note-image { display: none; } diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index 931f08c17f4..6a419384a34 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -527,6 +527,8 @@ } .comments-disabled-notif { + line-height: 28px; + .btn { margin-left: 5px; } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index 94ea4c5c8c6..ad0f2f6efbb 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -18,12 +18,12 @@ ul.notes { float: left; svg { - width: 18px; - height: 18px; + width: 16px; + height: 16px; fill: $gray-darkest; position: absolute; - left: 30px; - top: 15px; + left: 0; + top: 16px; } } @@ -144,6 +144,10 @@ ul.notes { padding: 0; clear: both; + @media (min-width: $screen-sm-min) { + margin-left: 65px; + } + &.timeline-entry::after { clear: none; } @@ -172,6 +176,10 @@ ul.notes { .timeline-content { padding: 14px 10px; + + @media (min-width: $screen-sm-min) { + margin-left: 20px; + } } .note-header { diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index c6651254d70..008d2f5815f 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -61,7 +61,6 @@ class Projects::CompareController < Projects::ApplicationController @environment = EnvironmentsFinder.new(@project, current_user, environment_params).execute.last @diff_notes_disabled = true - @grouped_diff_discussions = {} end end diff --git a/app/controllers/projects/hooks_controller.rb b/app/controllers/projects/hooks_controller.rb index b668a9331e7..1e41f980f31 100644 --- a/app/controllers/projects/hooks_controller.rb +++ b/app/controllers/projects/hooks_controller.rb @@ -10,7 +10,7 @@ class Projects::HooksController < Projects::ApplicationController @hook = @project.hooks.new(hook_params) @hook.save - unless @hook.valid? + unless @hook.valid? @hooks = @project.hooks.select(&:persisted?) flash[:alert] = @hook.errors.full_messages.join.html_safe end @@ -49,7 +49,7 @@ class Projects::HooksController < Projects::ApplicationController def hook_params params.require(:hook).permit( - :build_events, + :job_events, :pipeline_events, :enable_ssl_verification, :issues_events, diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 5c1f7e69ee8..09dc8b38229 100755 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -16,7 +16,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController before_action :define_show_vars, only: [:show, :diffs, :commits, :conflicts, :conflict_for_path, :builds, :pipelines] before_action :define_widget_vars, only: [:merge, :cancel_merge_when_pipeline_succeeds, :merge_check] before_action :define_commit_vars, only: [:diffs] - before_action :define_diff_comment_vars, only: [:diffs] before_action :ensure_ref_fetched, only: [:show, :diffs, :commits, :builds, :conflicts, :conflict_for_path, :pipelines] before_action :close_merge_request_without_source_project, only: [:show, :diffs, :commits, :builds, :pipelines] before_action :apply_diff_view_cookie!, only: [:new_diffs] @@ -39,7 +38,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @collection_type = "MergeRequest" @merge_requests = merge_requests_collection @merge_requests = @merge_requests.page(params[:page]) - @merge_requests = @merge_requests.includes(merge_request_diff: :merge_request) + @merge_requests = @merge_requests.preload(merge_request_diff: :merge_request) @issuable_meta_data = issuable_meta_data(@merge_requests, @collection_type) if @merge_requests.out_of_range? && @merge_requests.total_pages != 0 @@ -101,34 +100,11 @@ class Projects::MergeRequestsController < Projects::ApplicationController respond_to do |format| format.html { define_discussion_vars } format.json do - @merge_request_diff = - if params[:diff_id] - @merge_request.merge_request_diffs.viewable.find(params[:diff_id]) - else - @merge_request.merge_request_diff - end - - @merge_request_diffs = @merge_request.merge_request_diffs.viewable.select_without_diff - @comparable_diffs = @merge_request_diffs.select { |diff| diff.id < @merge_request_diff.id } - - if params[:start_sha].present? - @start_sha = params[:start_sha] - @start_version = @comparable_diffs.find { |diff| diff.head_commit_sha == @start_sha } - - unless @start_version - @start_sha = @merge_request_diff.head_commit_sha - @start_version = @merge_request_diff - end - end + define_diff_vars + define_diff_comment_vars @environment = @merge_request.environments_for(current_user).last - if @start_sha - compared_diff_version - else - original_diff_version - end - render json: { html: view_to_html_string("projects/merge_requests/show/_diffs") } end end @@ -140,16 +116,17 @@ class Projects::MergeRequestsController < Projects::ApplicationController def diff_for_path if params[:id] merge_request + define_diff_vars define_diff_comment_vars else build_merge_request + @diffs = @merge_request.diffs(diff_options) @diff_notes_disabled = true - @grouped_diff_discussions = {} end define_commit_vars - render_diff_for_path(@merge_request.diffs(diff_options)) + render_diff_for_path(@diffs) end def commits @@ -586,15 +563,46 @@ class Projects::MergeRequestsController < Projects::ApplicationController @base_commit = @merge_request.diff_base_commit || @merge_request.likely_diff_base_commit end + def define_diff_vars + @merge_request_diff = + if params[:diff_id] + @merge_request.merge_request_diffs.viewable.find(params[:diff_id]) + else + @merge_request.merge_request_diff + end + + @merge_request_diffs = @merge_request.merge_request_diffs.viewable.select_without_diff + @comparable_diffs = @merge_request_diffs.select { |diff| diff.id < @merge_request_diff.id } + + if params[:start_sha].present? + @start_sha = params[:start_sha] + @start_version = @comparable_diffs.find { |diff| diff.head_commit_sha == @start_sha } + + unless @start_version + @start_sha = @merge_request_diff.head_commit_sha + @start_version = @merge_request_diff + end + end + + @diffs = + if @start_sha + @merge_request_diff.compare_with(@start_sha).diffs(diff_options) + else + @merge_request_diff.diffs(diff_options) + end + end + def define_diff_comment_vars @new_diff_note_attrs = { noteable_type: 'MergeRequest', noteable_id: @merge_request.id } + @diff_notes_disabled = !@merge_request_diff.latest? || @start_sha + @use_legacy_diff_notes = !@merge_request.has_complete_diff_refs? - @grouped_diff_discussions = @merge_request.grouped_diff_discussions + @grouped_diff_discussions = @merge_request.grouped_diff_discussions(@merge_request_diff.diff_refs) @notes = prepare_notes_for_rendering(@grouped_diff_discussions.values.flatten.flat_map(&:notes)) end @@ -678,16 +686,6 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request = MergeRequests::BuildService.new(project, current_user, merge_request_params.merge(diff_options: diff_options)).execute end - def compared_diff_version - @diff_notes_disabled = true - @diffs = @merge_request_diff.compare_with(@start_sha).diffs(diff_options) - end - - def original_diff_version - @diff_notes_disabled = !@merge_request_diff.latest? - @diffs = @merge_request_diff.diffs(diff_options) - end - def close_merge_request_without_source_project if !@merge_request.source_project && @merge_request.open? @merge_request.close diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 5e0886cc599..dc144906548 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -62,6 +62,8 @@ module DiffHelper end def parallel_diff_discussions(left, right, diff_file) + return unless @grouped_diff_discussions + discussions_left = discussions_right = nil if left && (left.unchanged? || left.removed?) diff --git a/app/helpers/events_helper.rb b/app/helpers/events_helper.rb index fb872a13f74..5f5c76d3722 100644 --- a/app/helpers/events_helper.rb +++ b/app/helpers/events_helper.rb @@ -1,4 +1,15 @@ module EventsHelper + ICON_NAMES_BY_EVENT_TYPE = { + 'pushed to' => 'icon_commit', + 'pushed new' => 'icon_commit', + 'created' => 'icon_status_open', + 'opened' => 'icon_status_open', + 'closed' => 'icon_status_closed', + 'accepted' => 'icon_code_fork', + 'commented on' => 'icon_comment_o', + 'deleted' => 'icon_trash_o' + }.freeze + def link_to_author(event) author = event.author @@ -183,4 +194,21 @@ module EventsHelper "event-inline" end end + + def icon_for_event(note) + icon_name = ICON_NAMES_BY_EVENT_TYPE[note] + custom_icon(icon_name) if icon_name + end + + def icon_for_profile_event(event) + if current_path?('users#show') + content_tag :div, class: "system-note-image #{event.action_name.parameterize}-icon" do + icon_for_event(event.action_name) + end + else + content_tag :div, class: 'system-note-image user-avatar' do + author_avatar(event, size: 32) + end + end + end end diff --git a/app/helpers/javascript_helper.rb b/app/helpers/javascript_helper.rb index 68c09c922a6..d5e77c7e271 100644 --- a/app/helpers/javascript_helper.rb +++ b/app/helpers/javascript_helper.rb @@ -3,7 +3,8 @@ module JavascriptHelper javascript_include_tag asset_path(js) end - def page_specific_javascript_bundle_tag(js) - javascript_include_tag(*webpack_asset_paths(js)) + # deprecated; use webpack_bundle_tag directly instead + def page_specific_javascript_bundle_tag(bundle) + webpack_bundle_tag(bundle) end end diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 5f3d89cf6cb..eab0738a368 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -61,12 +61,23 @@ module NotesHelper end def discussion_diff_path(discussion) - return unless discussion.diff_discussion? + if discussion.for_merge_request? && discussion.diff_discussion? + if discussion.active? + # Without a diff ID, the link always points to the latest diff version + diff_id = nil + elsif merge_request_diff = discussion.latest_merge_request_diff + diff_id = merge_request_diff.id + else + # If the discussion is not active, and we cannot find the latest + # merge request diff for this discussion, we return no path at all. + return + end - if discussion.for_merge_request? && discussion.active? - diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code) + diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, diff_id: diff_id, anchor: discussion.line_code) elsif discussion.for_commit? - namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code) + anchor = discussion.line_code if discussion.diff_discussion? + + namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: anchor) end end end diff --git a/app/helpers/webpack_helper.rb b/app/helpers/webpack_helper.rb new file mode 100644 index 00000000000..6bacda9fe75 --- /dev/null +++ b/app/helpers/webpack_helper.rb @@ -0,0 +1,30 @@ +require 'webpack/rails/manifest' + +module WebpackHelper + def webpack_bundle_tag(bundle) + javascript_include_tag(*gitlab_webpack_asset_paths(bundle)) + end + + # override webpack-rails gem helper until changes can make it upstream + def gitlab_webpack_asset_paths(source, extension: nil) + return "" unless source.present? + + paths = Webpack::Rails::Manifest.asset_paths(source) + if extension + paths = paths.select { |p| p.ends_with? ".#{extension}" } + end + + # include full webpack-dev-server url for rspec tests running locally + if Rails.env.test? && Rails.configuration.webpack.dev_server.enabled + host = Rails.configuration.webpack.dev_server.host + port = Rails.configuration.webpack.dev_server.port + protocol = Rails.configuration.webpack.dev_server.https ? 'https' : 'http' + + paths.map! do |p| + "#{protocol}://#{host}:#{port}#{p}" + end + end + + paths + end +end diff --git a/app/models/commit.rb b/app/models/commit.rb index 5c452f78546..8b8b3f00202 100644 --- a/app/models/commit.rb +++ b/app/models/commit.rb @@ -326,14 +326,13 @@ class Commit end def raw_diffs(*args) - use_gitaly = Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs) - deltas_only = args.last.is_a?(Hash) && args.last[:deltas_only] - - if use_gitaly && !deltas_only - Gitlab::GitalyClient::Commit.diff_from_parent(self, *args) - else - raw.diffs(*args) - end + # NOTE: This feature is intentionally disabled until + # https://gitlab.com/gitlab-org/gitaly/issues/178 is resolved + # if Gitlab::GitalyClient.feature_enabled?(:commit_raw_diffs) + # Gitlab::GitalyClient::Commit.diff_from_parent(self, *args) + # else + raw.diffs(*args) + # end end def diffs(diff_options = nil) diff --git a/app/models/concerns/discussion_on_diff.rb b/app/models/concerns/discussion_on_diff.rb index 87db0c810c3..8ee42875670 100644 --- a/app/models/concerns/discussion_on_diff.rb +++ b/app/models/concerns/discussion_on_diff.rb @@ -2,11 +2,9 @@ module DiscussionOnDiff extend ActiveSupport::Concern - included do - NUMBER_OF_TRUNCATED_DIFF_LINES = 16 - - memoized_values << :active + NUMBER_OF_TRUNCATED_DIFF_LINES = 16 + included do delegate :line_code, :original_line_code, :diff_file, @@ -29,12 +27,6 @@ module DiscussionOnDiff true end - def active? - return @active if @active.present? - - @active = first_note.active? - end - # Returns an array of at most 16 highlighted lines above a diff note def truncated_diff_lines(highlight: true) lines = highlight ? highlighted_diff_lines : diff_lines diff --git a/app/models/concerns/note_on_diff.rb b/app/models/concerns/note_on_diff.rb index 1a5a7007a2b..6c27dd5aa5c 100644 --- a/app/models/concerns/note_on_diff.rb +++ b/app/models/concerns/note_on_diff.rb @@ -25,4 +25,18 @@ module NoteOnDiff def diff_attributes raise NotImplementedError end + + def active?(diff_refs = nil) + raise NotImplementedError + end + + private + + def noteable_diff_refs + if noteable.respond_to?(:diff_sha_refs) + noteable.diff_sha_refs + else + noteable.diff_refs + end + end end diff --git a/app/models/concerns/noteable.rb b/app/models/concerns/noteable.rb index 772ff6a6d2f..dd1e6630642 100644 --- a/app/models/concerns/noteable.rb +++ b/app/models/concerns/noteable.rb @@ -36,10 +36,10 @@ module Noteable .discussions(self) end - def grouped_diff_discussions + def grouped_diff_discussions(*args) # Doesn't use `discussion_notes`, because this may include commit diff notes # besides MR diff notes, that we do no want to display on the MR Changes tab. - notes.inc_relations_for_view.grouped_diff_discussions + notes.inc_relations_for_view.grouped_diff_discussions(*args) end def resolvable_discussions diff --git a/app/models/diff_discussion.rb b/app/models/diff_discussion.rb index d9b7e484e0f..6a6466b493b 100644 --- a/app/models/diff_discussion.rb +++ b/app/models/diff_discussion.rb @@ -10,6 +10,7 @@ class DiffDiscussion < Discussion delegate :position, :original_position, + :latest_merge_request_diff, to: :first_note diff --git a/app/models/diff_note.rb b/app/models/diff_note.rb index 1523244f8a8..abe4518d62a 100644 --- a/app/models/diff_note.rb +++ b/app/models/diff_note.rb @@ -65,20 +65,18 @@ class DiffNote < Note self.position.diff_refs == diff_refs end + def latest_merge_request_diff + return unless for_merge_request? + + self.noteable.merge_request_diff_for(self.position.diff_refs) + end + private def supported? for_commit? || self.noteable.has_complete_diff_refs? end - def noteable_diff_refs - if noteable.respond_to?(:diff_sha_refs) - noteable.diff_sha_refs - else - noteable.diff_refs - end - end - def set_original_position self.original_position = self.position.dup unless self.original_position&.complete? end diff --git a/app/models/label.rb b/app/models/label.rb index 568fa6d44f5..d8b0e250732 100644 --- a/app/models/label.rb +++ b/app/models/label.rb @@ -21,6 +21,8 @@ class Label < ActiveRecord::Base has_many :issues, through: :label_links, source: :target, source_type: 'Issue' has_many :merge_requests, through: :label_links, source: :target, source_type: 'MergeRequest' + before_validation :strip_whitespace_from_title_and_color + validates :color, color: true, allow_blank: false # Don't allow ',' for label titles @@ -193,4 +195,8 @@ class Label < ActiveRecord::Base def sanitize_title(value) CGI.unescapeHTML(Sanitize.clean(value.to_s)) end + + def strip_whitespace_from_title_and_color + %w(color title).each { |attr| self[attr] = self[attr]&.strip } + end end diff --git a/app/models/legacy_diff_discussion.rb b/app/models/legacy_diff_discussion.rb index cb2651a03f8..e617ce36f56 100644 --- a/app/models/legacy_diff_discussion.rb +++ b/app/models/legacy_diff_discussion.rb @@ -7,6 +7,8 @@ class LegacyDiffDiscussion < Discussion include DiscussionOnDiff + memoized_values << :active + def legacy_diff_discussion? true end @@ -15,6 +17,12 @@ class LegacyDiffDiscussion < Discussion LegacyDiffNote end + def active?(*args) + return @active if @active.present? + + @active = first_note.active?(*args) + end + def collapsed? !active? end diff --git a/app/models/legacy_diff_note.rb b/app/models/legacy_diff_note.rb index 9a77557ebcd..d7c627432d2 100644 --- a/app/models/legacy_diff_note.rb +++ b/app/models/legacy_diff_note.rb @@ -56,11 +56,12 @@ class LegacyDiffNote < Note # # If the note's current diff cannot be matched in the MergeRequest's current # diff, it's considered inactive. - def active? + def active?(diff_refs = nil) return @active if defined?(@active) return true if for_commit? return true unless diff_line return false unless noteable + return false if diff_refs && diff_refs != noteable_diff_refs noteable_diff = find_noteable_diff diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index d02fa308b9f..1d4827375d7 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -366,6 +366,14 @@ class MergeRequest < ActiveRecord::Base merge_request_diff(true) end + def merge_request_diff_for(diff_refs) + @merge_request_diffs_by_diff_refs ||= Hash.new do |h, diff_refs| + h[diff_refs] = merge_request_diffs.viewable.select_without_diff.find_by_diff_refs(diff_refs) + end + + @merge_request_diffs_by_diff_refs[diff_refs] + end + def reload_diff_if_branch_changed if source_branch_changed? || target_branch_changed? reload_diff diff --git a/app/models/merge_request_diff.rb b/app/models/merge_request_diff.rb index 6ad56b842b2..6604af2b47e 100644 --- a/app/models/merge_request_diff.rb +++ b/app/models/merge_request_diff.rb @@ -31,6 +31,10 @@ class MergeRequestDiff < ActiveRecord::Base # It allows you to override variables like head_commit_sha before getting diff. after_create :save_git_content, unless: :importing? + def self.find_by_diff_refs(diff_refs) + find_by(start_commit_sha: diff_refs.start_sha, head_commit_sha: diff_refs.head_sha, base_commit_sha: diff_refs.base_sha) + end + def self.select_without_diff select(column_names - ['st_diffs']) end @@ -130,6 +134,12 @@ class MergeRequestDiff < ActiveRecord::Base st_commits.map { |commit| commit[:id] } end + def diff_refs=(new_diff_refs) + self.base_commit_sha = new_diff_refs&.base_sha + self.start_commit_sha = new_diff_refs&.start_sha + self.head_commit_sha = new_diff_refs&.head_sha + end + def diff_refs return unless start_commit_sha || base_commit_sha diff --git a/app/models/note.rb b/app/models/note.rb index 1ea7b946061..630d0adbece 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -96,6 +96,7 @@ class Note < ActiveRecord::Base before_validation :set_discussion_id, on: :create after_save :keep_around_commit, unless: :for_personal_snippet? after_save :expire_etag_cache + after_destroy :expire_etag_cache class << self def model_name @@ -113,11 +114,11 @@ class Note < ActiveRecord::Base Discussion.build(notes) end - def grouped_diff_discussions + def grouped_diff_discussions(diff_refs = nil) diff_notes. fresh. discussions. - select(&:active?). + select { |n| n.active?(diff_refs) }. group_by(&:line_code) end @@ -140,6 +141,10 @@ class Note < ActiveRecord::Base true end + def latest_merge_request_diff + nil + end + def max_attachment_size current_application_settings.max_attachment_size.megabytes.to_i end diff --git a/app/models/repository.rb b/app/models/repository.rb index 526ab1e77a7..2b11ed6128e 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -963,13 +963,15 @@ class Repository end def is_ancestor?(ancestor_id, descendant_id) - Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled| - if is_enabled - raw_repository.is_ancestor?(ancestor_id, descendant_id) - else - merge_base_commit(ancestor_id, descendant_id) == ancestor_id - end - end + # NOTE: This feature is intentionally disabled until + # https://gitlab.com/gitlab-org/gitlab-ce/issues/30586 is resolved + # Gitlab::GitalyClient.migrate(:is_ancestor) do |is_enabled| + # if is_enabled + # raw_repository.is_ancestor?(ancestor_id, descendant_id) + # else + merge_base_commit(ancestor_id, descendant_id) == ancestor_id + # end + # end end def empty_repo? diff --git a/app/policies/group_policy.rb b/app/policies/group_policy.rb index cb58c115d54..87398303c68 100644 --- a/app/policies/group_policy.rb +++ b/app/policies/group_policy.rb @@ -28,6 +28,7 @@ class GroupPolicy < BasePolicy can! :admin_namespace can! :admin_group_member can! :change_visibility_level + can! :create_subgroup if @user.can_create_group end if globally_viewable && @subject.request_access_enabled && !member diff --git a/app/views/admin/health_check/show.html.haml b/app/views/admin/health_check/show.html.haml index e79303240f0..6a208d76a38 100644 --- a/app/views/admin/health_check/show.html.haml +++ b/app/views/admin/health_check/show.html.haml @@ -13,7 +13,7 @@ = button_to reset_health_check_token_admin_application_settings_path, method: :put, class: 'btn btn-default', data: { confirm: 'Are you sure you want to reset the health check token?' } do - = icon('refresh') + = icon('spinner') Reset health check access token %p.light Health information can be retrieved as plain text, JSON, or XML using: diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index 7d26864d0f3..f118804cace 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -21,7 +21,7 @@ = button_to reset_runners_token_admin_application_settings_path, method: :put, class: 'btn btn-default', data: { confirm: 'Are you sure you want to reset registration token?' } do - = icon('refresh') + = icon('spinner') Reset runners registration token .bs-callout diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml index e04958817e4..8440fb3d785 100644 --- a/app/views/discussions/_discussion.html.haml +++ b/app/views/discussions/_discussion.html.haml @@ -20,21 +20,22 @@ = discussion.author.to_reference started a discussion + - url = discussion_diff_path(discussion) - if discussion.for_commit? && @noteable != discussion.noteable on - commit = discussion.noteable - if commit commit - - anchor = discussion.line_code if discussion.diff_discussion? - = link_to commit.short_id, namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: anchor), class: 'monospace' + = link_to commit.short_id, url, class: 'monospace' - else a deleted commit - elsif discussion.diff_discussion? on - - if discussion.active? - = link_to 'the diff', discussion_diff_path(discussion) - - else - an outdated diff + = conditional_link_to url.present?, url do + - if discussion.active? + the diff + - else + an outdated diff = time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago") = render "discussions/headline", discussion: discussion diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml index af97e9588a5..01e72862114 100644 --- a/app/views/events/event/_common.html.haml +++ b/app/views/events/event/_common.html.haml @@ -1,13 +1,4 @@ -- if event.target - - if event.action_name == "opened" - .profile-icon.open-icon - = custom_icon("icon_status_open") - - elsif event.action_name == "closed" - .profile-icon.closed-icon - = custom_icon("icon_status_closed") - - else - .profile-icon.fork-icon - = custom_icon("icon_code_fork") += icon_for_profile_event(event) .event-title %span.author_name= link_to_author event diff --git a/app/views/events/event/_created_project.html.haml b/app/views/events/event/_created_project.html.haml index fee85c94277..d8e59be57bb 100644 --- a/app/views/events/event/_created_project.html.haml +++ b/app/views/events/event/_created_project.html.haml @@ -1,5 +1,4 @@ -.profile-icon.open-icon - = custom_icon("icon_status_open") += icon_for_profile_event(event) .event-title %span.author_name= link_to_author event diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml index 83709f5e4d0..df4b9562215 100644 --- a/app/views/events/event/_note.html.haml +++ b/app/views/events/event/_note.html.haml @@ -1,5 +1,4 @@ -.profile-icon - = custom_icon("icon_comment_o") += icon_for_profile_event(event) .event-title %span.author_name= link_to_author event diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index efdc8764acf..c0943100ae3 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -1,10 +1,6 @@ - project = event.project -.profile-icon - - if event.action_name == "deleted" - = custom_icon("trash_o") - - else - = custom_icon("icon_commit") += icon_for_profile_event(event) .event-title %span.author_name= link_to_author event diff --git a/app/views/groups/subgroups.html.haml b/app/views/groups/subgroups.html.haml index be809083139..8f0724c0677 100644 --- a/app/views/groups/subgroups.html.haml +++ b/app/views/groups/subgroups.html.haml @@ -9,7 +9,7 @@ .nav-controls = form_tag request.path, method: :get do |f| = search_field_tag :filter_groups, params[:filter_groups], placeholder: 'Filter by name', class: 'form-control', spellcheck: false - - if can? current_user, :admin_group, @group + - if can?(current_user, :create_subgroup, @group) = link_to new_group_path(parent_id: @group.id), class: 'btn btn-new pull-right' do New Subgroup diff --git a/app/views/help/ui.html.haml b/app/views/help/ui.html.haml index 207f80bedfe..615dd56afbd 100644 --- a/app/views/help/ui.html.haml +++ b/app/views/help/ui.html.haml @@ -252,7 +252,7 @@ = icon('chevron-down') .dropdown-menu.dropdown-select.dropdown-menu-selectable .dropdown-title - %span Dropdown Title + %span Dropdown title %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times') .dropdown-input @@ -291,7 +291,7 @@ = icon('chevron-down') .dropdown-menu.dropdown-select.dropdown-menu-selectable.is-loading .dropdown-title - %span Dropdown Title + %span Dropdown title %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times') .dropdown-input @@ -335,7 +335,7 @@ = icon('chevron-down') .dropdown-menu.dropdown-select.dropdown-menu-selectable.dropdown-menu-user .dropdown-title - %span Dropdown Title + %span Dropdown title %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times') .dropdown-input @@ -362,7 +362,7 @@ .dropdown-title %button.dropdown-title-button.dropdown-menu-back{ aria: { label: "Go back" } } = icon('arrow-left') - %span Dropdown Title + %span Dropdown title %button.dropdown-title-button.dropdown-menu-close{ aria: { label: "Close" } } = icon('times') .dropdown-input diff --git a/app/views/layouts/_head.html.haml b/app/views/layouts/_head.html.haml index a611481a0a4..19473b6ab27 100644 --- a/app/views/layouts/_head.html.haml +++ b/app/views/layouts/_head.html.haml @@ -28,9 +28,9 @@ = stylesheet_link_tag "application", media: "all" = stylesheet_link_tag "print", media: "print" - = javascript_include_tag(*webpack_asset_paths("runtime")) - = javascript_include_tag(*webpack_asset_paths("common")) - = javascript_include_tag(*webpack_asset_paths("main")) + = webpack_bundle_tag "runtime" + = webpack_bundle_tag "common" + = webpack_bundle_tag "main" - if content_for?(:page_specific_javascripts) = yield :page_specific_javascripts diff --git a/app/views/projects/blob/_blob.html.haml b/app/views/projects/blob/_blob.html.haml index fd0ae42571e..9aafff343f0 100644 --- a/app/views/projects/blob/_blob.html.haml +++ b/app/views/projects/blob/_blob.html.haml @@ -25,13 +25,6 @@ #blob-content-holder.blob-content-holder %article.file-holder = render "projects/blob/header", blob: blob - - if current_user - .js-file-fork-suggestion-section.file-fork-suggestion.hidden - %span.file-fork-suggestion-note - You don't have permission to edit this file. Try forking this project to edit the file. - = link_to 'Fork', fork_path, method: :post, class: 'btn btn-grouped btn-inverted btn-new' - %button.js-cancel-fork-suggestion.btn.btn-grouped{ type: 'button' } - Cancel - if blob.empty? .file-content.code diff --git a/app/views/projects/blob/_header.html.haml b/app/views/projects/blob/_header.html.haml index c42bf3c324a..7a4a293548c 100644 --- a/app/views/projects/blob/_header.html.haml +++ b/app/views/projects/blob/_header.html.haml @@ -38,3 +38,10 @@ - if current_user = replace_blob_link = delete_blob_link +- if current_user + .js-file-fork-suggestion-section.file-fork-suggestion.hidden + %span.file-fork-suggestion-note + You don't have permission to edit this file. Try forking this project to edit the file. + = link_to 'Fork', fork_path, method: :post, class: 'btn btn-grouped btn-inverted btn-new' + %button.js-cancel-fork-suggestion.btn.btn-grouped{ type: 'button' } + Cancel diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml index f4a66398c85..c4159ce1a36 100644 --- a/app/views/projects/builds/_sidebar.html.haml +++ b/app/views/projects/builds/_sidebar.html.haml @@ -136,7 +136,7 @@ - else = build.id - if build.retried? - %i.fa.fa-refresh.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' } + %i.fa.fa-spinner.has-tooltip{ data: { container: 'body', placement: 'bottom' }, title: 'Job was retried' } :javascript new Sidebar(); diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index 4700b7a9a45..2c3fd1fcd4d 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -36,7 +36,7 @@ = icon('warning', class: 'text-warning has-tooltip', title: 'Job is stuck. Check runners.') - if retried - = icon('refresh', class: 'text-warning has-tooltip', title: 'Job was retried') + = icon('spinner', class: 'text-warning has-tooltip', title: 'Job was retried') .label-container - if job.tags.any? diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml index f920f359de2..45c95f7ab6a 100644 --- a/app/views/projects/diffs/_parallel_view.html.haml +++ b/app/views/projects/diffs/_parallel_view.html.haml @@ -5,8 +5,7 @@ - left = line[:left] - right = line[:right] - last_line = right.new_pos if right - - unless @diff_notes_disabled - - discussions_left, discussions_right = parallel_diff_discussions(left, right, diff_file) + - discussions_left, discussions_right = parallel_diff_discussions(left, right, diff_file) %tr.line_holder.parallel - if left - case left.type diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml index ebd1a914ee7..5f3968b6709 100644 --- a/app/views/projects/diffs/_text_file.html.haml +++ b/app/views/projects/diffs/_text_file.html.haml @@ -4,11 +4,10 @@ %a.show-suppressed-diff.js-show-suppressed-diff Changes suppressed. Click to show. %table.text-file.code.js-syntax-highlight{ data: diff_view_data, class: too_big ? 'hide' : '' } - - discussions = @grouped_diff_discussions unless @diff_notes_disabled = render partial: "projects/diffs/line", collection: diff_file.highlighted_diff_lines, as: :line, - locals: { diff_file: diff_file, discussions: discussions } + locals: { diff_file: diff_file, discussions: @grouped_diff_discussions } - if !diff_file.new_file && !diff_file.deleted_file && diff_file.highlighted_diff_lines.any? - last_line = diff_file.highlighted_diff_lines.last diff --git a/app/views/projects/merge_requests/show/_versions.html.haml b/app/views/projects/merge_requests/show/_versions.html.haml index 74a7b1dc498..547be78992e 100644 --- a/app/views/projects/merge_requests/show/_versions.html.haml +++ b/app/views/projects/merge_requests/show/_versions.html.haml @@ -72,13 +72,16 @@ = link_to namespace_project_compare_path(@project.namespace, @project, from: @start_version.base_commit_sha, to: @merge_request_diff.base_commit_sha) do new commits from - %code= @merge_request.target_branch + = succeed '.' do + %code= @merge_request.target_branch - - unless @merge_request_diff.latest? && !@start_sha + - if @diff_notes_disabled .comments-disabled-notif.content-block = icon('info-circle') - if @start_sha Comments are disabled because you're comparing two versions of this merge request. - else - Comments are disabled because you're viewing an old version of this merge request. - = link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm' + Discussions on this version of the merge request are displayed but comment creation is disabled. + + .pull-right + = link_to 'Show latest version', diffs_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-sm' diff --git a/app/views/shared/icons/_icon_merged.svg b/app/views/shared/icons/_icon_merged.svg index d8f96558bea..43d591daefa 100644 --- a/app/views/shared/icons/_icon_merged.svg +++ b/app/views/shared/icons/_icon_merged.svg @@ -1 +1 @@ -<svg width="14" height="14" viewBox="0 0 14 14" xmlns="http://www.w3.org/2000/svg"><path d="M0 7c0-3.866 3.142-7 7-7 3.866 0 7 3.142 7 7 0 3.866-3.142 7-7 7-3.866 0-7-3.142-7-7z"/><path d="M1 7c0 3.309 2.69 6 6 6 3.309 0 6-2.69 6-6 0-3.309-2.69-6-6-6-3.309 0-6 2.69-6 6z" fill="#FFF"/><path d="M9.427 6.523a.932.932 0 0 0-.808.489v-.01c-.49-.01-1.059-.172-1.46-.489-.35-.278-.7-.772-.882-1.17a.964.964 0 0 0 .35-.744.943.943 0 0 0-.934-.959c-.518 0-.933.432-.933.964 0 .35.191.662.467.825v3.147a.97.97 0 0 0-.467.825c0 .532.415.959.933.959a.943.943 0 0 0 .934-.96.965.965 0 0 0-.467-.824V6.844c.313.336.672.61 1.073.81.402.202.948.303 1.386.308v-.01c.168.293.467.49.808.49a.943.943 0 0 0 .933-.96.943.943 0 0 0-.933-.96z"/></svg> +<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="m2 3c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m.761.85c.154 2.556 1.987 4.692 4.45 5.255.328-.655 1.01-1.105 1.789-1.105 1.105 0 2 .895 2 2 0 1.105-.895 2-2 2-.89 0-1.645-.582-1.904-1.386-1.916-.376-3.548-1.5-4.596-3.044v4.493c.863.222 1.5 1.01 1.5 1.937 0 1.105-.895 2-2 2-1.105 0-2-.895-2-2 0-.74.402-1.387 1-1.732v-8.535c-.598-.346-1-.992-1-1.732 0-1.105.895-2 2-2 1.105 0 2 .895 2 2 0 .835-.512 1.551-1.239 1.85m6.239 7.15c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1m-7 4c.552 0 1-.448 1-1 0-.552-.448-1-1-1-.552 0-1 .448-1 1 0 .552.448 1 1 1" transform="translate(3)"/></svg> diff --git a/app/views/shared/icons/_trash_o.svg b/app/views/shared/icons/_icon_trash_o.svg index 0d7a91ab536..0d7a91ab536 100644 --- a/app/views/shared/icons/_trash_o.svg +++ b/app/views/shared/icons/_icon_trash_o.svg diff --git a/app/workers/build_coverage_worker.rb b/app/workers/build_coverage_worker.rb index def0ab1dde1..f7ae996bb17 100644 --- a/app/workers/build_coverage_worker.rb +++ b/app/workers/build_coverage_worker.rb @@ -3,7 +3,6 @@ class BuildCoverageWorker include BuildQueue def perform(build_id) - Ci::Build.find_by(id: build_id) - .try(:update_coverage) + Ci::Build.find_by(id: build_id)&.update_coverage end end |