diff options
author | Douwe Maan <douwe@selenight.nl> | 2016-03-21 23:22:21 +0100 |
---|---|---|
committer | Douwe Maan <douwe@selenight.nl> | 2016-03-21 23:22:21 +0100 |
commit | ae7b2ef62cdf61c990f914d776a6fdfc2bc49fa2 (patch) | |
tree | 6f0022bf04b1b566fa79b979cc9cc373cd0ebaa1 /app | |
parent | 8d544645f0ef114586212835cf011a3e268c9ec1 (diff) | |
parent | 0305dd98b32b5a989f2b84e0810cf5ddc14abd7f (diff) | |
download | gitlab-ce-ae7b2ef62cdf61c990f914d776a6fdfc2bc49fa2.tar.gz |
Merge branch 'master' into issue_12658
# Conflicts:
# app/models/issue.rb
# app/views/projects/_home_panel.html.haml
# app/views/shared/projects/_project.html.haml
# db/schema.rb
# spec/models/project_spec.rb
Diffstat (limited to 'app')
115 files changed, 530 insertions, 230 deletions
diff --git a/app/assets/javascripts/application.js.coffee b/app/assets/javascripts/application.js.coffee index d415bbd3476..01451830653 100644 --- a/app/assets/javascripts/application.js.coffee +++ b/app/assets/javascripts/application.js.coffee @@ -7,6 +7,7 @@ #= require jquery #= require jquery-ui/autocomplete #= require jquery-ui/datepicker +#= require jquery-ui/draggable #= require jquery-ui/effect-highlight #= require jquery-ui/sortable #= require jquery_ujs @@ -138,7 +139,7 @@ $ -> # Initialize tooltips $('body').tooltip( - selector: '.has_tooltip, [data-toggle="tooltip"]' + selector: '.has-tooltip, [data-toggle="tooltip"]' placement: (_, el) -> $el = $(el) $el.data('placement') || 'bottom' diff --git a/app/assets/javascripts/aside.js.coffee b/app/assets/javascripts/aside.js.coffee index 85473101944..66ab5054326 100644 --- a/app/assets/javascripts/aside.js.coffee +++ b/app/assets/javascripts/aside.js.coffee @@ -5,7 +5,6 @@ class @Aside e.preventDefault() btn = $(e.currentTarget) icon = btn.find('i') - console.log('1') if icon.hasClass('fa-angle-left') btn.parent().find('section').hide() diff --git a/app/assets/javascripts/awards_handler.coffee b/app/assets/javascripts/awards_handler.coffee index 03a44874161..47b080406d4 100644 --- a/app/assets/javascripts/awards_handler.coffee +++ b/app/assets/javascripts/awards_handler.coffee @@ -122,7 +122,7 @@ class @AwardsHandler nodes = [] nodes.push( - "<button class='btn award-control js-emoji-btn has_tooltip active' title='me'>", + "<button class='btn award-control js-emoji-btn has-tooltip active' title='me'>", "<div class='icon emoji-icon #{emojiCssClass}' data-emoji='#{emoji}'></div>", "<span class='award-control-text js-counter'>1</span>", "</button>" diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index c81e8bf760a..960585245d7 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -167,7 +167,11 @@ class GitLabDropdown hidden: => if @options.filterable - @dropdown.find(".dropdown-input-field").blur().val("") + @dropdown + .find(".dropdown-input-field") + .blur() + .val("") + .trigger("keyup") if @dropdown.find(".dropdown-toggle-page").length $('.dropdown-menu', @dropdown).removeClass PAGE_TWO_CLASS diff --git a/app/assets/javascripts/issuable_form.js.coffee b/app/assets/javascripts/issuable_form.js.coffee index 6c1699c178c..7a788f761b7 100644 --- a/app/assets/javascripts/issuable_form.js.coffee +++ b/app/assets/javascripts/issuable_form.js.coffee @@ -1,5 +1,7 @@ class @IssuableForm + issueMoveConfirmMsg: 'Are you sure you want to move this issue to another project?' wipRegex: /^\s*(\[WIP\]\s*|WIP:\s*|WIP\s+)+\s*/i + constructor: (@form) -> GitLab.GfmAutoComplete.setup() new UsersSelect() @@ -7,12 +9,13 @@ class @IssuableForm @titleField = @form.find("input[name*='[title]']") @descriptionField = @form.find("textarea[name*='[description]']") + @issueMoveField = @form.find("#move_to_project_id") return unless @titleField.length && @descriptionField.length @initAutosave() - @form.on "submit", @resetAutosave + @form.on "submit", @handleSubmit @form.on "click", ".btn-cancel", @resetAutosave @initWip() @@ -30,6 +33,12 @@ class @IssuableForm "description" ] + handleSubmit: => + if (parseInt(@issueMoveField?.val()) ? 0) > 0 + return false unless confirm(@issueMoveConfirmMsg) + + @resetAutosave() + resetAutosave: => @titleField.data("autosave").reset() @descriptionField.data("autosave").reset() diff --git a/app/assets/javascripts/merge_request_tabs.js.coffee b/app/assets/javascripts/merge_request_tabs.js.coffee index 8322b4c46ad..839e6ec2c08 100644 --- a/app/assets/javascripts/merge_request_tabs.js.coffee +++ b/app/assets/javascripts/merge_request_tabs.js.coffee @@ -3,6 +3,8 @@ # Handles persisting and restoring the current tab selection and lazily-loading # content on the MergeRequests#show page. # +#= require jquery.cookie +# # ### Example Markup # # <ul class="nav-links merge-request-tabs"> @@ -68,11 +70,15 @@ class @MergeRequestTabs if action == 'commits' @loadCommits($target.attr('href')) + @expandView() else if action == 'diffs' @loadDiff($target.attr('href')) @shrinkView() else if action == 'builds' @loadBuilds($target.attr('href')) + @expandView() + else + @expandView() @setCurrentAction(action) @@ -189,11 +195,24 @@ class @MergeRequestTabs $('.container-fluid').removeClass('container-limited') shrinkView: -> - $gutterIcon = $('.js-sidebar-toggle i') + $gutterIcon = $('.js-sidebar-toggle i:visible') # Wait until listeners are set setTimeout( -> - # Only when sidebar is collapsed + # Only when sidebar is expanded if $gutterIcon.is('.fa-angle-double-right') - $gutterIcon.closest('a').trigger('click',[true]) + $gutterIcon.closest('a').trigger('click', [true]) + , 0) + + # Expand the issuable sidebar unless the user explicitly collapsed it + expandView: -> + return if $.cookie('collapsed_gutter') == 'true' + + $gutterIcon = $('.js-sidebar-toggle i:visible') + + # Wait until listeners are set + setTimeout( -> + # Only when sidebar is collapsed + if $gutterIcon.is('.fa-angle-double-left') + $gutterIcon.closest('a').trigger('click', [true]) , 0) diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 82532216589..ff06c57f2b5 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -361,14 +361,12 @@ class @Notes showEditForm: (e) -> e.preventDefault() note = $(this).closest(".note") - note.find(".note-body > .note-text").hide() - note.find(".note-header").hide() + note.addClass "is-editting" form = note.find(".note-edit-form") isNewForm = form.is(':not(.gfm-form)') if isNewForm form.addClass('gfm-form') form.addClass('current-note-edit-form') - form.show() # Show the attachment delete link note.find(".js-note-attachment-delete").show() @@ -402,11 +400,9 @@ class @Notes cancelEdit: (e) -> e.preventDefault() note = $(this).closest(".note") - note.find(".note-body > .note-text").show() - note.find(".note-header").show() + note.removeClass "is-editting" note.find(".current-note-edit-form") .removeClass("current-note-edit-form") - .hide() ### Called in response to deleting a note of any kind. diff --git a/app/assets/javascripts/project.js.coffee b/app/assets/javascripts/project.js.coffee index 76bc4ff42a2..87d313ed67c 100644 --- a/app/assets/javascripts/project.js.coffee +++ b/app/assets/javascripts/project.js.coffee @@ -11,7 +11,6 @@ class @Project $(@).toggleClass('active') url = $("#project_clone").val() - console.log("url",url) # Update the input field $('#project_clone').val(url) diff --git a/app/assets/javascripts/stat_graph_contributors_util.js.coffee b/app/assets/javascripts/stat_graph_contributors_util.js.coffee index f5584bcfe4b..31617c88b4a 100644 --- a/app/assets/javascripts/stat_graph_contributors_util.js.coffee +++ b/app/assets/javascripts/stat_graph_contributors_util.js.coffee @@ -95,4 +95,4 @@ window.ContributorsStatGraphUtil = if date_range is null || date_range[0] <= new Date(date) <= date_range[1] true else - false
\ No newline at end of file + false diff --git a/app/assets/stylesheets/framework/avatar.scss b/app/assets/stylesheets/framework/avatar.scss index b7ffa3e6ffb..5aa425dab6c 100644 --- a/app/assets/stylesheets/framework/avatar.scss +++ b/app/assets/stylesheets/framework/avatar.scss @@ -16,7 +16,7 @@ } &.group-avatar, &.project-avatar, &.avatar-tile { - @include border-radius(0px); + @include border-radius(0); } &.s16 { width: 16px; height: 16px; margin-right: 6px; } diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index 2fac6abbac9..62b2af0dbf7 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -107,7 +107,7 @@ margin: 0; font-size: 23px; font-weight: normal; - margin: 16px 0 5px 0; + margin: 16px 0 5px; color: #4c4e54; font-size: 23px; line-height: 1.1; diff --git a/app/assets/stylesheets/framework/dropdowns.scss b/app/assets/stylesheets/framework/dropdowns.scss index a48b6c17fa0..d92cf6e6c44 100644 --- a/app/assets/stylesheets/framework/dropdowns.scss +++ b/app/assets/stylesheets/framework/dropdowns.scss @@ -75,7 +75,7 @@ width: 240px; margin-top: 2px; margin-bottom: 0; - padding: 10px 10px; + padding: 10px; font-size: 14px; font-weight: normal; background-color: $dropdown-bg; diff --git a/app/assets/stylesheets/framework/gitlab-theme.scss b/app/assets/stylesheets/framework/gitlab-theme.scss index 2a4cf4fc335..c83cf881596 100644 --- a/app/assets/stylesheets/framework/gitlab-theme.scss +++ b/app/assets/stylesheets/framework/gitlab-theme.scss @@ -117,4 +117,4 @@ body { &.ui_violet { @include gitlab-theme(#98c, $theme-violet, #436, #325); } -}
\ No newline at end of file +} diff --git a/app/assets/stylesheets/framework/layout.scss b/app/assets/stylesheets/framework/layout.scss index e901c78d02f..8bb047db2dd 100644 --- a/app/assets/stylesheets/framework/layout.scss +++ b/app/assets/stylesheets/framework/layout.scss @@ -16,7 +16,7 @@ body { } .container .content { - margin: 0 0; + margin: 0; } .navless-container { diff --git a/app/assets/stylesheets/framework/mixins.scss b/app/assets/stylesheets/framework/mixins.scss index 377bfa174bd..250d6309291 100644 --- a/app/assets/stylesheets/framework/mixins.scss +++ b/app/assets/stylesheets/framework/mixins.scss @@ -1,7 +1,7 @@ /** * Generic mixins */ - @mixin box-shadow($shadow) { +@mixin box-shadow($shadow) { -webkit-box-shadow: $shadow; -moz-box-shadow: $shadow; -ms-box-shadow: $shadow; diff --git a/app/assets/stylesheets/framework/selects.scss b/app/assets/stylesheets/framework/selects.scss index b3371229d5a..fa7944cdabe 100644 --- a/app/assets/stylesheets/framework/selects.scss +++ b/app/assets/stylesheets/framework/selects.scss @@ -41,7 +41,7 @@ } .select2-drop { - @include box-shadow(rgba(76, 86, 103, 0.247059) 0px 0px 1px 0px, rgba(31, 37, 50, 0.317647) 0px 2px 18px 0px); + @include box-shadow(rgba(76, 86, 103, 0.247059) 0 0 1px 0, rgba(31, 37, 50, 0.317647) 0 2px 18px 0); @include border-radius ($border-radius-default); border: none; } diff --git a/app/assets/stylesheets/framework/typography.scss b/app/assets/stylesheets/framework/typography.scss index 949295a1d0c..b1886fbe67b 100644 --- a/app/assets/stylesheets/framework/typography.scss +++ b/app/assets/stylesheets/framework/typography.scss @@ -39,8 +39,8 @@ h1 { font-size: 1.3em; font-weight: 600; - margin: 24px 0 12px 0; - padding: 0 0 10px 0; + margin: 24px 0 12px; + padding: 0 0 10px; border-bottom: 1px solid #e7e9ed; color: #313236; } @@ -48,27 +48,27 @@ h2 { font-size: 1.2em; font-weight: 600; - margin: 24px 0 12px 0; + margin: 24px 0 12px; color: #313236; } h3 { - margin: 24px 0 12px 0; + margin: 24px 0 12px; font-size: 1.1em; } h4 { - margin: 24px 0 12px 0; + margin: 24px 0 12px; font-size: 0.98em; } h5 { - margin: 24px 0 12px 0; + margin: 24px 0 12px; font-size: 0.95em; } h6 { - margin: 24px 0 12px 0; + margin: 24px 0 12px; font-size: 0.90em; } @@ -76,7 +76,7 @@ color: #7f8fa4; font-size: inherit; padding: 8px 21px; - margin: 12px 0 12px; + margin: 12px 0; border-left: 3px solid #e7e9ed; } @@ -88,13 +88,13 @@ p { color: #5c5d5e; - margin: 6px 0 0 0; + margin: 6px 0 0; } table { @extend .table; @extend .table-bordered; - margin: 12px 0 12px 0; + margin: 12px 0; color: #5c5d5e; th { background: #f8fafc; @@ -102,7 +102,7 @@ } pre { - margin: 12px 0 12px 0; + margin: 12px 0; font-size: 13px; line-height: 1.6em; overflow-x: auto; @@ -191,7 +191,7 @@ body { line-height: 1.3; font-size: 1.25em; font-weight: 600; - margin: 12px 7px 12px 7px; + margin: 12px 7px; } h1, h2, h3, h4, h5, h6 { diff --git a/app/assets/stylesheets/notify.scss b/app/assets/stylesheets/notify.scss new file mode 100644 index 00000000000..f1d42f80f56 --- /dev/null +++ b/app/assets/stylesheets/notify.scss @@ -0,0 +1,24 @@ +img { + max-width: 100%; + height: auto; +} +p.details { + font-style:italic; + color:#777 +} +.footer p { + font-size:small; + color:#777 +} +pre.commit-message { + white-space: pre-wrap; +} +.file-stats a { + text-decoration: none; +} +.file-stats .new-file { + color: #090; +} +.file-stats .deleted-file { + color: #B00; +} diff --git a/app/assets/stylesheets/pages/admin.scss b/app/assets/stylesheets/pages/admin.scss index a61161810a3..e05f14e7496 100644 --- a/app/assets/stylesheets/pages/admin.scss +++ b/app/assets/stylesheets/pages/admin.scss @@ -34,9 +34,9 @@ background: #fff } - .visibility-levels { - .controls { - margin-bottom: 9px; + .visibility-levels { + .controls { + margin-bottom: 9px; } i { diff --git a/app/assets/stylesheets/pages/diff.scss b/app/assets/stylesheets/pages/diff.scss index d5862a11aca..f1368d74b3b 100644 --- a/app/assets/stylesheets/pages/diff.scss +++ b/app/assets/stylesheets/pages/diff.scss @@ -132,7 +132,7 @@ } .image-info { font-size: 12px; - margin: 5px 0 0 0; + margin: 5px 0 0; color: grey; } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 2760af8a48a..5300bb52a1b 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -183,7 +183,7 @@ .block { width: $sidebar_collapsed_width - 1px; margin-left: -19px; - padding: 15px 0 0 0; + padding: 15px 0 0; border-bottom: none; overflow: hidden; } @@ -273,12 +273,12 @@ } .participants-list { - margin: -5px -5px; + margin: -5px; } .participants-author { display: inline-block; - padding: 5px 5px; + padding: 5px; .author_link { display: block; diff --git a/app/assets/stylesheets/pages/login.scss b/app/assets/stylesheets/pages/login.scss index bc41f7d306f..777bcbca5c3 100644 --- a/app/assets/stylesheets/pages/login.scss +++ b/app/assets/stylesheets/pages/login.scss @@ -45,7 +45,7 @@ .login-heading h3 { font-weight: 300; line-height: 1.5; - margin: 0 0 10px 0; + margin: 0 0 10px; } .login-footer { diff --git a/app/assets/stylesheets/pages/note_form.scss b/app/assets/stylesheets/pages/note_form.scss index 61783ec46aa..daf2651425f 100644 --- a/app/assets/stylesheets/pages/note_form.scss +++ b/app/assets/stylesheets/pages/note_form.scss @@ -26,7 +26,7 @@ display: none; } -.new_note, .edit_note { +.new_note, .note-edit-form { .note-form-actions { margin-top: $gl-padding; } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index d408853cc80..4bd2016bdcf 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -100,6 +100,18 @@ ul.notes { display: block; position: relative; + &.is-editting { + .note-header, + .note-text, + .edited-text { + display: none; + } + + .note-edit-form { + display: block; + } + } + .note-body { overflow: auto; diff --git a/app/assets/stylesheets/pages/profile.scss b/app/assets/stylesheets/pages/profile.scss index 260179074cf..e96dfc8e8d8 100644 --- a/app/assets/stylesheets/pages/profile.scss +++ b/app/assets/stylesheets/pages/profile.scss @@ -54,7 +54,7 @@ } .account-well { - padding: 10px 10px; + padding: 10px; background-color: $help-well-bg; border: 1px solid $help-well-border; border-radius: $border-radius-base; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 250de079ff5..c68bd673a67 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -37,7 +37,7 @@ .dropdown-menu { left: auto; width: auto; - right: 0px; + right: 0; max-width: 240px; } } @@ -311,7 +311,7 @@ pre.light-well { } .git-empty { - margin: 0 7px 0 7px; + margin: 0 7px; h5 { color: #5c5d5e; diff --git a/app/assets/stylesheets/pages/stat_graph.scss b/app/assets/stylesheets/pages/stat_graph.scss index b9be47e7700..85a0304196c 100644 --- a/app/assets/stylesheets/pages/stat_graph.scss +++ b/app/assets/stylesheets/pages/stat_graph.scss @@ -16,7 +16,7 @@ #contributors { .contributors-list { - margin: 0 0 10px 0; + margin: 0 0 10px; list-style: none; padding: 0; } diff --git a/app/assets/stylesheets/pages/xterm.scss b/app/assets/stylesheets/pages/xterm.scss index 8886c1dff56..3f28e402929 100644 --- a/app/assets/stylesheets/pages/xterm.scss +++ b/app/assets/stylesheets/pages/xterm.scss @@ -21,19 +21,19 @@ $l-white: #fff; .term-bold { - font-weight: bold; + font-weight: bold; } .term-italic { - font-style: italic; + font-style: italic; } .term-conceal { - visibility: hidden; + visibility: hidden; } .term-underline { - text-decoration: underline; + text-decoration: underline; } .term-cross { - text-decoration: line-through; + text-decoration: line-through; } .term-fg-black { diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 8e2a981be7c..a6db4690df0 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -5,12 +5,12 @@ class Admin::GroupsController < Admin::ApplicationController @groups = Group.all @groups = @groups.sort(@sort = params[:sort]) @groups = @groups.search(params[:name]) if params[:name].present? - @groups = @groups.page(params[:page]).per(PER_PAGE) + @groups = @groups.page(params[:page]) end def show - @members = @group.members.order("access_level DESC").page(params[:members_page]).per(PER_PAGE) - @projects = @group.projects.page(params[:projects_page]).per(PER_PAGE) + @members = @group.members.order("access_level DESC").page(params[:members_page]) + @projects = @group.projects.page(params[:projects_page]) end def new diff --git a/app/controllers/admin/labels_controller.rb b/app/controllers/admin/labels_controller.rb index d79ce2b10fe..d496f08a598 100644 --- a/app/controllers/admin/labels_controller.rb +++ b/app/controllers/admin/labels_controller.rb @@ -2,7 +2,7 @@ class Admin::LabelsController < Admin::ApplicationController before_action :set_label, only: [:show, :edit, :update, :destroy] def index - @labels = Label.templates.page(params[:page]).per(PER_PAGE) + @labels = Label.templates.page(params[:page]) end def show diff --git a/app/controllers/admin/projects_controller.rb b/app/controllers/admin/projects_controller.rb index 8b212225b2d..4089091d569 100644 --- a/app/controllers/admin/projects_controller.rb +++ b/app/controllers/admin/projects_controller.rb @@ -11,15 +11,15 @@ class Admin::ProjectsController < Admin::ApplicationController @projects = @projects.non_archived unless params[:with_archived].present? @projects = @projects.search(params[:name]) if params[:name].present? @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]).per(PER_PAGE) + @projects = @projects.includes(:namespace).order("namespaces.path, projects.name ASC").page(params[:page]) end def show if @group - @group_members = @group.members.order("access_level DESC").page(params[:group_members_page]).per(PER_PAGE) + @group_members = @group.members.order("access_level DESC").page(params[:group_members_page]) end - @project_members = @project.project_members.page(params[:project_members_page]).per(PER_PAGE) + @project_members = @project.project_members.page(params[:project_members_page]) end def transfer diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 3a0eb96a460..c81cb85dc1b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -6,8 +6,6 @@ class ApplicationController < ActionController::Base include GitlabRoutingHelper include PageLayoutHelper - PER_PAGE = 20 - before_action :authenticate_user_from_token! before_action :authenticate_user! before_action :validate_user_service_ticket! diff --git a/app/controllers/autocomplete_controller.rb b/app/controllers/autocomplete_controller.rb index 77c8dafc012..81ba58ce49c 100644 --- a/app/controllers/autocomplete_controller.rb +++ b/app/controllers/autocomplete_controller.rb @@ -7,7 +7,7 @@ class AutocompleteController < ApplicationController @users = @users.search(params[:search]) if params[:search].present? @users = @users.active @users = @users.reorder(:name) - @users = @users.page(params[:page]).per(PER_PAGE) + @users = @users.page(params[:page]) if params[:search].blank? # Include current user if available to filter by "Me" diff --git a/app/controllers/concerns/global_milestones.rb b/app/controllers/concerns/global_milestones.rb index 3e4c0e63601..54ea1e454fc 100644 --- a/app/controllers/concerns/global_milestones.rb +++ b/app/controllers/concerns/global_milestones.rb @@ -6,7 +6,7 @@ module GlobalMilestones @milestones = MilestonesFinder.new.execute(@projects, params) @milestones = GlobalMilestone.build_collection(@milestones) @milestones = @milestones.sort_by { |x| x.due_date.nil? ? epoch : x.due_date } - @milestones = Kaminari.paginate_array(@milestones).page(params[:page]).per(ApplicationController::PER_PAGE) + @milestones = Kaminari.paginate_array(@milestones).page(params[:page]) end def milestone diff --git a/app/controllers/concerns/issuable_actions.rb b/app/controllers/concerns/issuable_actions.rb new file mode 100644 index 00000000000..f40b62446e5 --- /dev/null +++ b/app/controllers/concerns/issuable_actions.rb @@ -0,0 +1,23 @@ +module IssuableActions + extend ActiveSupport::Concern + + included do + before_action :authorize_destroy_issuable!, only: :destroy + end + + def destroy + issuable.destroy + + name = issuable.class.name.titleize.downcase + flash[:notice] = "The #{name} was successfully deleted." + redirect_to polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable.class]) + end + + private + + def authorize_destroy_issuable! + unless current_user.can?(:"destroy_#{issuable.to_ability_name}", issuable) + return access_denied! + end + end +end diff --git a/app/controllers/concerns/issues_action.rb b/app/controllers/concerns/issues_action.rb index ef8e74a4641..4feabc32b1c 100644 --- a/app/controllers/concerns/issues_action.rb +++ b/app/controllers/concerns/issues_action.rb @@ -3,7 +3,7 @@ module IssuesAction def issues @issues = get_issues_collection.non_archived - @issues = @issues.page(params[:page]).per(ApplicationController::PER_PAGE) + @issues = @issues.page(params[:page]) @issues = @issues.preload(:author, :project) @label = @issuable_finder.labels.first diff --git a/app/controllers/concerns/merge_requests_action.rb b/app/controllers/concerns/merge_requests_action.rb index 9c49596bd0b..06a6b065e7e 100644 --- a/app/controllers/concerns/merge_requests_action.rb +++ b/app/controllers/concerns/merge_requests_action.rb @@ -3,7 +3,7 @@ module MergeRequestsAction def merge_requests @merge_requests = get_merge_requests_collection.non_archived - @merge_requests = @merge_requests.page(params[:page]).per(ApplicationController::PER_PAGE) + @merge_requests = @merge_requests.page(params[:page]) @merge_requests = @merge_requests.preload(:author, :target_project) @label = @issuable_finder.labels.first diff --git a/app/controllers/dashboard/groups_controller.rb b/app/controllers/dashboard/groups_controller.rb index 3bc94ff2187..71ba6153021 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]).per(PER_PAGE) + @group_members = current_user.group_members.page(params[:page]) end end diff --git a/app/controllers/dashboard/projects_controller.rb b/app/controllers/dashboard/projects_controller.rb index 0e8b63872ca..71acc244a91 100644 --- a/app/controllers/dashboard/projects_controller.rb +++ b/app/controllers/dashboard/projects_controller.rb @@ -8,7 +8,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController @projects = filter_projects(@projects) @projects = @projects.includes(:namespace) @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.page(params[:page]).per(PER_PAGE) + @projects = @projects.page(params[:page]) @last_push = current_user.recent_push @@ -32,7 +32,7 @@ class Dashboard::ProjectsController < Dashboard::ApplicationController @projects = filter_projects(@projects) @projects = @projects.includes(:namespace, :forked_from_project, :tags) @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.page(params[:page]).per(PER_PAGE) + @projects = @projects.page(params[:page]) @last_push = current_user.recent_push @groups = [] diff --git a/app/controllers/dashboard/snippets_controller.rb b/app/controllers/dashboard/snippets_controller.rb index b3594d82530..bcfdbe14be9 100644 --- a/app/controllers/dashboard/snippets_controller.rb +++ b/app/controllers/dashboard/snippets_controller.rb @@ -6,6 +6,6 @@ class Dashboard::SnippetsController < Dashboard::ApplicationController user: current_user, scope: params[:scope] ) - @snippets = @snippets.page(params[:page]).per(PER_PAGE) + @snippets = @snippets.page(params[:page]) end end diff --git a/app/controllers/dashboard/todos_controller.rb b/app/controllers/dashboard/todos_controller.rb index be488483b09..5abf97342c3 100644 --- a/app/controllers/dashboard/todos_controller.rb +++ b/app/controllers/dashboard/todos_controller.rb @@ -2,7 +2,7 @@ class Dashboard::TodosController < Dashboard::ApplicationController before_action :find_todos, only: [:index, :destroy, :destroy_all] def index - @todos = @todos.page(params[:page]).per(PER_PAGE) + @todos = @todos.page(params[:page]) end def destroy diff --git a/app/controllers/explore/groups_controller.rb b/app/controllers/explore/groups_controller.rb index 9575a87ee41..a962f9a0937 100644 --- a/app/controllers/explore/groups_controller.rb +++ b/app/controllers/explore/groups_controller.rb @@ -3,6 +3,6 @@ class Explore::GroupsController < Explore::ApplicationController @groups = GroupsFinder.new.execute(current_user) @groups = @groups.search(params[:search]) if params[:search].present? @groups = @groups.sort(@sort = params[:sort]) - @groups = @groups.page(params[:page]).per(PER_PAGE) + @groups = @groups.page(params[:page]) end end diff --git a/app/controllers/explore/projects_controller.rb b/app/controllers/explore/projects_controller.rb index 8271ca87436..88a0c18180b 100644 --- a/app/controllers/explore/projects_controller.rb +++ b/app/controllers/explore/projects_controller.rb @@ -8,7 +8,7 @@ class Explore::ProjectsController < Explore::ApplicationController @projects = @projects.where(visibility_level: params[:visibility_level]) if params[:visibility_level].present? @projects = filter_projects(@projects) @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.includes(:namespace).page(params[:page]).per(PER_PAGE) + @projects = @projects.includes(:namespace).page(params[:page]) respond_to do |format| format.html @@ -23,7 +23,7 @@ class Explore::ProjectsController < Explore::ApplicationController def trending @projects = TrendingProjectsFinder.new.execute(current_user) @projects = filter_projects(@projects) - @projects = @projects.page(params[:page]).per(PER_PAGE) + @projects = @projects.page(params[:page]) respond_to do |format| format.html @@ -39,7 +39,7 @@ class Explore::ProjectsController < Explore::ApplicationController @projects = ProjectsFinder.new.execute(current_user) @projects = filter_projects(@projects) @projects = @projects.reorder('star_count DESC') - @projects = @projects.page(params[:page]).per(PER_PAGE) + @projects = @projects.page(params[:page]) respond_to do |format| format.html diff --git a/app/controllers/explore/snippets_controller.rb b/app/controllers/explore/snippets_controller.rb index b70ac51d06e..28760c3f84b 100644 --- a/app/controllers/explore/snippets_controller.rb +++ b/app/controllers/explore/snippets_controller.rb @@ -1,6 +1,6 @@ class Explore::SnippetsController < Explore::ApplicationController def index @snippets = SnippetsFinder.new.execute(current_user, filter: :all) - @snippets = @snippets.page(params[:page]).per(PER_PAGE) + @snippets = @snippets.page(params[:page]) end end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index 87efb0a8970..c1adc999567 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -42,7 +42,7 @@ class GroupsController < Groups::ApplicationController @projects = @projects.includes(:namespace) @projects = filter_projects(@projects) @projects = @projects.sort(@sort = params[:sort]) - @projects = @projects.page(params[:page]).per(PER_PAGE) if params[:filter_projects].blank? + @projects = @projects.page(params[:page]) if params[:filter_projects].blank? @shared_projects = GroupProjectsFinder.new(group, only_shared: true).execute(current_user) diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 32fca6b838e..9042d8e5f0d 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -34,8 +34,7 @@ class ProfilesController < Profiles::ApplicationController def audit_log @events = AuditEvent.where(entity_type: "User", entity_id: current_user.id). order("created_at DESC"). - page(params[:page]). - per(PER_PAGE) + page(params[:page]) end def update_username diff --git a/app/controllers/projects/branches_controller.rb b/app/controllers/projects/branches_controller.rb index 43ea717cbd2..c0a53734921 100644 --- a/app/controllers/projects/branches_controller.rb +++ b/app/controllers/projects/branches_controller.rb @@ -8,7 +8,7 @@ class Projects::BranchesController < Projects::ApplicationController def index @sort = params[:sort] || 'name' @branches = @repository.branches_sorted_by(@sort) - @branches = Kaminari.paginate_array(@branches).page(params[:page]).per(PER_PAGE) + @branches = Kaminari.paginate_array(@branches).page(params[:page]) @max_commits = @branches.reduce(0) do |memo, branch| diverging_commit_counts = repository.diverging_commit_counts(branch) diff --git a/app/controllers/projects/forks_controller.rb b/app/controllers/projects/forks_controller.rb index a1b8632df98..ade01c706a7 100644 --- a/app/controllers/projects/forks_controller.rb +++ b/app/controllers/projects/forks_controller.rb @@ -15,7 +15,7 @@ class Projects::ForksController < Projects::ApplicationController @sort = params[:sort] || 'id_desc' @forks = @forks.search(params[:filter_projects]) if params[:filter_projects].present? - @forks = @forks.order_by(@sort).page(params[:page]).per(PER_PAGE) + @forks = @forks.order_by(@sort).page(params[:page]) respond_to do |format| format.html diff --git a/app/controllers/projects/issues_controller.rb b/app/controllers/projects/issues_controller.rb index 6603f28a082..877b39c9b1b 100644 --- a/app/controllers/projects/issues_controller.rb +++ b/app/controllers/projects/issues_controller.rb @@ -1,5 +1,6 @@ class Projects::IssuesController < Projects::ApplicationController include ToggleSubscriptionAction + include IssuableActions before_action :module_enabled before_action :issue, only: [:edit, :update, :show] @@ -33,7 +34,7 @@ class Projects::IssuesController < Projects::ApplicationController end end - @issues = @issues.page(params[:page]).per(PER_PAGE) + @issues = @issues.page(params[:page]) @label = @project.labels.find_by(title: params[:label_name]) respond_to do |format| @@ -90,6 +91,12 @@ class Projects::IssuesController < Projects::ApplicationController def update @issue = Issues::UpdateService.new(project, current_user, issue_params).execute(issue) + if params[:move_to_project_id].to_i > 0 + new_project = Project.find(params[:move_to_project_id]) + move_service = Issues::MoveService.new(project, current_user) + @issue = move_service.execute(@issue, new_project) + end + respond_to do |format| format.js format.html do @@ -127,6 +134,7 @@ class Projects::IssuesController < Projects::ApplicationController end end alias_method :subscribable_resource, :issue + alias_method :issuable, :issue def authorize_read_issue! return render_404 unless can?(current_user, :read_issue, @issue) diff --git a/app/controllers/projects/labels_controller.rb b/app/controllers/projects/labels_controller.rb index 5f471d405f5..ff771ea6d9c 100644 --- a/app/controllers/projects/labels_controller.rb +++ b/app/controllers/projects/labels_controller.rb @@ -11,7 +11,7 @@ class Projects::LabelsController < Projects::ApplicationController respond_to :js, :html def index - @labels = @project.labels.page(params[:page]).per(PER_PAGE) + @labels = @project.labels.page(params[:page]) respond_to do |format| format.html diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index 7248ede1699..b830d777752 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -1,6 +1,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController include ToggleSubscriptionAction include DiffHelper + include IssuableActions before_action :module_enabled before_action :merge_request, only: [ @@ -34,7 +35,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController end end - @merge_requests = @merge_requests.page(params[:page]).per(PER_PAGE) + @merge_requests = @merge_requests.page(params[:page]) @merge_requests = @merge_requests.preload(:target_project) @label = @project.labels.find_by(title: params[:label_name]) @@ -255,6 +256,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController @merge_request ||= @project.merge_requests.find_by!(iid: params[:id]) end alias_method :subscribable_resource, :merge_request + alias_method :issuable, :merge_request def closes_issues @closes_issues ||= @merge_request.closes_issues diff --git a/app/controllers/projects/milestones_controller.rb b/app/controllers/projects/milestones_controller.rb index 0998b191c07..b2e974eff17 100644 --- a/app/controllers/projects/milestones_controller.rb +++ b/app/controllers/projects/milestones_controller.rb @@ -22,7 +22,7 @@ class Projects::MilestonesController < Projects::ApplicationController respond_to do |format| format.html do - @milestones = @milestones.page(params[:page]).per(PER_PAGE) + @milestones = @milestones.page(params[:page]) end format.json do render json: @milestones diff --git a/app/controllers/projects/snippets_controller.rb b/app/controllers/projects/snippets_controller.rb index 92b0caa2efb..b578b419a46 100644 --- a/app/controllers/projects/snippets_controller.rb +++ b/app/controllers/projects/snippets_controller.rb @@ -21,7 +21,7 @@ class Projects::SnippetsController < Projects::ApplicationController filter: :by_project, project: @project }) - @snippets = @snippets.page(params[:page]).per(PER_PAGE) + @snippets = @snippets.page(params[:page]) end def new diff --git a/app/controllers/projects/tags_controller.rb b/app/controllers/projects/tags_controller.rb index e580487a2c6..46b242aa5ff 100644 --- a/app/controllers/projects/tags_controller.rb +++ b/app/controllers/projects/tags_controller.rb @@ -7,7 +7,7 @@ class Projects::TagsController < Projects::ApplicationController def index sorted = VersionSorter.rsort(@repository.tag_names) - @tags = Kaminari.paginate_array(sorted).page(params[:page]).per(PER_PAGE) + @tags = Kaminari.paginate_array(sorted).page(params[:page]) @releases = project.releases.where(tag: @tags) end diff --git a/app/controllers/projects/wikis_controller.rb b/app/controllers/projects/wikis_controller.rb index 88fccfed509..02ceb8f4334 100644 --- a/app/controllers/projects/wikis_controller.rb +++ b/app/controllers/projects/wikis_controller.rb @@ -7,7 +7,7 @@ class Projects::WikisController < Projects::ApplicationController before_action :load_project_wiki def pages - @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]).per(PER_PAGE) + @wiki_pages = Kaminari.paginate_array(@project_wiki.pages).page(params[:page]) end def show diff --git a/app/controllers/snippets_controller.rb b/app/controllers/snippets_controller.rb index c72df73af46..2daceed039b 100644 --- a/app/controllers/snippets_controller.rb +++ b/app/controllers/snippets_controller.rb @@ -25,7 +25,7 @@ class SnippetsController < ApplicationController filter: :by_user, user: @user, scope: params[:scope] }). - page(params[:page]).per(PER_PAGE) + page(params[:page]) render 'index' else diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 481d00d6aae..8e7956da48f 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -100,7 +100,7 @@ class UsersController < ApplicationController def load_projects @projects = PersonalProjectsFinder.new(@user).execute(current_user) - .page(params[:page]).per(PER_PAGE) + .page(params[:page]) end def load_contributed_projects diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 0f77b3b299a..820d69c230b 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -27,7 +27,7 @@ module BlobHelper link_opts) if !on_top_of_branch?(project, ref) - button_tag "Edit", class: "btn btn-default disabled has_tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' } + button_tag "Edit", class: "btn btn-default disabled has-tooltip", title: "You can only edit files when you are on a branch", data: { container: 'body' } elsif can_edit_blob?(blob, project, ref) link_to "Edit", edit_path, class: 'btn' elsif can?(current_user, :fork_project, project) @@ -50,9 +50,9 @@ module BlobHelper return unless blob if !on_top_of_branch?(project, ref) - button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' } + button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "You can only #{action} files when you are on a branch", data: { container: 'body' } elsif blob.lfs_pointer? - button_tag label, class: "btn btn-#{btn_class} disabled has_tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' } + button_tag label, class: "btn btn-#{btn_class} disabled has-tooltip", title: "It is not possible to #{action} files that are stored in LFS using the web interface", data: { container: 'body' } elsif can_edit_blob?(blob, project, ref) button_tag label, class: "btn btn-#{btn_class}", 'data-target' => "#modal-#{modal_type}-blob", 'data-toggle' => 'modal' elsif can?(current_user, :fork_project, project) diff --git a/app/helpers/button_helper.rb b/app/helpers/button_helper.rb index d6c05843743..a9047ede8c5 100644 --- a/app/helpers/button_helper.rb +++ b/app/helpers/button_helper.rb @@ -23,36 +23,34 @@ module ButtonHelper end def http_clone_button(project) - klass = 'btn js-protocol-switch' - klass << ' active' if default_clone_protocol == 'http' - klass << ' has_tooltip' if current_user.try(:require_password?) + klass = 'http-selector' + klass << ' has-tooltip' if current_user.try(:require_password?) protocol = gitlab_config.protocol.upcase - content_tag :button, protocol, + content_tag :a, protocol, class: klass, + href: @project.http_url_to_repo, data: { - clone: project.http_url_to_repo, + html: true, + placement: 'right', container: 'body', - html: 'true', title: "Set a password on your account<br>to pull or push via #{protocol}" - }, - type: :button + } end def ssh_clone_button(project) - klass = 'btn js-protocol-switch' - klass << ' active' if default_clone_protocol == 'ssh' - klass << ' has_tooltip' if current_user.try(:require_ssh_key?) + klass = 'ssh-selector' + klass << ' has-tooltip' if current_user.try(:require_ssh_key?) - content_tag :button, 'SSH', + content_tag :a, 'SSH', class: klass, + href: project.ssh_url_to_repo, data: { - clone: project.ssh_url_to_repo, + html: true, + placement: 'right', container: 'body', - html: 'true', title: 'Add an SSH key to your profile<br>to pull or push via SSH.' - }, - type: :button + } end end diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index f994c9e6170..bde0799f3de 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -182,7 +182,7 @@ module CommitsHelper end options = { - class: "commit-#{options[:source]}-link has_tooltip", + class: "commit-#{options[:source]}-link has-tooltip", data: { 'original-title'.to_sym => sanitize(source_email) } } diff --git a/app/helpers/issues_helper.rb b/app/helpers/issues_helper.rb index e00d3204027..24b90fef4fe 100644 --- a/app/helpers/issues_helper.rb +++ b/app/helpers/issues_helper.rb @@ -57,6 +57,19 @@ module IssuesHelper options_from_collection_for_select(milestones, 'id', 'title', object.milestone_id) end + def project_options(issuable, current_user, ability: :read_project) + projects = current_user.authorized_projects + projects = projects.select do |project| + current_user.can?(ability, project) + end + + no_project = OpenStruct.new(id: 0, name_with_namespace: 'No project') + projects.unshift(no_project) + projects.delete(issuable.project) + + options_from_collection_for_select(projects, :id, :name_with_namespace) + end + def status_box_class(item) if item.respond_to?(:expired?) && item.expired? 'status-box-expired' diff --git a/app/helpers/labels_helper.rb b/app/helpers/labels_helper.rb index ed37176aa6b..e0a8552dfa7 100644 --- a/app/helpers/labels_helper.rb +++ b/app/helpers/labels_helper.rb @@ -56,7 +56,7 @@ module LabelsHelper # Intentionally not using content_tag here so that this method can be called # by LabelReferenceFilter - span = %(<span class="label color-label #{"has_tooltip" if tooltip}" ) + + span = %(<span class="label color-label #{"has-tooltip" if tooltip}" ) + %(style="background-color: #{label_color}; color: #{text_color}" ) + %(title="#{escape_once(label.description)}" data-container="body">) + %(#{escape_once(label.name)}#{label_suffix}</span>) diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index 5473419ef24..4e4c6e301d5 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -52,7 +52,7 @@ module ProjectsHelper link_to(author_html, user_path(author), class: "author_link #{"#{opts[:mobile_classes]}" if opts[:mobile_classes]}").html_safe else title = opts[:title].sub(":name", sanitize(author.name)) - link_to(author_html, user_path(author), class: "author_link has_tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe + link_to(author_html, user_path(author), class: "author_link has-tooltip", data: { 'original-title'.to_sym => title, container: 'body' } ).html_safe end end @@ -209,7 +209,7 @@ module ProjectsHelper def default_clone_protocol if !current_user || current_user.require_ssh_key? - "http" + gitlab_config.protocol else "ssh" end diff --git a/app/mailers/emails/issues.rb b/app/mailers/emails/issues.rb index 5f9adb32e00..6f54c42146c 100644 --- a/app/mailers/emails/issues.rb +++ b/app/mailers/emails/issues.rb @@ -36,6 +36,14 @@ module Emails mail_answer_thread(@issue, issue_thread_options(updated_by_user_id, recipient_id)) end + def issue_moved_email(recipient, issue, new_issue, updated_by_user) + setup_issue_mail(issue.id, recipient.id) + + @new_issue = new_issue + @new_project = new_issue.project + mail_answer_thread(issue, issue_thread_options(updated_by_user.id, recipient.id)) + end + private def setup_issue_mail(issue_id, recipient_id) diff --git a/app/models/ability.rb b/app/models/ability.rb index de9253fcdd8..42b978e04d5 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -234,7 +234,9 @@ class Ability :rename_project, :remove_project, :archive_project, - :remove_fork_project + :remove_fork_project, + :destroy_merge_request, + :destroy_issue ] end diff --git a/app/models/concerns/internal_id.rb b/app/models/concerns/internal_id.rb index 821ed54fb98..51288094ef1 100644 --- a/app/models/concerns/internal_id.rb +++ b/app/models/concerns/internal_id.rb @@ -7,7 +7,10 @@ module InternalId end def set_iid - max_iid = project.send(self.class.name.tableize).maximum(:iid) + records = project.send(self.class.name.tableize) + records = records.with_deleted if self.paranoid? + max_iid = records.maximum(:iid) + self.iid = max_iid.to_i + 1 end diff --git a/app/models/concerns/issuable.rb b/app/models/concerns/issuable.rb index 86ab84615ba..476e1ce7af0 100644 --- a/app/models/concerns/issuable.rb +++ b/app/models/concerns/issuable.rb @@ -58,6 +58,8 @@ module Issuable attr_mentionable :description, cache: true participant :author, :assignee, :notes_with_associations strip_attributes :title + + acts_as_paranoid end module ClassMethods @@ -209,4 +211,13 @@ module Issuable Taskable.get_updated_tasks(old_content: previous_changes['description'].first, new_content: description) end + + ## + # Method that checks if issuable can be moved to another project. + # + # Should be overridden if issuable can be moved. + # + def can_move?(*) + false + end end diff --git a/app/models/issue.rb b/app/models/issue.rb index 60250322b04..f32db59ac9f 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -16,6 +16,7 @@ # state :string(255) # iid :integer # updated_by_id :integer +# moved_to_id :integer # require 'carrierwave/orm/activerecord' @@ -31,6 +32,8 @@ class Issue < ActiveRecord::Base ActsAsTaggableOn.strict_case_match = true belongs_to :project + belongs_to :moved_to, class_name: 'Issue' + validates :project, presence: true scope :cared, ->(user) { where(assignee_id: user) } @@ -102,9 +105,9 @@ class Issue < ActiveRecord::Base end def related_branches - return [] if self.project.empty_repo? - - self.project.repository.branch_names.select { |branch| branch.end_with?("-#{iid}") } + project.repository.branch_names.select do |branch| + branch.end_with?("-#{iid}") + end end # Reset issue events cache @@ -134,6 +137,18 @@ class Issue < ActiveRecord::Base end.uniq.select { |mr| mr.open? && mr.closes_issue?(self) } end + def moved? + !moved_to.nil? + end + + def can_move?(user, to_project = nil) + if to_project + return false unless user.can?(:admin_issue, to_project) + end + + !moved? && user.can?(:admin_issue, self.project) + end + def to_branch_name "#{title.parameterize}-#{iid}" end diff --git a/app/models/project.rb b/app/models/project.rb index 4d23cc8de40..9c8246e8ac0 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -431,6 +431,7 @@ class Project < ActiveRecord::Base def safe_import_url result = URI.parse(self.import_url) result.password = '*****' unless result.password.nil? + result.user = '*****' unless result.user.nil? || result.user == "git" #tokens or other data may be saved as user result.to_s rescue self.import_url @@ -887,6 +888,7 @@ class Project < ActiveRecord::Base # Forked import is handled asynchronously unless forked? if gitlab_shell.add_repository(path_with_namespace) + repository.after_create true else errors.add(:base, 'Failed to create repository via gitlab-shell') diff --git a/app/models/project_wiki.rb b/app/models/project_wiki.rb index 59b1b86d1fb..7c1a61bb0bf 100644 --- a/app/models/project_wiki.rb +++ b/app/models/project_wiki.rb @@ -123,23 +123,27 @@ class ProjectWiki end def repository - Repository.new(path_with_namespace, @project) + @repository ||= Repository.new(path_with_namespace, @project) end def default_branch wiki.class.default_ref end - private - def create_repo! if init_repo(path_with_namespace) - Gollum::Wiki.new(path_to_repo) + wiki = Gollum::Wiki.new(path_to_repo) else raise CouldNotCreateWikiError end + + repository.after_create + + wiki end + private + def init_repo(path_with_namespace) gitlab_shell.add_repository(path_with_namespace) end diff --git a/app/models/repository.rb b/app/models/repository.rb index 25d24493f6e..13154eb4205 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -42,12 +42,15 @@ class Repository end def exists? - return false unless raw_repository + return @exists unless @exists.nil? - raw_repository.rugged - true - rescue Gitlab::Git::Repository::NoRepository - false + @exists = cache.fetch(:exists?) do + begin + raw_repository && raw_repository.rugged ? true : false + rescue Gitlab::Git::Repository::NoRepository + false + end + end end def empty? @@ -320,12 +323,23 @@ class Repository @avatar = nil end + def expire_exists_cache + cache.expire(:exists?) + @exists = nil + end + + # Runs code after a repository has been created. + def after_create + expire_exists_cache + end + # Runs code just before a repository is deleted. def before_delete expire_cache if exists? expire_root_ref_cache expire_emptiness_caches + expire_exists_cache end # Runs code just before the HEAD of a repository is changed. @@ -351,6 +365,7 @@ class Repository # Runs code after a repository has been forked/imported. def after_import expire_emptiness_caches + expire_exists_cache end # Runs code after a new commit has been pushed. diff --git a/app/models/user.rb b/app/models/user.rb index c011af03591..9c315cfe966 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -435,7 +435,7 @@ class User < ActiveRecord::Base Group.where("namespaces.id IN (#{union.to_sql})") end - # Returns the groups a user is authorized to access. + # Returns projects user is authorized to access. def authorized_projects Project.where("projects.id IN (#{projects_union.to_sql})") end diff --git a/app/services/git_push_service.rb b/app/services/git_push_service.rb index 14e2a2c0699..c007d648dd6 100644 --- a/app/services/git_push_service.rb +++ b/app/services/git_push_service.rb @@ -120,7 +120,7 @@ class GitPushService < BaseService closed_issues = commit.closes_issues(current_user) closed_issues.each do |issue| if can?(current_user, :update_issue, issue) - Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit) + Issues::CloseService.new(project, authors[commit], {}).execute(issue, commit: commit) end end end diff --git a/app/services/issues/close_service.rb b/app/services/issues/close_service.rb index 78254b49af3..859c934ea3b 100644 --- a/app/services/issues/close_service.rb +++ b/app/services/issues/close_service.rb @@ -1,6 +1,6 @@ module Issues class CloseService < Issues::BaseService - def execute(issue, commit = nil) + def execute(issue, commit: nil, notifications: true, system_note: true) if project.jira_tracker? && project.jira_service.active project.jira_service.execute(commit, issue) todo_service.close_issue(issue, current_user) @@ -9,8 +9,8 @@ module Issues if project.default_issues_tracker? && issue.close event_service.close_issue(issue, current_user) - create_note(issue, commit) - notification_service.close_issue(issue, current_user) + create_note(issue, commit) if system_note + notification_service.close_issue(issue, current_user) if notifications todo_service.close_issue(issue, current_user) execute_hooks(issue, 'close') end diff --git a/app/services/issues/create_service.rb b/app/services/issues/create_service.rb index 10787e8873c..e63e1af8766 100644 --- a/app/services/issues/create_service.rb +++ b/app/services/issues/create_service.rb @@ -4,7 +4,7 @@ module Issues filter_params label_params = params[:label_ids] issue = project.issues.new(params.except(:label_ids)) - issue.author = current_user + issue.author = params[:author] || current_user if issue.save issue.update_attributes(label_ids: label_params) diff --git a/app/services/issues/move_service.rb b/app/services/issues/move_service.rb new file mode 100644 index 00000000000..3cfbafe1576 --- /dev/null +++ b/app/services/issues/move_service.rb @@ -0,0 +1,94 @@ +module Issues + class MoveService < Issues::BaseService + class MoveError < StandardError; end + + def execute(issue, new_project) + @old_issue = issue + @old_project = @project + @new_project = new_project + + unless issue.can_move?(current_user, new_project) + raise MoveError, 'Cannot move issue due to insufficient permissions!' + end + + if @project == new_project + raise MoveError, 'Cannot move issue to project it originates from!' + end + + # Using transaction because of a high resources footprint + # on rewriting notes (unfolding references) + # + ActiveRecord::Base.transaction do + # New issue tasks + # + @new_issue = create_new_issue + + rewrite_notes + add_note_moved_from + + # Old issue tasks + # + add_note_moved_to + close_issue + mark_as_moved + end + + notify_participants + + @new_issue + end + + private + + def create_new_issue + new_params = { id: nil, iid: nil, label_ids: [], milestone: nil, + project: @new_project, author: @old_issue.author, + description: unfold_references(@old_issue.description) } + + new_params = @old_issue.serializable_hash.merge(new_params) + CreateService.new(@new_project, @current_user, new_params).execute + end + + def rewrite_notes + @old_issue.notes.find_each do |note| + new_note = note.dup + new_params = { project: @new_project, noteable: @new_issue, + note: unfold_references(new_note.note), + created_at: note.created_at } + + new_note.update(new_params) + end + end + + def close_issue + close_service = CloseService.new(@old_project, @current_user) + close_service.execute(@old_issue, notifications: false, system_note: false) + end + + def add_note_moved_from + SystemNoteService.noteable_moved(@new_issue, @new_project, + @old_issue, @current_user, + direction: :from) + end + + def add_note_moved_to + SystemNoteService.noteable_moved(@old_issue, @old_project, + @new_issue, @current_user, + direction: :to) + end + + def unfold_references(content) + rewriter = Gitlab::Gfm::ReferenceRewriter.new(content, @old_project, + @current_user) + rewriter.rewrite(@new_project) + end + + def notify_participants + notification_service.issue_moved(@old_issue, @new_issue, @current_user) + end + + def mark_as_moved + @old_issue.update(moved_to: @new_issue) + end + end +end diff --git a/app/services/merge_requests/post_merge_service.rb b/app/services/merge_requests/post_merge_service.rb index ebb67c7db65..064910f81f7 100644 --- a/app/services/merge_requests/post_merge_service.rb +++ b/app/services/merge_requests/post_merge_service.rb @@ -22,7 +22,7 @@ module MergeRequests closed_issues = merge_request.closes_issues(current_user) closed_issues.each do |issue| if can?(current_user, :update_issue, issue) - Issues::CloseService.new(project, current_user, {}).execute(issue, merge_request) + Issues::CloseService.new(project, current_user, {}).execute(issue, commit: merge_request) end end end diff --git a/app/services/notification_service.rb b/app/services/notification_service.rb index 19a6779dea9..3bdf00a8291 100644 --- a/app/services/notification_service.rb +++ b/app/services/notification_service.rb @@ -236,6 +236,16 @@ class NotificationService end end + def issue_moved(issue, new_issue, current_user) + recipients = build_recipients(issue, issue.project, current_user) + + recipients.map do |recipient| + email = mailer.issue_moved_email(recipient, issue, new_issue, current_user) + email.deliver_later + email + end + end + protected # Get project users with WATCH notification level diff --git a/app/services/system_note_service.rb b/app/services/system_note_service.rb index c644cd0b951..e022a046c48 100644 --- a/app/services/system_note_service.rb +++ b/app/services/system_note_service.rb @@ -411,4 +411,26 @@ class SystemNoteService body = "Marked the task **#{new_task.source}** as #{status_label}" create_note(noteable: noteable, project: project, author: author, note: body) end + + # Called when noteable has been moved to another project + # + # direction - symbol, :to or :from + # noteable - Noteable object + # noteable_ref - Referenced noteable + # author - User performing the move + # + # Example Note text: + # + # "Moved to some_namespace/project_new#11" + # + # Returns the created Note object + def self.noteable_moved(noteable, project, noteable_ref, author, direction:) + unless [:to, :from].include?(direction) + raise ArgumentError, "Invalid direction `#{direction}`" + end + + cross_reference = noteable_ref.to_reference(project) + body = "Moved #{direction} #{cross_reference}" + create_note(noteable: noteable, project: project, author: author, note: body) + end end diff --git a/app/views/admin/applications/_delete_form.html.haml b/app/views/admin/applications/_delete_form.html.haml index 3147cbd659f..042971e1eed 100644 --- a/app/views/admin/applications/_delete_form.html.haml +++ b/app/views/admin/applications/_delete_form.html.haml @@ -1,4 +1,4 @@ - submit_btn_css ||= 'btn btn-link btn-remove btn-sm' = form_tag admin_application_path(application) do %input{:name => "_method", :type => "hidden", :value => "delete"}/ - = submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css
\ No newline at end of file + = submit_tag 'Destroy', onclick: "return confirm('Are you sure?')", class: submit_btn_css diff --git a/app/views/devise/sessions/_new_crowd.html.haml b/app/views/devise/sessions/_new_crowd.html.haml index 4974bb7f7fb..8e81671b7e7 100644 --- a/app/views/devise/sessions/_new_crowd.html.haml +++ b/app/views/devise/sessions/_new_crowd.html.haml @@ -6,4 +6,4 @@ %label{for: "remember_me"} = check_box_tag :remember_me, '1', false, id: 'remember_me' %span Remember me - = button_tag "Sign in", class: "btn-save btn"
\ No newline at end of file + = button_tag "Sign in", class: "btn-save btn" diff --git a/app/views/doorkeeper/applications/new.html.haml b/app/views/doorkeeper/applications/new.html.haml index fd32a468b45..d3692d1f759 100644 --- a/app/views/doorkeeper/applications/new.html.haml +++ b/app/views/doorkeeper/applications/new.html.haml @@ -4,4 +4,4 @@ %hr -= render 'form', application: @application
\ No newline at end of file += render 'form', application: @application diff --git a/app/views/doorkeeper/authorizations/error.html.haml b/app/views/doorkeeper/authorizations/error.html.haml index 7561ec85ed9..a4c607cea60 100644 --- a/app/views/doorkeeper/authorizations/error.html.haml +++ b/app/views/doorkeeper/authorizations/error.html.haml @@ -1,3 +1,3 @@ %h3.page-title An error has occurred %main{:role => "main"} - %pre= @pre_auth.error_response.body[:error_description]
\ No newline at end of file + %pre= @pre_auth.error_response.body[:error_description] diff --git a/app/views/doorkeeper/authorizations/show.html.haml b/app/views/doorkeeper/authorizations/show.html.haml index 9a402007194..01f9e46f142 100644 --- a/app/views/doorkeeper/authorizations/show.html.haml +++ b/app/views/doorkeeper/authorizations/show.html.haml @@ -1,3 +1,3 @@ %h3.page-title Authorization code: %main{:role => "main"} - %code#authorization_code= params[:code]
\ No newline at end of file + %code#authorization_code= params[:code] diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index db0cf393922..4a0069f18f8 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -1,7 +1,7 @@ %ul.nav.nav-sidebar = nav_link(path: ['root#index', 'projects#trending', 'projects#starred', 'dashboard/projects#index'], html_options: {class: 'home'}) do = link_to dashboard_projects_path, title: 'Projects' do - = icon('home fw') + = icon('bookmark fw') %span Projects = nav_link(controller: :todos) do diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml index 48039ca2918..f08c5edf99c 100644 --- a/app/views/layouts/nav/_explore.html.haml +++ b/app/views/layouts/nav/_explore.html.haml @@ -1,7 +1,7 @@ %ul.nav.nav-sidebar = nav_link(path: ['dashboard#show', 'root#show', 'projects#trending', 'projects#starred', 'projects#index'], html_options: {class: 'home'}) do = link_to explore_root_path, title: 'Projects' do - = icon('home fw') + = icon('bookmark fw') %span Projects = nav_link(controller: :groups) do diff --git a/app/views/layouts/notify.html.haml b/app/views/layouts/notify.html.haml index 37b4d562966..2997f59d946 100644 --- a/app/views/layouts/notify.html.haml +++ b/app/views/layouts/notify.html.haml @@ -1,33 +1,9 @@ %html{lang: "en"} %head %meta{content: "text/html; charset=utf-8", "http-equiv" => "Content-Type"} - %title - GitLab - :css - img { - max-width: 100%; - height: auto; - } - p.details { - font-style:italic; - color:#777 - } - .footer p { - font-size:small; - color:#777 - } - pre.commit-message { - white-space: pre-wrap; - } - .file-stats a { - text-decoration: none; - } - .file-stats .new-file { - color: #090; - } - .file-stats .deleted-file { - color: #B00; - } + %title + GitLab + = stylesheet_link_tag 'notify' %body %div.content = yield diff --git a/app/views/notify/issue_moved_email.html.haml b/app/views/notify/issue_moved_email.html.haml new file mode 100644 index 00000000000..40f7d61fe19 --- /dev/null +++ b/app/views/notify/issue_moved_email.html.haml @@ -0,0 +1,6 @@ +%p + Issue was moved to another project. +%p + New issue: + = link_to namespace_project_issue_url(@new_project.namespace, @new_project, @new_issue) do + = @new_issue.title diff --git a/app/views/notify/issue_moved_email.text.erb b/app/views/notify/issue_moved_email.text.erb new file mode 100644 index 00000000000..b3bd43c2055 --- /dev/null +++ b/app/views/notify/issue_moved_email.text.erb @@ -0,0 +1,4 @@ +Issue was moved to another project. + +New issue location: +<%= namespace_project_issue_url(@new_project.namespace, @new_project, @new_issue) %> diff --git a/app/views/profiles/keys/_key.html.haml b/app/views/profiles/keys/_key.html.haml index 25e9e8ff008..4dbaa662b66 100644 --- a/app/views/profiles/keys/_key.html.haml +++ b/app/views/profiles/keys/_key.html.haml @@ -8,7 +8,7 @@ = key.fingerprint .pull-right %span.key-created-at - created #{time_ago_with_tooltip(key.created_at)} ago + created #{time_ago_with_tooltip(key.created_at)} = link_to path_to_key(key, is_admin), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-transparent prepend-left-10" do %span.sr-only Remove = icon('trash') diff --git a/app/views/projects/branches/_branch.html.haml b/app/views/projects/branches/_branch.html.haml index 76a823d3828..57e507e68c8 100644 --- a/app/views/projects/branches/_branch.html.haml +++ b/app/views/projects/branches/_branch.html.haml @@ -11,7 +11,7 @@ - if branch.name == @repository.root_ref %span.label.label-primary default - elsif @repository.merged_to_root_ref? branch.name - %span.label.label-info.has_tooltip(title="Merged into #{@repository.root_ref}") + %span.label.label-info.has-tooltip(title="Merged into #{@repository.root_ref}") merged - if @project.protected_branch? branch.name @@ -30,7 +30,7 @@ Compare - if can_remove_branch?(@project, branch.name) - = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do + = link_to namespace_project_branch_path(@project.namespace, @project, branch.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Delete branch", method: :delete, data: { confirm: "Deleting the '#{branch.name}' branch cannot be undone. Are you sure?", container: 'body' }, remote: true do = icon("trash-o") - if branch.name != @repository.root_ref diff --git a/app/views/projects/buttons/_download.html.haml b/app/views/projects/buttons/_download.html.haml index 6a60cfeff76..58f43ecb5d5 100644 --- a/app/views/projects/buttons/_download.html.haml +++ b/app/views/projects/buttons/_download.html.haml @@ -1,4 +1,4 @@ - unless @project.empty_repo? - if can? current_user, :download_code, @project - = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has_tooltip', data: {container: "body"}, rel: 'nofollow', title: "Download ZIP" do + = link_to archive_namespace_project_repository_path(@project.namespace, @project, ref: @ref, format: 'zip'), class: 'btn has-tooltip', data: {container: "body"}, rel: 'nofollow', title: "Download ZIP" do = icon('download') diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml index 133531887a2..88cbb7c03c5 100644 --- a/app/views/projects/buttons/_fork.html.haml +++ b/app/views/projects/buttons/_fork.html.haml @@ -1,7 +1,7 @@ - unless @project.empty_repo? - if current_user && can?(current_user, :fork_project, @project) - if current_user.already_forked?(@project) && current_user.manageable_namespaces.size < 2 - = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has_tooltip' do + = link_to namespace_project_path(current_user, current_user.fork_of(@project)), title: 'Go to your fork', class: 'btn has-tooltip' do = icon('code-fork fw') Fork %div.count-with-arrow @@ -9,7 +9,7 @@ %span.count = @project.forks_count - else - = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has_tooltip' do + = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do = icon('code-fork fw') Fork %div.count-with-arrow diff --git a/app/views/projects/buttons/_notifications.html.haml b/app/views/projects/buttons/_notifications.html.haml index 3e83ec3912f..a3786c35a1f 100644 --- a/app/views/projects/buttons/_notifications.html.haml +++ b/app/views/projects/buttons/_notifications.html.haml @@ -14,7 +14,7 @@ = notification_list_item(level, @membership) - when GroupMember - .btn.disabled.notifications-btn.has_tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."} + .btn.disabled.notifications-btn.has-tooltip{title: "To change the notification level, you need to be a member of the project itself, not only its group."} = icon('bell') = notification_label(@membership) = icon('angle-down') diff --git a/app/views/projects/buttons/_star.html.haml b/app/views/projects/buttons/_star.html.haml index 21ba426aaa1..02dbb2985a4 100644 --- a/app/views/projects/buttons/_star.html.haml +++ b/app/views/projects/buttons/_star.html.haml @@ -1,5 +1,5 @@ - if current_user - = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has_tooltip', method: :post, remote: true, title: "Star project" do + = link_to toggle_star_namespace_project_path(@project.namespace, @project), class: 'btn star-btn toggle-star has-tooltip', method: :post, remote: true, title: "Star project" do - if current_user.starred?(@project) = icon('star fw') %span.starred Unstar @@ -12,7 +12,7 @@ = @project.star_count - else - = link_to new_user_session_path, class: 'btn has_tooltip star-btn', title: 'You must sign in to star a project' do + = link_to new_user_session_path, class: 'btn has-tooltip star-btn', title: 'You must sign in to star a project' do = icon('star fw') Star %div.count-with-arrow diff --git a/app/views/projects/compare/_form.html.haml b/app/views/projects/compare/_form.html.haml index 4ab81f3635c..dd590a4b8ec 100644 --- a/app/views/projects/compare/_form.html.haml +++ b/app/views/projects/compare/_form.html.haml @@ -1,7 +1,7 @@ = form_tag namespace_project_compare_index_path(@project.namespace, @project), method: :post, class: 'form-inline js-requires-input' do .clearfix - if params[:to] && params[:from] - = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has_tooltip', title: 'Switch base of comparison'} + = link_to 'switch', {from: params[:to], to: params[:from]}, {class: 'commits-compare-switch has-tooltip', title: 'Switch base of comparison'} .form-group .input-group.inline-input-group %span.input-group-addon from diff --git a/app/views/projects/diffs/_file.html.haml b/app/views/projects/diffs/_file.html.haml index 3898bb202c5..698ed02ea0e 100644 --- a/app/views/projects/diffs/_file.html.haml +++ b/app/views/projects/diffs/_file.html.haml @@ -28,7 +28,7 @@ .file-actions.hidden-xs - if blob_text_viewable?(blob) - = link_to '#', class: 'js-toggle-diff-comments btn active has_tooltip', title: "Toggle comments for this file" do + = link_to '#', class: 'js-toggle-diff-comments btn active has-tooltip', title: "Toggle comments for this file" do = icon('comments') \ diff --git a/app/views/projects/diffs/_line.html.haml b/app/views/projects/diffs/_line.html.haml new file mode 100644 index 00000000000..9464c8dc996 --- /dev/null +++ b/app/views/projects/diffs/_line.html.haml @@ -0,0 +1,26 @@ +- type = line.type +%tr.line_holder{id: line_code, class: type} + - case type + - when 'match' + = render "projects/diffs/match_line", {line: line.text, + line_old: line.old_pos, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file} + - when 'nonewline' + %td.old_line.diff-line-num + %td.new_line.diff-line-num + %td.line_content.match= line.text + - else + %td.old_line.diff-line-num{class: type} + - link_text = raw(type == "new" ? " " : line.old_pos) + - if defined?(plain) && plain + = link_text + - else + = link_to link_text, "##{line_code}", id: line_code + - if @comments_allowed && can?(current_user, :create_note, @project) + = link_to_new_diff_note(line_code) + %td.new_line.diff-line-num{class: type, data: {linenumber: line.new_pos}} + - link_text = raw(type == "old" ? " " : line.new_pos) + - if defined?(plain) && plain + = link_text + - else + = link_to link_text, "##{line_code}", id: line_code + %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text) diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml index 9a8208202e4..e7169d7b599 100644 --- a/app/views/projects/diffs/_text_file.html.haml +++ b/app/views/projects/diffs/_text_file.html.haml @@ -8,26 +8,9 @@ - last_line = 0 - raw_diff_lines = diff_file.diff_lines.to_a - diff_file.highlighted_diff_lines.each_with_index do |line, index| - - type = line.type - - last_line = line.new_pos - line_code = generate_line_code(diff_file.file_path, line) - - line_old = line.old_pos - %tr.line_holder{ id: line_code, class: "#{type}" } - - if type == "match" - = render "projects/diffs/match_line", {line: line.text, - line_old: line_old, line_new: line.new_pos, bottom: false, new_file: diff_file.new_file} - - elsif type == 'nonewline' - %td.old_line.diff-line-num - %td.new_line.diff-line-num - %td.line_content.match= line.text - - else - %td.old_line.diff-line-num{class: type} - = link_to raw(type == "new" ? " " : line_old), "##{line_code}", id: line_code - - if @comments_allowed && can?(current_user, :create_note, @project) - = link_to_new_diff_note(line_code) - %td.new_line.diff-line-num{class: type, data: {linenumber: line.new_pos}} - = link_to raw(type == "old" ? " " : line.new_pos), "##{line_code}", id: line_code - %td.line_content{class: "noteable_line #{type} #{line_code}", data: { line_code: line_code }}= diff_line_content(line.text) + - last_line = line.new_pos + = render "projects/diffs/line", {line: line, diff_file: diff_file, line_code: line_code} - if @reply_allowed - comments = @line_notes.select { |n| n.line_code == line_code && n.active? }.sort_by(&:created_at) diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml index edabc2d3b44..73a7fc0e1ac 100644 --- a/app/views/projects/forks/new.html.haml +++ b/app/views/projects/forks/new.html.haml @@ -12,7 +12,7 @@ .col-md-2.col-sm-3 - if fork = namespace.find_fork_of(@project) .fork-thumbnail - = link_to project_path(fork), title: "Visit project fork", class: 'has_tooltip' do + = link_to project_path(fork), title: "Visit project fork", class: 'has-tooltip' do = image_tag namespace_icon(namespace, 100) .caption %strong @@ -22,7 +22,7 @@ - else .fork-thumbnail - = link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has_tooltip' do + = link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), title: "Fork here", method: "POST", class: 'has-tooltip' do = image_tag namespace_icon(namespace, 100) .caption %strong diff --git a/app/views/projects/issues/show.html.haml b/app/views/projects/issues/show.html.haml index 52df3de8a27..6fa059cbe68 100644 --- a/app/views/projects/issues/show.html.haml +++ b/app/views/projects/issues/show.html.haml @@ -45,7 +45,6 @@ - if can?(current_user, :update_issue, @issue) = link_to 'Reopen issue', issue_path(@issue, issue: {state_event: :reopen}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-reopen #{issue_button_visibility(@issue, false)}", title: 'Reopen issue' = link_to 'Close issue', issue_path(@issue, issue: {state_event: :close}, status_only: true, format: 'json'), data: {no_turbolink: true}, class: "btn btn-nr btn-grouped btn-close #{issue_button_visibility(@issue, true)}", title: 'Close issue' - = link_to edit_namespace_project_issue_path(@project.namespace, @project, @issue), class: 'btn btn-nr btn-grouped issuable-edit' do = icon('pencil-square-o') Edit diff --git a/app/views/projects/merge_requests/_merge_request.html.haml b/app/views/projects/merge_requests/_merge_request.html.haml index 13d0cbdde1d..391193eed6c 100644 --- a/app/views/projects/merge_requests/_merge_request.html.haml +++ b/app/views/projects/merge_requests/_merge_request.html.haml @@ -17,7 +17,7 @@ - if merge_request.open? && merge_request.broken? %li - = link_to merge_request_path(merge_request), class: "has_tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do + = link_to merge_request_path(merge_request), class: "has-tooltip", title: "Cannot be merged automatically", data: { container: 'body' } do = icon('exclamation-triangle') - if merge_request.assignee diff --git a/app/views/projects/merge_requests/show/_mr_title.html.haml b/app/views/projects/merge_requests/show/_mr_title.html.haml index eeb605e2dc5..ab4b1f14be5 100644 --- a/app/views/projects/merge_requests/show/_mr_title.html.haml +++ b/app/views/projects/merge_requests/show/_mr_title.html.haml @@ -29,7 +29,7 @@ - if @merge_request.open? = link_to 'Close', merge_request_path(@merge_request, merge_request: { state_event: :close }), method: :put, class: 'btn btn-nr btn-grouped btn-close', title: 'Close merge request' = link_to edit_namespace_project_merge_request_path(@project.namespace, @project, @merge_request), class: 'btn btn-nr btn-grouped issuable-edit', id: 'edit_merge_request' do - %i.fa.fa-pencil-square-o + = icon('pencil-square-o') Edit - if @merge_request.closed? = link_to 'Reopen', merge_request_path(@merge_request, merge_request: {state_event: :reopen }), method: :put, class: 'btn btn-nr btn-grouped btn-reopen reopen-mr-link', title: 'Reopen merge request' diff --git a/app/views/projects/notes/_edit_form.html.haml b/app/views/projects/notes/_edit_form.html.haml index 13e624764d9..2999befffc6 100644 --- a/app/views/projects/notes/_edit_form.html.haml +++ b/app/views/projects/notes/_edit_form.html.haml @@ -5,6 +5,6 @@ = render 'projects/zen', f: f, attr: :note, classes: 'note_text js-note-text js-task-list-field' = render 'projects/notes/hints' - .note-form-actions + .note-form-actions.clearfix = f.submit 'Save Comment', class: 'btn btn-nr btn-save btn-grouped js-comment-button' = link_to 'Cancel', '#', class: 'btn btn-nr btn-cancel note-edit-cancel' diff --git a/app/views/projects/tags/_tag.html.haml b/app/views/projects/tags/_tag.html.haml index 399782273d3..dbc35c16feb 100644 --- a/app/views/projects/tags/_tag.html.haml +++ b/app/views/projects/tags/_tag.html.haml @@ -15,11 +15,11 @@ = render 'projects/tags/download', ref: tag.name, project: @project - if can?(current_user, :push_code, @project) - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has_tooltip', title: "Edit release notes" do + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, tag.name), class: 'btn-grouped btn has-tooltip', title: "Edit release notes" do = icon("pencil") - if can?(current_user, :admin_project, @project) - = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do + = link_to namespace_project_tag_path(@project.namespace, @project, tag.name), class: 'btn btn-grouped btn-xs btn-remove remove-row has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{tag.name}' tag cannot be undone. Are you sure?", container: 'body' }, remote: true do = icon("trash-o") - if commit diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index 8c7f93f93b6..1dc9b799a95 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -5,17 +5,17 @@ .gray-content-block .pull-right - if can?(current_user, :push_code, @project) - = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has_tooltip', title: 'Edit release notes' do + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn-grouped btn has-tooltip', title: 'Edit release notes' do = icon("pencil") - = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse files' do + = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse files' do = icon('files-o') - = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has_tooltip', title: 'Browse commits' do + = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn btn-grouped has-tooltip', title: 'Browse commits' do = icon('history') - if can? current_user, :download_code, @project = render 'projects/tags/download', ref: @tag.name, project: @project - if can?(current_user, :admin_project, @project) .pull-right - = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has_tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do + = link_to namespace_project_tag_path(@project.namespace, @project, @tag.name), class: 'btn btn-remove remove-row grouped has-tooltip', title: "Delete tag", method: :delete, data: { confirm: "Deleting the '#{@tag.name}' tag cannot be undone. Are you sure?" } do %i.fa.fa-trash-o .title %span.item-title= @tag.name diff --git a/app/views/projects/tree/_tree_header.html.haml b/app/views/projects/tree/_tree_header.html.haml index 3eb626e6dca..ba69569b1e7 100644 --- a/app/views/projects/tree/_tree_header.html.haml +++ b/app/views/projects/tree/_tree_header.html.haml @@ -15,7 +15,7 @@ - if current_user %li - if !on_top_of_branch? - %span.btn.btn-sm.add-to-tree.disabled.has_tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }} + %span.btn.btn-sm.add-to-tree.disabled.has-tooltip{title: "You can only add files when you are on a branch", data: { container: 'body' }} = icon('plus') - else %span.dropdown diff --git a/app/views/search/results/_milestone.html.haml b/app/views/search/results/_milestone.html.haml index e0b18733d74..b31595d8d1c 100644 --- a/app/views/search/results/_milestone.html.haml +++ b/app/views/search/results/_milestone.html.haml @@ -6,4 +6,4 @@ - if milestone.description.present? .description.term = preserve do - = search_md_sanitize(markdown(milestone.description))
\ No newline at end of file + = search_md_sanitize(markdown(milestone.description)) diff --git a/app/views/shared/_clone_panel.html.haml b/app/views/shared/_clone_panel.html.haml index faf7e49ed29..974751d9970 100644 --- a/app/views/shared/_clone_panel.html.haml +++ b/app/views/shared/_clone_panel.html.haml @@ -8,11 +8,9 @@ = icon('angle-down') %ul.dropdown-menu.dropdown-menu-right.clone-options-dropdown %li - %a#ssh-selector{href: @project.ssh_url_to_repo} - SSH + = ssh_clone_button(project) %li - %a#http-selector{href: @project.http_url_to_repo} - HTTPS + = http_clone_button(project) = text_field_tag :project_clone, default_url_to_repo(project), class: "js-select-on-focus form-control", readonly: true .input-group-btn diff --git a/app/views/shared/groups/_group.html.haml b/app/views/shared/groups/_group.html.haml index 03103d46bb9..db416b9d91a 100644 --- a/app/views/shared/groups/_group.html.haml +++ b/app/views/shared/groups/_group.html.haml @@ -14,7 +14,7 @@ .stats %span - = icon('home') + = icon('bookmark') = number_with_delimiter(group.projects.count) %span diff --git a/app/views/shared/issuable/_form.html.haml b/app/views/shared/issuable/_form.html.haml index 80418e69d9c..551f0cc0b51 100644 --- a/app/views/shared/issuable/_form.html.haml +++ b/app/views/shared/issuable/_form.html.haml @@ -85,13 +85,26 @@ - if can? current_user, :admin_label, issuable.project = link_to 'Create new label', new_namespace_project_label_path(issuable.project.namespace, issuable.project), target: :blank +- if issuable.can_move?(current_user) + %hr + .form-group + = label_tag :move_to_project_id, 'Move', class: 'control-label' + .col-sm-10 + - projects = project_options(issuable, current_user, ability: :admin_issue) + = select_tag(:move_to_project_id, projects, include_blank: true, + class: 'select2', data: { placeholder: 'Select project' }) + + %span{ data: { toggle: 'tooltip', placement: 'auto top' }, style: 'cursor: default', + title: 'Moving an issue will copy the discussion to a different project and close it here. All participants will be notified of the new location.' } + = icon('question-circle') + - if issuable.is_a?(MergeRequest) %hr - - if @merge_request.new_record? - .form-group - = f.label :source_branch, class: 'control-label' - .col-sm-10 - = f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true }) + - if @merge_request.new_record? + .form-group + = f.label :source_branch, class: 'control-label' + .col-sm-10 + = f.select(:source_branch, [@merge_request.source_branch], { }, { class: 'source_branch select2 span2', disabled: true }) .form-group = f.label :target_branch, class: 'control-label' .col-sm-10 @@ -114,7 +127,11 @@ for this project. - if issuable.new_record? - - cancel_project = issuable.source_project + = link_to 'Cancel', namespace_project_issues_path(@project.namespace, @project), class: 'btn btn-cancel' - else - - cancel_project = issuable.project - = link_to 'Cancel', [cancel_project.namespace.becomes(Namespace), cancel_project, issuable], class: 'btn btn-cancel' + .pull-right + - if current_user.can?(:"destroy_#{issuable.to_ability_name}", @project) + = link_to polymorphic_path([@project.namespace.becomes(Namespace), @project, issuable]), method: :delete, class: 'btn btn-grouped' do + = icon('trash-o') + Delete + = link_to 'Cancel', namespace_project_issue_path(@project.namespace, @project, issuable), class: 'btn btn-grouped btn-cancel' diff --git a/app/views/shared/milestones/_issuable.html.haml b/app/views/shared/milestones/_issuable.html.haml index 85888096722..e1127b2311c 100644 --- a/app/views/shared/milestones/_issuable.html.haml +++ b/app/views/shared/milestones/_issuable.html.haml @@ -23,5 +23,5 @@ - if assignee = link_to polymorphic_path(base_url_args, { milestone_title: @milestone.title, assignee_id: issuable.assignee_id, state: 'all' }), - class: 'has_tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do + class: 'has-tooltip', data: { 'original-title' => "Assigned to #{sanitize(assignee.name)}", container: 'body' } do - image_tag(avatar_icon(issuable.assignee, 16), class: "avatar s16", alt: '') diff --git a/app/views/shared/snippets/_header.html.haml b/app/views/shared/snippets/_header.html.haml index aa5acee9c14..3c445f67236 100644 --- a/app/views/shared/snippets/_header.html.haml +++ b/app/views/shared/snippets/_header.html.haml @@ -1,5 +1,5 @@ .detail-page-header - .snippet-box.has_tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }} + .snippet-box.has-tooltip{class: visibility_level_color(@snippet.visibility_level), title: snippet_visibility_level_description(@snippet.visibility_level, @snippet), data: { container: 'body' }} = visibility_level_icon(@snippet.visibility_level, fw: false) = visibility_level_label(@snippet.visibility_level) %span.identifier diff --git a/app/views/votes/_votes_block.html.haml b/app/views/votes/_votes_block.html.haml index 20d2d5f317b..02647229776 100644 --- a/app/views/votes/_votes_block.html.haml +++ b/app/views/votes/_votes_block.html.haml @@ -1,6 +1,6 @@ .awards.votes-block - awards_sort(votable.notes.awards.grouped_awards).each do |emoji, notes| - %button.btn.award-control.js-emoji-btn.has_tooltip{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user), data: {placement: "top"}} + %button.btn.award-control.js-emoji-btn.has-tooltip{class: (note_active_class(notes, current_user)), title: emoji_author_list(notes, current_user), data: {placement: "top"}} = emoji_icon(emoji) %span.award-control-text.js-counter = notes.count diff --git a/app/workers/repository_fork_worker.rb b/app/workers/repository_fork_worker.rb index 21d311579e3..f9e32337983 100644 --- a/app/workers/repository_fork_worker.rb +++ b/app/workers/repository_fork_worker.rb @@ -20,14 +20,15 @@ class RepositoryForkWorker return end + project.repository.after_import + unless project.valid_repo? - logger.error("Project #{id} had an invalid repository after fork") + logger.error("Project #{project_id} had an invalid repository after fork") project.update(import_error: "The forked repository is invalid.") project.import_fail return end - project.repository.after_import project.import_finish end end |