diff options
43 files changed, 260 insertions, 221 deletions
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2dc09e4a1a5..ec53271b6bc 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,7 +9,7 @@ variables: MYSQL_ALLOW_EMPTY_PASSWORD: "1" before_script: - - ./scripts/prepare_build.sh + - source ./scripts/prepare_build.sh - ruby -v - which ruby - gem install bundler --no-ri --no-rdoc diff --git a/CHANGELOG b/CHANGELOG index 0d3e0571b59..7af6a22f37f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -5,6 +5,7 @@ v 8.5.0 (unreleased) - Ignore binary files in code search to prevent Error 500 (Stan Hu) - Upgrade gitlab_git to 7.2.23 to fix commit message mentions in first branch push - New UI for pagination + - Fix diff comments loaded by AJAX to load comment with diff in discussion tab v 8.4.0 - Allow LDAP users to change their email if it was not set by the LDAP server @@ -212,6 +212,9 @@ gem 'select2-rails', '~> 3.5.9' gem 'virtus', '~> 1.0.1' gem 'net-ssh', '~> 3.0.1' +# Sentry integration +gem 'sentry-raven' + # Metrics group :metrics do gem 'allocations', '~> 1.0', require: false, platform: :mri @@ -293,9 +296,6 @@ end group :production do gem "gitlab_meta", '7.0' - - # Sentry integration - gem 'sentry-raven' end gem "newrelic_rpm", '~> 3.9.4.245' diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 2bfc5cb2d9c..53d72be66e3 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -15,6 +15,8 @@ class @Notes @last_fetched_at = last_fetched_at @view = view @noteable_url = document.URL + @notesCountBadge ||= $(".issuable-details").find(".notes-tab .badge") + @initRefresh() @setupMainTargetNoteForm() @cleanBinding() @@ -89,7 +91,7 @@ class @Notes , 15000 refresh: -> - unless document.hidden or (@noteable_url != document.URL) + if not document.hidden and document.URL.indexOf(@noteable_url) is 0 @getContent() getContent: -> @@ -101,7 +103,10 @@ class @Notes notes = data.notes @last_fetched_at = data.last_fetched_at $.each notes, (i, note) => - @renderNote(note) + if note.discussion_with_diff_html? + @renderDiscussionNote(note) + else + @renderNote(note) ### @@ -116,18 +121,21 @@ class @Notes flash.pinTo('.header-content') return + if note.award + awards_handler.addAwardToEmojiBar(note.note) + awards_handler.scrollToAwards() + # render note if it not present in loaded list # or skip if rendered - if @isNewNote(note) && !note.award + else if @isNewNote(note) @note_ids.push(note.id) - $('ul.main-notes-list'). - append(note.html). - syntaxHighlight() + + $('ul.main-notes-list') + .append(note.html) + .syntaxHighlight() @initTaskList() + @updateNotesCount(1) - if note.award - awards_handler.addAwardToEmojiBar(note.note) - awards_handler.scrollToAwards() ### Check if note does not exists on page @@ -144,34 +152,39 @@ class @Notes Note: for rendering inline notes use renderDiscussionNote ### renderDiscussionNote: (note) -> + return unless @isNewNote(note) + @note_ids.push(note.id) - form = $("form[rel='" + note.discussion_id + "']") + form = $("#new-discussion-note-form-#{note.discussion_id}") row = form.closest("tr") note_html = $(note.html) note_html.syntaxHighlight() # is this the first note of discussion? - if row.is(".js-temp-notes-holder") + discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']") + if discussionContainer.length is 0 # insert the note and the reply button after the temp row row.after note.discussion_html # remove the note (will be added again below) row.next().find(".note").remove() + # Before that, the container didn't exist + discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']") + # Add note to 'Changes' page discussions - $(".notes[rel='" + note.discussion_id + "']").append note_html + discussionContainer.append note_html # Init discussion on 'Discussion' page if it is merge request page - if $('body').attr('data-page').indexOf('projects:merge_request') == 0 - discussion_html = $(note.discussion_with_diff_html) - discussion_html.syntaxHighlight() - $('ul.main-notes-list').append(discussion_html) + if $('body').attr('data-page').indexOf('projects:merge_request') is 0 + $('ul.main-notes-list') + .append(note.discussion_with_diff_html) + .syntaxHighlight() else # append new note to all matching discussions - $(".notes[rel='" + note.discussion_id + "']").append note_html + discussionContainer.append note_html - # cleanup after successfully creating a diff/discussion note - @removeDiscussionNoteForm(form) + @updateNotesCount(1) ### Called in response the main target form has been successfully submitted. @@ -278,6 +291,9 @@ class @Notes addDiscussionNote: (xhr, note, status) => @renderDiscussionNote(note) + # cleanup after successfully creating a diff/discussion note + @removeDiscussionNoteForm($("#new-discussion-note-form-#{note.discussion_id}")) + ### Called in response to the edit note form being submitted @@ -349,30 +365,32 @@ class @Notes Removes the actual note from view. Removes the whole discussion if the last note is being removed. ### - removeNote: -> - note = $(this).closest(".note") - note_id = note.attr('id') + removeNote: (e) => + noteId = $(e.currentTarget) + .closest(".note") + .attr("id") - $('.note[id="' + note_id + '"]').each -> - note = $(this) + # A same note appears in the "Discussion" and in the "Changes" tab, we have + # to remove all. Using $(".note[id='noteId']") ensure we get all the notes, + # where $("#noteId") would return only one. + $(".note[id='#{noteId}']").each (i, el) => + note = $(el) notes = note.closest(".notes") - count = notes.closest(".issuable-details").find(".notes-tab .badge") # check if this is the last note for this line if notes.find(".note").length is 1 - # for discussions - notes.closest(".discussion").remove() + # "Discussions" tab + notes.closest(".timeline-entry").remove() - # for diff lines + # "Changes" tab / commit view notes.closest("tr").remove() - # update notes count - oldNum = parseInt(count.text()) - count.text(oldNum - 1) - note.remove() + # Decrement the "Discussions" counter only once + @updateNotesCount(-1) + ### Called in response to clicking the delete attachment link @@ -412,7 +430,7 @@ class @Notes ### setupDiscussionNoteForm: (dataHolder, form) => # setup note target - form.attr "rel", dataHolder.data("discussionId") + form.attr 'id', "new-discussion-note-form-#{dataHolder.data("discussionId")}" form.find("#line_type").val dataHolder.data("lineType") form.find("#note_commit_id").val dataHolder.data("commitId") form.find("#note_line_code").val dataHolder.data("lineCode") @@ -542,3 +560,6 @@ class @Notes updateTaskList: -> $('form', this).submit() + + updateNotesCount: (updateCount) -> + @notesCountBadge.text(parseInt(@notesCountBadge.text()) + updateCount) diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index d0f5d33bf4d..bd89cc7dc1d 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -146,6 +146,10 @@ border-bottom: 1px solid $border-color; &.oneline-block { - line-height: 42px; + line-height: 36px; + } + + > .controls { + float: right; } } diff --git a/app/assets/stylesheets/framework/panels.scss b/app/assets/stylesheets/framework/panels.scss index 57b9451b264..ae7bdf14c40 100644 --- a/app/assets/stylesheets/framework/panels.scss +++ b/app/assets/stylesheets/framework/panels.scss @@ -2,7 +2,13 @@ margin-bottom: $gl-padding; .panel-heading { - padding: 7px $gl-padding; + padding: $gl-vert-padding $gl-padding; + line-height: 36px; + + .controls { + margin-top: -2px; + float: right; + } } .panel-body { @@ -14,7 +20,3 @@ } } } - -.container-blank .panel .panel-heading { - line-height: 42px !important; -} diff --git a/app/assets/stylesheets/framework/tw_bootstrap.scss b/app/assets/stylesheets/framework/tw_bootstrap.scss index 88072606bf5..3e709244879 100644 --- a/app/assets/stylesheets/framework/tw_bootstrap.scss +++ b/app/assets/stylesheets/framework/tw_bootstrap.scss @@ -114,22 +114,9 @@ * */ -.container-blank .panel .panel-heading { - font-size: 17px; - line-height: 38px; -} - .panel { box-shadow: none; - .panel-heading { - .panel-head-actions { - position: relative; - top: -5px; - float: right; - } - } - .panel-body { form, pre { margin: 0; diff --git a/app/assets/stylesheets/highlight/solarized_dark.scss b/app/assets/stylesheets/highlight/solarized_dark.scss index ae7ecc65c39..b0aaadd0f7f 100644 --- a/app/assets/stylesheets/highlight/solarized_dark.scss +++ b/app/assets/stylesheets/highlight/solarized_dark.scss @@ -26,7 +26,7 @@ } .diff-line-num.old, .line_content.old { - @include diff_background(rgba(220, 50, 47, 0.2), rgba(220, 50, 47, 0.3), #808080); + @include diff_background(rgba(220, 50, 47, 0.3), rgba(220, 50, 47, 0.3), #808080); } .line_content.match { diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 6f1e186d408..4a2599dda37 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -11,11 +11,9 @@ class Projects::NotesController < Projects::ApplicationController notes_json = { notes: [], last_fetched_at: current_fetched_at } @notes.each do |note| - notes_json[:notes] << { - id: note.id, - html: note_to_html(note), - valid: note.valid? - } + next if note.cross_reference_not_visible_for?(current_user) + + notes_json[:notes] << note_json(note) end render json: notes_json @@ -25,7 +23,7 @@ class Projects::NotesController < Projects::ApplicationController @note = Notes::CreateService.new(project, current_user, note_params).execute respond_to do |format| - format.json { render_note_json(@note) } + format.json { render json: note_json(@note) } format.html { redirect_back_or_default } end end @@ -34,7 +32,7 @@ class Projects::NotesController < Projects::ApplicationController @note = Notes::UpdateService.new(project, current_user, note_params).execute(note) respond_to do |format| - format.json { render_note_json(@note) } + format.json { render json: note_json(@note) } format.html { redirect_back_or_default } end end @@ -99,6 +97,8 @@ class Projects::NotesController < Projects::ApplicationController end def note_to_discussion_html(note) + return unless note.for_diff_line? + if params[:view] == 'parallel' template = "projects/notes/_diff_notes_with_reply_parallel" locals = @@ -131,9 +131,9 @@ class Projects::NotesController < Projects::ApplicationController ) end - def render_note_json(note) + def note_json(note) if note.valid? - render json: { + { valid: true, id: note.id, discussion_id: note.discussion_id, @@ -144,7 +144,7 @@ class Projects::NotesController < Projects::ApplicationController discussion_with_diff_html: note_to_discussion_with_diff_html(note) } else - render json: { + { valid: false, award: note.is_award, errors: note.errors @@ -163,8 +163,6 @@ class Projects::NotesController < Projects::ApplicationController ) end - private - def find_current_user_notes @notes = NotesFinder.new.execute(project, current_user, params) end diff --git a/app/helpers/snippets_helper.rb b/app/helpers/snippets_helper.rb index 906cb12cd48..bc36434f549 100644 --- a/app/helpers/snippets_helper.rb +++ b/app/helpers/snippets_helper.rb @@ -17,4 +17,79 @@ module SnippetsHelper snippet_path(snippet) end end + + # Get an array of line numbers surrounding a matching + # line, bounded by min/max. + # + # @returns Array of line numbers + def bounded_line_numbers(line, min, max, surrounding_lines) + lower = line - surrounding_lines > min ? line - surrounding_lines : min + upper = line + surrounding_lines < max ? line + surrounding_lines : max + (lower..upper).to_a + end + + # Returns a sorted set of lines to be included in a snippet preview. + # This ensures matching adjacent lines do not display duplicated + # surrounding code. + # + # @returns Array, unique and sorted. + def matching_lines(lined_content, surrounding_lines) + used_lines = [] + lined_content.each_with_index do |line, line_number| + used_lines.concat bounded_line_numbers( + line_number, + 0, + lined_content.size, + surrounding_lines + ) if line.include?(query) + end + + used_lines.uniq.sort + end + + # 'Chunkify' entire snippet. Splits the snippet data into matching lines + + # surrounding_lines() worth of unmatching lines. + # + # @returns a hash with {snippet_object, snippet_chunks:{data,start_line}} + def chunk_snippet(snippet, surrounding_lines = 3) + lined_content = snippet.content.split("\n") + used_lines = matching_lines(lined_content, surrounding_lines) + + snippet_chunk = [] + snippet_chunks = [] + snippet_start_line = 0 + last_line = -1 + + # Go through each used line, and add consecutive lines as a single chunk + # to the snippet chunk array. + used_lines.each do |line_number| + if last_line < 0 + # Start a new chunk. + snippet_start_line = line_number + snippet_chunk << lined_content[line_number] + elsif last_line == line_number - 1 + # Consecutive line, continue chunk. + snippet_chunk << lined_content[line_number] + else + # Non-consecutive line, add chunk to chunk array. + snippet_chunks << { + data: snippet_chunk.join("\n"), + start_line: snippet_start_line + 1 + } + + # Start a new chunk. + snippet_chunk = [lined_content[line_number]] + snippet_start_line = line_number + end + last_line = line_number + end + # Add final chunk to chunk array + snippet_chunks << { + data: snippet_chunk.join("\n"), + start_line: snippet_start_line + 1 + } + + # Return snippet with chunk array + { snippet_object: snippet, snippet_chunks: snippet_chunks } + end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 7beba984608..5f58c0508fd 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -38,6 +38,7 @@ class Issue < ActiveRecord::Base scope :cared, ->(user) { where(assignee_id: user) } scope :open_for, ->(user) { opened.assigned_to(user) } + scope :in_projects, ->(project_ids) { where(project_id: project_ids) } state_machine :state, initial: :opened do event :close do diff --git a/app/models/project.rb b/app/models/project.rb index 5579710a476..4bd51449c25 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -904,4 +904,8 @@ class Project < ActiveRecord::Base def runners_token ensure_runners_token! end + + def wiki + @wiki ||= ProjectWiki.new(self, self.owner) + end end diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 8ce47495971..c847eba8d1c 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -12,6 +12,7 @@ class ProjectWiki # Returns a string describing what went wrong after # an operation fails. attr_reader :error_message + attr_reader :project def initialize(project, user = nil) @project = project diff --git a/app/views/admin/deploy_keys/index.html.haml b/app/views/admin/deploy_keys/index.html.haml index 841e6971fb2..41c43899978 100644 --- a/app/views/admin/deploy_keys/index.html.haml +++ b/app/views/admin/deploy_keys/index.html.haml @@ -2,7 +2,7 @@ .panel.panel-default .panel-heading Public deploy keys (#{@deploy_keys.count}) - .panel-head-actions + .controls = link_to 'New Deploy Key', new_admin_deploy_key_path, class: "btn btn-new btn-sm" - if @deploy_keys.any? .table-holder diff --git a/app/views/admin/projects/index.html.haml b/app/views/admin/projects/index.html.haml index d9b481404f7..b96ad6e2208 100644 --- a/app/views/admin/projects/index.html.haml +++ b/app/views/admin/projects/index.html.haml @@ -1,7 +1,7 @@ - page_title "Projects" = render 'shared/show_aside' -.row +.row.prepend-top-default %aside.col-md-3 .admin-filter = form_tag admin_namespaces_projects_path, method: :get, class: '' do @@ -47,7 +47,7 @@ .panel.panel-default .panel-heading Projects (#{@projects.total_count}) - .panel-head-actions + .controls .dropdown.inline %button.dropdown-toggle.btn.btn-sm{type: 'button', 'data-toggle' => 'dropdown'} %span.light sort: diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index e2f97fd9337..3430f56a9c9 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -1,5 +1,4 @@ - header_title group_title(@group, "Settings", edit_group_path(@group)) -- @blank_container = true .panel.panel-default.prepend-top-default .panel-heading diff --git a/app/views/groups/group_members/index.html.haml b/app/views/groups/group_members/index.html.haml index 6a8acc42af9..6b7fd5746d6 100644 --- a/app/views/groups/group_members/index.html.haml +++ b/app/views/groups/group_members/index.html.haml @@ -1,6 +1,5 @@ - page_title "Members" - header_title group_title(@group, "Members", group_group_members_path(@group)) -- @blank_container = true .group-members-page.prepend-top-default - if current_user && current_user.can?(:admin_group_member, @group) @@ -20,7 +19,7 @@ group members %small (#{@members.total_count}) - .pull-right + .controls = form_tag group_group_members_path(@group), method: :get, class: 'form-inline member-search-form' do .form-group = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control', spellcheck: false } diff --git a/app/views/groups/projects.html.haml b/app/views/groups/projects.html.haml index 9ca11ed1177..dd75766121e 100644 --- a/app/views/groups/projects.html.haml +++ b/app/views/groups/projects.html.haml @@ -6,9 +6,9 @@ %strong= @group.name projects: - if can? current_user, :admin_group, @group - .panel-head-actions + .controls = link_to new_project_path(namespace_id: @group.id), class: "btn btn-sm btn-success" do - %i.fa.fa-plus + = icon('plus') New Project %ul.well-list - @projects.each do |project| diff --git a/app/views/profiles/accounts/show.html.haml b/app/views/profiles/accounts/show.html.haml index a42fd38de3a..52bfc595fda 100644 --- a/app/views/profiles/accounts/show.html.haml +++ b/app/views/profiles/accounts/show.html.haml @@ -1,6 +1,5 @@ - page_title "Account" - header_title page_title, profile_account_path -- @blank_container = true - if current_user.ldap_user? .alert.alert-info diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 7ed898ce72f..51dcca7a1ab 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -35,7 +35,7 @@ Edit .issue-details.issuable-details - .detail-page-description.gray-content-block.second-block + .detail-page-description.content-block %h2.title = markdown escape_once(@issue.title), pipeline: :single_line %div @@ -50,7 +50,7 @@ .merge-requests = render 'merge_requests' - .gray-content-block.second-block.oneline-block + .content-block = render 'votes/votes_block', votable: @issue .row diff --git a/app/views/projects/merge_requests/_show.html.haml b/app/views/projects/merge_requests/_show.html.haml index 200bfa5ac4f..8641c3d8b4b 100644 --- a/app/views/projects/merge_requests/_show.html.haml +++ b/app/views/projects/merge_requests/_show.html.haml @@ -66,7 +66,7 @@ .tab-content #notes.notes.tab-pane.voting_notes - .gray-content-block.second-block.oneline-block + .content-block.oneline-block = render 'votes/votes_block', votable: @merge_request .row diff --git a/app/views/projects/merge_requests/show/_commits.html.haml b/app/views/projects/merge_requests/show/_commits.html.haml index 7f904ec42a0..a8f09f855d4 100644 --- a/app/views/projects/merge_requests/show/_commits.html.haml +++ b/app/views/projects/merge_requests/show/_commits.html.haml @@ -1,4 +1,4 @@ -.gray-content-block.middle-block.oneline-block +.content-block.oneline-block = icon("sort-amount-desc") Most recent commits displayed first diff --git a/app/views/projects/merge_requests/show/_how_to_merge.html.haml b/app/views/projects/merge_requests/show/_how_to_merge.html.haml index 877cc3d744b..0dbd159298e 100644 --- a/app/views/projects/merge_requests/show/_how_to_merge.html.haml +++ b/app/views/projects/merge_requests/show/_how_to_merge.html.haml @@ -45,6 +45,10 @@ - unless @merge_request.can_be_merged_by?(current_user) %p Note that pushing to GitLab requires write access to this repository. + %p + %strong Tip: + You can also checkout merge requests locally by + %a{href: 'https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/workflow/merge_requests.md#checkout-merge-requests-locally', target: '_blank'} following these guidelines :javascript $(function(){ diff --git a/app/views/projects/merge_requests/show/_mr_box.html.haml b/app/views/projects/merge_requests/show/_mr_box.html.haml index 0f81e5e8914..905823f79d9 100644 --- a/app/views/projects/merge_requests/show/_mr_box.html.haml +++ b/app/views/projects/merge_requests/show/_mr_box.html.haml @@ -1,4 +1,4 @@ -.detail-page-description.gray-content-block.second-block +.detail-page-description.content-block %h2.title = markdown escape_once(@merge_request.title), pipeline: :single_line diff --git a/app/views/projects/milestones/show.html.haml b/app/views/projects/milestones/show.html.haml index 1142c584592..528a4f9552f 100644 --- a/app/views/projects/milestones/show.html.haml +++ b/app/views/projects/milestones/show.html.haml @@ -32,7 +32,7 @@ = icon('pencil-square-o') Edit -.detail-page-description.gray-content-block.second-block +.detail-page-description.content-block %h2.title = markdown escape_once(@milestone.title), pipeline: :single_line %div @@ -73,8 +73,8 @@ .tab-content .tab-pane.active#tab-issues - .gray-content-block.middle-block - .pull-right + .content-block.oneline-block + .controls - if can?(current_user, :create_issue, @project) = link_to new_namespace_project_issue_path(@project.namespace, @project, issue: { milestone_id: @milestone.id }), class: "btn btn-grouped", title: "New Issue" do %i.fa.fa-plus @@ -94,8 +94,8 @@ = render('issues', title: 'Completed Issues (closed)', issues: @issues.closed, id: 'closed') .tab-pane#tab-merge-requests - .gray-content-block.middle-block - .pull-right + .content-block.oneline-block + .controls - if can?(current_user, :read_merge_request, @project) = link_to 'Browse Merge Requests', namespace_project_merge_requests_path(@milestone.project.namespace, @milestone.project, milestone_title: @milestone.title), class: "btn btn-grouped" @@ -117,9 +117,8 @@ = render 'merge_request', merge_request: merge_request .tab-pane#tab-participants - .gray-content-block.middle-block - .oneline - All participants to this milestone + .content-block.oneline-block + All participants to this milestone %ul.bordered-list - @users.each do |user| diff --git a/app/views/projects/notes/_diff_notes_with_reply.html.haml b/app/views/projects/notes/_diff_notes_with_reply.html.haml index c731baf0a65..11f9859a90f 100644 --- a/app/views/projects/notes/_diff_notes_with_reply.html.haml +++ b/app/views/projects/notes/_diff_notes_with_reply.html.haml @@ -7,7 +7,7 @@ %i.fa.fa-comment = notes.count %td.notes_content - %ul.notes{ rel: note.discussion_id } + %ul.notes{ data: { discussion_id: note.discussion_id } } = render notes .discussion-reply-holder = link_to_reply_diff(note) diff --git a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml index c6726cbafa3..bb761ed2f94 100644 --- a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml +++ b/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml @@ -8,7 +8,7 @@ %i.fa.fa-comment = notes_left.count %td.notes_content.parallel.old - %ul.notes{ rel: note1.discussion_id } + %ul.notes{ data: { discussion_id: note1.discussion_id } } = render notes_left .discussion-reply-holder @@ -23,7 +23,7 @@ %i.fa.fa-comment = notes_right.count %td.notes_content.parallel.new - %ul.notes{ rel: note2.discussion_id } + %ul.notes{ data: { discussion_id: note2.discussion_id } } = render notes_right .discussion-reply-holder @@ -31,4 +31,3 @@ - else %td.notes_line.new= "" %td.notes_content.parallel.new= "" - diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index 922535e5c4a..e858c412836 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -1,4 +1,4 @@ -%li.timeline-entry{ id: dom_id(note), class: [dom_class(note), "note-row-#{note.id}", ('system-note' if note.system)], data: { discussion: note.discussion_id } } +%li.timeline-entry{ id: dom_id(note), class: [dom_class(note), "note-row-#{note.id}", ('system-note' if note.system)] } .timeline-entry-inner .timeline-icon %a{href: user_path(note.author)} diff --git a/app/views/projects/notes/discussions/_commit.html.haml b/app/views/projects/notes/discussions/_commit.html.haml index 6903fad4a0a..3da2f2060b8 100644 --- a/app/views/projects/notes/discussions/_commit.html.haml +++ b/app/views/projects/notes/discussions/_commit.html.haml @@ -20,8 +20,7 @@ = render "projects/notes/discussions/diff", discussion_notes: discussion_notes, note: note - else .panel.panel-default - .notes{ rel: discussion_notes.first.discussion_id } + .notes{ data: { discussion_id: discussion_notes.first.discussion_id } } = render discussion_notes .discussion-reply-holder = link_to_reply_diff(discussion_notes.first) - diff --git a/app/views/projects/project_members/_group_members.html.haml b/app/views/projects/project_members/_group_members.html.haml index 1c2458fa144..c53033e367c 100644 --- a/app/views/projects/project_members/_group_members.html.haml +++ b/app/views/projects/project_members/_group_members.html.haml @@ -5,7 +5,7 @@ %small (#{members.count}) - if can?(current_user, :admin_group_member, @group) - .pull-right + .controls = link_to group_group_members_path(@group), class: 'btn' do = icon('pencil-square-o') Manage group members diff --git a/app/views/projects/project_members/_team.html.haml b/app/views/projects/project_members/_team.html.haml index ccddab13aaf..e8dce30425f 100644 --- a/app/views/projects/project_members/_team.html.haml +++ b/app/views/projects/project_members/_team.html.haml @@ -4,7 +4,7 @@ project members %small (#{members.count}) - .pull-right + .controls = form_tag namespace_project_project_members_path(@project.namespace, @project), method: :get, class: 'form-inline member-search-form' do .form-group = search_field_tag :search, params[:search], { placeholder: 'Find existing member by name', class: 'form-control', spellcheck: false } diff --git a/app/views/projects/project_members/index.html.haml b/app/views/projects/project_members/index.html.haml index 6239a148905..0f8848a5cbe 100644 --- a/app/views/projects/project_members/index.html.haml +++ b/app/views/projects/project_members/index.html.haml @@ -1,13 +1,12 @@ - page_title "Members" = render "header_title" -- @blank_container = true .project-members-page.prepend-top-default - if can?(current_user, :admin_project_member, @project) .panel.panel-default .panel-heading Add new user to project - .pull-right + .controls = link_to import_namespace_project_project_members_path(@project.namespace, @project), class: "btn btn-grouped", title: "Import members from another project" do Import members .panel-body diff --git a/app/views/search/results/_merge_request.html.haml b/app/views/search/results/_merge_request.html.haml index 2efa616d664..faeb2b55c6f 100644 --- a/app/views/search/results/_merge_request.html.haml +++ b/app/views/search/results/_merge_request.html.haml @@ -6,7 +6,7 @@ - if merge_request.description.present? .description.term = preserve do - = search_md_sanitize(markdown(merge_request.description)) + = search_md_sanitize(markdown(merge_request.description, { project: merge_request.project })) %span.light #{merge_request.project.name_with_namespace} .pull-right diff --git a/doc/administration/housekeeping.md b/doc/administration/housekeeping.md index c27cf1812dc..a5fa7d358a2 100644 --- a/doc/administration/housekeeping.md +++ b/doc/administration/housekeeping.md @@ -4,8 +4,8 @@ _**Note:** This feature was [introduced][ce-2371] in GitLab 8.4_ --- -The housekeeping function runs [`git gc`][man] on the current project Git -repository. +The housekeeping function runs `git gc` ([man page][man]) on the current +project Git repository. `git gc` runs a number of housekeeping tasks, such as compressing file revisions (to reduce disk space and increase performance) and removing diff --git a/doc/ci/build_artifacts/README.md b/doc/ci/build_artifacts/README.md index 61418cef2c6..b112beef7b0 100644 --- a/doc/ci/build_artifacts/README.md +++ b/doc/ci/build_artifacts/README.md @@ -152,7 +152,7 @@ inside GitLab that make that possible. 1. While inside a specific build, you are presented with a download button along with the one that browses the archive -1. And finally, when browsing and archive you can see the download button at +1. And finally, when browsing an archive you can see the download button at the top right corner --- diff --git a/doc/integration/README.md b/doc/integration/README.md index 5edac746c7b..846526f4e80 100644 --- a/doc/integration/README.md +++ b/doc/integration/README.md @@ -8,7 +8,7 @@ See the documentation below for details on how to configure these services. - [Jira](jira.md) Integrate with the JIRA issue tracker - [External issue tracker](external-issue-tracker.md) Redmine, JIRA, etc. - [LDAP](ldap.md) Set up sign in via LDAP -- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab, and Google via OAuth. +- [OmniAuth](omniauth.md) Sign in via Twitter, GitHub, GitLab.com, Google, Bitbucket, Facebook, Shibboleth, SAML, Crowd and Azure - [SAML](saml.md) Configure GitLab as a SAML 2.0 Service Provider - [CAS](cas.md) Configure GitLab to sign in using CAS - [Slack](slack.md) Integrate with the Slack chat service diff --git a/features/project/merge_requests.feature b/features/project/merge_requests.feature index f1629a26f10..4f780aa680f 100644 --- a/features/project/merge_requests.feature +++ b/features/project/merge_requests.feature @@ -102,6 +102,15 @@ Feature: Project Merge Requests And I leave a comment like "Line is wrong" on diff And I switch to the merge request's comments tab Then I should see a discussion has started on diff + And I should see a badge of "1" next to the discussion link + + @javascript + Scenario: I see a new comment on merge request diff from another user in the discussion tab + Given project "Shop" have "Bug NS-05" open merge request with diffs inside + And I visit merge request page "Bug NS-05" + And user "John Doe" leaves a comment like "Line is wrong" on diff + Then I should see a discussion by user "John Doe" has started on diff + And I should see a badge of "1" next to the discussion link @javascript Scenario: I edit a comment on a merge request diff @@ -119,9 +128,11 @@ Feature: Project Merge Requests And I visit merge request page "Bug NS-05" And I click on the Changes tab And I leave a comment like "Line is wrong" on diff + And I should see a badge of "1" next to the discussion link And I delete the comment "Line is wrong" on diff And I click on the Discussion tab Then I should not see any discussion + And I should see a badge of "0" next to the discussion link @javascript Scenario: I comment on a line of a commit in merge request diff --git a/features/steps/project/merge_requests.rb b/features/steps/project/merge_requests.rb index 8af635689e0..337893e6209 100644 --- a/features/steps/project/merge_requests.rb +++ b/features/steps/project/merge_requests.rb @@ -181,6 +181,15 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps leave_comment "Line is wrong" end + step 'user "John Doe" leaves a comment like "Line is wrong" on diff' do + mr = MergeRequest.find_by(title: "Bug NS-05") + create(:note_on_merge_request_diff, project: project, + noteable_id: mr.id, + author: user_exists("John Doe"), + line_code: sample_commit.line_code, + note: 'Line is wrong') + end + step 'I leave a comment like "Line is wrong" on diff in commit' do click_diff_line(sample_commit.line_code) leave_comment "Line is wrong" @@ -238,6 +247,22 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps end end + step 'I should see a discussion by user "John Doe" has started on diff' do + page.within(".notes .discussion") do + page.should have_content "#{user_exists("John Doe").name} started a discussion" + page.should have_content sample_commit.line_code_path + page.should have_content "Line is wrong" + end + end + + step 'I should see a badge of "1" next to the discussion link' do + expect_discussion_badge_to_have_counter("1") + end + + step 'I should see a badge of "0" next to the discussion link' do + expect_discussion_badge_to_have_counter("0") + end + step 'I should see a discussion has started on commit diff' do page.within(".notes .discussion") do page.should have_content "#{current_user.name} started a discussion on commit" @@ -452,4 +477,10 @@ class Spinach::Features::ProjectMergeRequests < Spinach::FeatureSteps def have_visible_content (text) have_css("*", text: text, visible: true) end + + def expect_discussion_badge_to_have_counter(value) + page.within(".notes-tab .badge") do + page.should have_content value + end + end end diff --git a/features/steps/shared/diff_note.rb b/features/steps/shared/diff_note.rb index c6a0ae2ba38..06e69441894 100644 --- a/features/steps/shared/diff_note.rb +++ b/features/steps/shared/diff_note.rb @@ -23,7 +23,7 @@ module SharedDiffNote page.within(diff_file_selector) do click_diff_line(sample_commit.line_code) - page.within("form[rel$='#{sample_commit.line_code}']") do + page.within("form[id$='#{sample_commit.line_code}']") do fill_in "note[note]", with: "Typo, please fix" find(".js-comment-button").trigger("click") sleep 0.05 @@ -33,7 +33,7 @@ module SharedDiffNote step 'I leave a diff comment in a parallel view on the left side like "Old comment"' do click_parallel_diff_line(sample_commit.line_code, 'old') - page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do + page.within("#{diff_file_selector} form[id$='#{sample_commit.line_code}']") do fill_in "note[note]", with: "Old comment" find(".js-comment-button").trigger("click") end @@ -41,7 +41,7 @@ module SharedDiffNote step 'I leave a diff comment in a parallel view on the right side like "New comment"' do click_parallel_diff_line(sample_commit.line_code, 'new') - page.within("#{diff_file_selector} form[rel$='#{sample_commit.line_code}']") do + page.within("#{diff_file_selector} form[id$='#{sample_commit.line_code}']") do fill_in "note[note]", with: "New comment" find(".js-comment-button").trigger("click") end @@ -51,7 +51,7 @@ module SharedDiffNote page.within(diff_file_selector) do click_diff_line(sample_commit.line_code) - page.within("form[rel$='#{sample_commit.line_code}']") do + page.within("form[id$='#{sample_commit.line_code}']") do fill_in "note[note]", with: "Should fix it :smile:" find('.js-md-preview-button').click end @@ -62,7 +62,7 @@ module SharedDiffNote page.within(diff_file_selector) do click_diff_line(sample_commit.del_line_code) - page.within("form[rel$='#{sample_commit.del_line_code}']") do + page.within("form[id$='#{sample_commit.del_line_code}']") do fill_in "note[note]", with: "DRY this up" find('.js-md-preview-button').click end @@ -91,7 +91,7 @@ module SharedDiffNote page.within(diff_file_selector) do click_diff_line(sample_commit.line_code) - page.within("form[rel$='#{sample_commit.line_code}']") do + page.within("form[id$='#{sample_commit.line_code}']") do fill_in 'note[note]', with: ':smile:' click_button('Add Comment') end diff --git a/lib/gitlab/snippet_search_results.rb b/lib/gitlab/snippet_search_results.rb index 938219efdb2..38364a0b151 100644 --- a/lib/gitlab/snippet_search_results.rb +++ b/lib/gitlab/snippet_search_results.rb @@ -1,5 +1,7 @@ module Gitlab class SnippetSearchResults < SearchResults + include SnippetsHelper + attr_reader :limit_snippet_ids def initialize(limit_snippet_ids, query) @@ -47,85 +49,5 @@ module Gitlab def default_scope 'snippet_blobs' end - - # Get an array of line numbers surrounding a matching - # line, bounded by min/max. - # - # @returns Array of line numbers - def bounded_line_numbers(line, min, max) - lower = line - surrounding_lines > min ? line - surrounding_lines : min - upper = line + surrounding_lines < max ? line + surrounding_lines : max - (lower..upper).to_a - end - - # Returns a sorted set of lines to be included in a snippet preview. - # This ensures matching adjacent lines do not display duplicated - # surrounding code. - # - # @returns Array, unique and sorted. - def matching_lines(lined_content) - used_lines = [] - lined_content.each_with_index do |line, line_number| - used_lines.concat bounded_line_numbers( - line_number, - 0, - lined_content.size - ) if line.include?(query) - end - - used_lines.uniq.sort - end - - # 'Chunkify' entire snippet. Splits the snippet data into matching lines + - # surrounding_lines() worth of unmatching lines. - # - # @returns a hash with {snippet_object, snippet_chunks:{data,start_line}} - def chunk_snippet(snippet) - lined_content = snippet.content.split("\n") - used_lines = matching_lines(lined_content) - - snippet_chunk = [] - snippet_chunks = [] - snippet_start_line = 0 - last_line = -1 - - # Go through each used line, and add consecutive lines as a single chunk - # to the snippet chunk array. - used_lines.each do |line_number| - if last_line < 0 - # Start a new chunk. - snippet_start_line = line_number - snippet_chunk << lined_content[line_number] - elsif last_line == line_number - 1 - # Consecutive line, continue chunk. - snippet_chunk << lined_content[line_number] - else - # Non-consecutive line, add chunk to chunk array. - snippet_chunks << { - data: snippet_chunk.join("\n"), - start_line: snippet_start_line + 1 - } - - # Start a new chunk. - snippet_chunk = [lined_content[line_number]] - snippet_start_line = line_number - end - last_line = line_number - end - # Add final chunk to chunk array - snippet_chunks << { - data: snippet_chunk.join("\n"), - start_line: snippet_start_line + 1 - } - - # Return snippet with chunk array - { snippet_object: snippet, snippet_chunks: snippet_chunks } - end - - # Defines how many unmatching lines should be - # included around the matching lines in a snippet - def surrounding_lines - 3 - end end end diff --git a/scripts/ci/prepare_build.sh b/scripts/ci/prepare_build.sh deleted file mode 100755 index 864a683a1bd..00000000000 --- a/scripts/ci/prepare_build.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/bash -if [ -f /.dockerinit ]; then - export FLAGS=(--deployment --path /cache) - - apt-get update -qq - apt-get install -y -qq nodejs - - wget -q http://ftp.de.debian.org/debian/pool/main/p/phantomjs/phantomjs_1.9.0-1+b1_amd64.deb - dpkg -i phantomjs_1.9.0-1+b1_amd64.deb - - cp config/database.yml.mysql config/database.yml - sed -i "s/username:.*/username: root/g" config/database.yml - sed -i "s/password:.*/password:/g" config/database.yml - sed -i "s/# socket:.*/host: mysql/g" config/database.yml -else - export PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin - - cp config/database.yml.mysql config/database.yml - sed -i "s/username\:.*$/username\: runner/" config/database.yml - sed -i "s/password\:.*$/password\: 'password'/" config/database.yml - sed -i "s/gitlab_ci_test/gitlab_ci_test_$((RANDOM/5000))/" config/database.yml -fi diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index 119cc90fc1e..fb648d27621 100755 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -1,10 +1,16 @@ #!/bin/bash + if [ -f /.dockerinit ]; then - wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb - dpkg -i phantomjs_1.9.8-0jessie_amd64.deb + # Docker runners use `/cache` folder which is persisted every build + if [ ! -e /cache/phantomjs_1.9.8-0jessie_amd64.deb ]; then + wget -q https://gitlab.com/axil/phantomjs-debian/raw/master/phantomjs_1.9.8-0jessie_amd64.deb + mv phantomjs_1.9.8-0jessie_amd64.deb /cache + fi + dpkg -i /cache/phantomjs_1.9.8-0jessie_amd64.deb apt-get update -qq - apt-get install -y -qq libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client + apt-get -o dir::cache::archives="/cache/apt" install -y -qq \ + libicu-dev libkrb5-dev cmake nodejs postgresql-client mysql-client cp config/database.yml.mysql config/database.yml sed -i 's/username:.*/username: root/g' config/database.yml @@ -13,8 +19,8 @@ if [ -f /.dockerinit ]; then cp config/resque.yml.example config/resque.yml sed -i 's/localhost/redis/g' config/resque.yml - FLAGS=(--deployment --path /cache) - export FLAGS + + export FLAGS=(--path /cache) else export PATH=$HOME/bin:/usr/local/bin:/usr/bin:/bin cp config/database.yml.mysql config/database.yml diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb index f0fc6916c4d..1a360cd1ebc 100644 --- a/spec/features/notes_on_merge_requests_spec.rb +++ b/spec/features/notes_on_merge_requests_spec.rb @@ -167,7 +167,7 @@ describe 'Comments', feature: true do end it 'should be removed when canceled' do - page.within(".diff-file form[rel$='#{line_code}']") do + page.within(".diff-file form[id$='#{line_code}']") do find('.js-close-discussion-note-form').trigger('click') end |