diff options
author | tiagonbotelho <tiagonbotelho@hotmail.com> | 2016-07-22 21:57:46 +0100 |
---|---|---|
committer | tiagonbotelho <tiagonbotelho@hotmail.com> | 2016-07-22 21:57:46 +0100 |
commit | 850813d29867aac5935e1128880784f97071a6d8 (patch) | |
tree | d5da375e2dcdaf54d653bc50aa75d2e4dd7f096e /app | |
parent | 2d64bda01f983c43f915e96bd5bf8fcb0790eb0e (diff) | |
parent | 08b5bec2fa3beb2880d451a9d9270813b0a22519 (diff) | |
download | gitlab-ce-850813d29867aac5935e1128880784f97071a6d8.tar.gz |
Merge branch 'master' into filter-branch-by-name
Diffstat (limited to 'app')
139 files changed, 1616 insertions, 831 deletions
diff --git a/app/assets/javascripts/admin.js.coffee b/app/assets/javascripts/admin.js.coffee index b2b8e1b7ffb..90c09619f8c 100644 --- a/app/assets/javascripts/admin.js.coffee +++ b/app/assets/javascripts/admin.js.coffee @@ -38,3 +38,14 @@ class @Admin $('li.group_member').bind 'ajax:success', -> Turbolinks.visit(location.href) + + showBlacklistType = -> + if $("input[name='blacklist_type']:checked").val() == 'file' + $('.blacklist-file').show() + $('.blacklist-raw').hide() + else + $('.blacklist-file').hide() + $('.blacklist-raw').show() + + $("input[name='blacklist_type']").click showBlacklistType + showBlacklistType() diff --git a/app/assets/javascripts/files_comment_button.js.coffee b/app/assets/javascripts/files_comment_button.js.coffee index db0bf7082a9..5ab82c39fcd 100644 --- a/app/assets/javascripts/files_comment_button.js.coffee +++ b/app/assets/javascripts/files_comment_button.js.coffee @@ -7,7 +7,6 @@ class @FilesCommentButton UNFOLDABLE_LINE_CLASS = 'js-unfold' EMPTY_CELL_CLASS = 'empty-cell' OLD_LINE_CLASS = 'old_line' - NEW_CLASS = 'new' LINE_COLUMN_CLASSES = ".#{LINE_NUMBER_CLASS}, .line_content" TEXT_FILE_SELECTOR = '.text-file' DEBOUNCE_TIMEOUT_DURATION = 100 @@ -18,6 +17,8 @@ class @FilesCommentButton debounce = _.debounce @render, DEBOUNCE_TIMEOUT_DURATION $(document) + .off 'mouseover', LINE_COLUMN_CLASSES + .off 'mouseleave', LINE_COLUMN_CLASSES .on 'mouseover', LINE_COLUMN_CLASSES, debounce .on 'mouseleave', LINE_COLUMN_CLASSES, @destroy @@ -64,20 +65,20 @@ class @FilesCommentButton getLineContent: (hoveredElement) -> return hoveredElement if hoveredElement.hasClass LINE_CONTENT_CLASS - $(".#{LINE_CONTENT_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent()) + if @VIEW_TYPE is 'inline' + return $(hoveredElement).closest(LINE_HOLDER_CLASS).find ".#{LINE_CONTENT_CLASS}" + else + return $(hoveredElement).next ".#{LINE_CONTENT_CLASS}" getButtonParent: (hoveredElement) -> if @VIEW_TYPE is 'inline' return hoveredElement if hoveredElement.hasClass OLD_LINE_CLASS - $(".#{OLD_LINE_CLASS}", hoveredElement.parent()) + hoveredElement.parent().find ".#{OLD_LINE_CLASS}" else return hoveredElement if hoveredElement.hasClass LINE_NUMBER_CLASS - $(".#{LINE_NUMBER_CLASS + @diffTypeClass hoveredElement}", hoveredElement.parent()) - - diffTypeClass: (hoveredElement) -> - if hoveredElement.hasClass(NEW_CLASS) then '.new' else '.old' + $(hoveredElement).prev ".#{LINE_NUMBER_CLASS}" isMovingToSameType: (e) -> newButtonParent = @getButtonParent $(e.toElement) diff --git a/app/assets/javascripts/gl_dropdown.js.coffee b/app/assets/javascripts/gl_dropdown.js.coffee index 951530e03a5..7086ece29b8 100644 --- a/app/assets/javascripts/gl_dropdown.js.coffee +++ b/app/assets/javascripts/gl_dropdown.js.coffee @@ -210,9 +210,22 @@ class GitLabDropdown data: => return @fullData callback: (data) => - currentIndex = -1 @parseData data + unless @filterInput.val() is '' + selector = '.dropdown-content li:not(.divider):visible' + + if @dropdown.find('.dropdown-toggle-page').length + selector = ".dropdown-page-one #{selector}" + + $(selector, @dropdown) + .first() + .find('a') + .addClass('is-focused') + + currentIndex = 0 + + # Event listeners @dropdown.on "shown.bs.dropdown", @opened diff --git a/app/assets/javascripts/graphs/application.js.coffee b/app/assets/javascripts/graphs/graphs_bundle.js.coffee index e0f681acf0b..e0f681acf0b 100644 --- a/app/assets/javascripts/graphs/application.js.coffee +++ b/app/assets/javascripts/graphs/graphs_bundle.js.coffee diff --git a/app/assets/javascripts/merge_request_widget.js.coffee b/app/assets/javascripts/merge_request_widget.js.coffee index 779f536d9f0..963a0550c35 100644 --- a/app/assets/javascripts/merge_request_widget.js.coffee +++ b/app/assets/javascripts/merge_request_widget.js.coffee @@ -55,10 +55,13 @@ class @MergeRequestWidget $('.mr-state-widget').replaceWith(data) ciLabelForStatus: (status) -> - if status is 'success' - 'passed' - else - status + switch status + when 'success' + 'passed' + when 'success_with_warnings' + 'passed with warnings' + else + status pollCIStatus: -> @fetchBuildStatusInterval = setInterval ( => @@ -116,7 +119,7 @@ class @MergeRequestWidget showCIStatus: (state) -> return if not state? $('.ci_widget').hide() - allowed_states = ["failed", "canceled", "running", "pending", "success", "skipped", "not_found"] + allowed_states = ["failed", "canceled", "running", "pending", "success", "success_with_warnings", "skipped", "not_found"] if state in allowed_states $('.ci_widget.ci-' + state).show() switch state @@ -124,7 +127,7 @@ class @MergeRequestWidget @setMergeButtonClass('btn-danger') when "running" @setMergeButtonClass('btn-warning') - when "success" + when "success", "success_with_warnings" @setMergeButtonClass('btn-create') else $('.ci_widget.ci-error').show() diff --git a/app/assets/javascripts/network/application.js.coffee b/app/assets/javascripts/network/network_bundle.js.coffee index f75f63869c5..f75f63869c5 100644 --- a/app/assets/javascripts/network/application.js.coffee +++ b/app/assets/javascripts/network/network_bundle.js.coffee diff --git a/app/assets/javascripts/notes.js.coffee b/app/assets/javascripts/notes.js.coffee index 0ea54faae1a..d4de712f88c 100644 --- a/app/assets/javascripts/notes.js.coffee +++ b/app/assets/javascripts/notes.js.coffee @@ -162,7 +162,7 @@ class @Notes @last_fetched_at = data.last_fetched_at @setPollingInterval(data.notes.length) $.each notes, (i, note) => - if note.discussion_with_diff_html? + if note.discussion_html? @renderDiscussionNote(note) else @renderNote(note) @@ -251,7 +251,7 @@ class @Notes discussionContainer = $(".notes[data-discussion-id='" + note.original_discussion_id + "']") if discussionContainer.length is 0 # insert the note and the reply button after the temp row - row.after note.discussion_html + row.after note.diff_discussion_html # remove the note (will be added again below) row.next().find(".note").remove() @@ -265,7 +265,7 @@ class @Notes # Init discussion on 'Discussion' page if it is merge request page if $('body').attr('data-page').indexOf('projects:merge_request') is 0 $('ul.main-notes-list') - .append(note.discussion_with_diff_html) + .append(note.discussion_html) .syntaxHighlight() else # append new note to all matching discussions diff --git a/app/assets/javascripts/profile/application.js.coffee b/app/assets/javascripts/profile/profile_bundle.js.coffee index 91cacfece46..91cacfece46 100644 --- a/app/assets/javascripts/profile/application.js.coffee +++ b/app/assets/javascripts/profile/profile_bundle.js.coffee diff --git a/app/assets/javascripts/right_sidebar.js.coffee b/app/assets/javascripts/right_sidebar.js.coffee index 12340bbce54..0c95301e380 100644 --- a/app/assets/javascripts/right_sidebar.js.coffee +++ b/app/assets/javascripts/right_sidebar.js.coffee @@ -120,6 +120,9 @@ class @Sidebar i.show() sidebarCollapseClicked: (e) -> + + return if $(e.currentTarget).hasClass('dont-change-state') + sidebar = e.data e.preventDefault() $block = $(@).closest('.block') diff --git a/app/assets/javascripts/users/application.js.coffee b/app/assets/javascripts/users/users_bundle.js.coffee index 91cacfece46..91cacfece46 100644 --- a/app/assets/javascripts/users/application.js.coffee +++ b/app/assets/javascripts/users/users_bundle.js.coffee diff --git a/app/assets/stylesheets/framework/blocks.scss b/app/assets/stylesheets/framework/blocks.scss index ad94e457cfd..7ce203d2ec7 100644 --- a/app/assets/stylesheets/framework/blocks.scss +++ b/app/assets/stylesheets/framework/blocks.scss @@ -232,7 +232,9 @@ .nav-block { .controls { float: right; - margin-top: 11px; + margin-top: 8px; + padding-bottom: 7px; + border-bottom: 1px solid $border-color; } } diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss index 590b8f54363..f87b8a2ad1c 100644 --- a/app/assets/stylesheets/framework/buttons.scss +++ b/app/assets/stylesheets/framework/buttons.scss @@ -49,6 +49,17 @@ border-color: $border-dark; color: $color; } + + svg { + + path { + fill: $color; + } + + use { + stroke: $color; + } + } } @mixin btn-green { @@ -173,6 +184,13 @@ .caret { margin-left: 5px; } + + svg { + height: 15px; + width: auto; + position: relative; + top: 2px; + } } .btn-lg { diff --git a/app/assets/stylesheets/framework/markdown_area.scss b/app/assets/stylesheets/framework/markdown_area.scss index 5d3273ea64d..96565da1bc9 100644 --- a/app/assets/stylesheets/framework/markdown_area.scss +++ b/app/assets/stylesheets/framework/markdown_area.scss @@ -98,13 +98,30 @@ .md { &.md-preview-holder { - code { - white-space: pre-wrap; - word-break: keep-all; - } - + // Reset ul style types since we're nested inside a ul already @include bulleted-list; } + + // On diffs code should wrap nicely and not overflow + code { + white-space: pre-wrap; + word-break: keep-all; + } + + hr { + // Darken 'whitesmoke' a bit to make it more visible in note bodies + border-color: darken(#f5f5f5, 8%); + margin: 10px 0; + } + + // Border around images in issue and MR comments. + img:not(.emoji) { + border: 1px solid $table-border-gray; + padding: 5px; + margin: 5px 0; + // Ensure that image does not exceed viewport + max-height: calc(100vh - 100px); + } } .toolbar-group { diff --git a/app/assets/stylesheets/framework/sidebar.scss b/app/assets/stylesheets/framework/sidebar.scss index d52e8f00172..3fa4a22258d 100644 --- a/app/assets/stylesheets/framework/sidebar.scss +++ b/app/assets/stylesheets/framework/sidebar.scss @@ -198,6 +198,10 @@ header.header-pinned-nav { .sidebar-collapsed-icon { cursor: pointer; + + .btn { + background-color: $gray-light; + } } } diff --git a/app/assets/stylesheets/pages/builds.scss b/app/assets/stylesheets/pages/builds.scss index 99a2cd306cf..e26f8f7080d 100644 --- a/app/assets/stylesheets/pages/builds.scss +++ b/app/assets/stylesheets/pages/builds.scss @@ -53,6 +53,14 @@ left: 70px; } } + + .nav-links { + svg { + position: relative; + top: 2px; + margin-right: 3px; + } + } } .build-header { diff --git a/app/assets/stylesheets/pages/commit.scss b/app/assets/stylesheets/pages/commit.scss index 35ab28b3fea..bbe0c6c5f1f 100644 --- a/app/assets/stylesheets/pages/commit.scss +++ b/app/assets/stylesheets/pages/commit.scss @@ -68,6 +68,12 @@ } } +.ci-status-link { + svg { + overflow: visible; + } +} + .commit-box { border-top: 1px solid $border-color; diff --git a/app/assets/stylesheets/pages/events.scss b/app/assets/stylesheets/pages/events.scss index a2145956eb5..5c336bb1c7e 100644 --- a/app/assets/stylesheets/pages/events.scss +++ b/app/assets/stylesheets/pages/events.scss @@ -176,3 +176,11 @@ } } } + +// hide event scope (namespace + project) where it is not necessary +.project-activity { + .event-scope { + display: none; + } +} + diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 542fa244689..7a50bc9c832 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -122,7 +122,8 @@ button { float: right; - padding: 3px 5px; + padding: 1px 5px; + background-color: $gray-light; } } @@ -268,7 +269,7 @@ .issuable-header-btn { background: $gray-normal; border: 1px solid $border-gray-normal; - + &:hover { background: $gray-dark; border: 1px solid $border-gray-dark; diff --git a/app/assets/stylesheets/pages/issues.scss b/app/assets/stylesheets/pages/issues.scss index 9807c5a808d..ee3b2d2b801 100644 --- a/app/assets/stylesheets/pages/issues.scss +++ b/app/assets/stylesheets/pages/issues.scss @@ -78,6 +78,14 @@ form.edit-issue { } } +.merge-request-ci-status { + svg { + margin-right: 4px; + position: relative; + top: 1px; + } +} + @media (max-width: $screen-xs-max) { .issue-btn-group { width: 100%; diff --git a/app/assets/stylesheets/pages/merge_requests.scss b/app/assets/stylesheets/pages/merge_requests.scss index fbff0c97355..db295935b00 100644 --- a/app/assets/stylesheets/pages/merge_requests.scss +++ b/app/assets/stylesheets/pages/merge_requests.scss @@ -60,14 +60,25 @@ .ci_widget { border-bottom: 1px solid #eef0f2; - i { + svg { margin-right: 4px; + position: relative; + top: 1px; + overflow: visible; } &.ci-success { color: $gl-success; } + &.ci-success_with_warnings { + color: $gl-success; + + i { + color: $gl-warning; + } + } + &.ci-skipped { background-color: #eee; color: #888; @@ -196,6 +207,16 @@ .merge-request-title { margin-bottom: 2px; + + .ci-status-link { + + svg { + height: 16px; + width: 16px; + position: relative; + top: 3px; + } + } } } diff --git a/app/assets/stylesheets/pages/notes.scss b/app/assets/stylesheets/pages/notes.scss index ac8c02b59dc..a2b5437e503 100644 --- a/app/assets/stylesheets/pages/notes.scss +++ b/app/assets/stylesheets/pages/notes.scss @@ -91,34 +91,11 @@ ul.notes { // Reset ul style types since we're nested inside a ul already @include bulleted-list; - // On diffs code should wrap nicely and not overflow - code { - white-space: pre-wrap; - } - ul.task-list { ul:not(.task-list) { padding-left: 1.3em; } } - - hr { - // Darken 'whitesmoke' a bit to make it more visible in note bodies - border-color: darken(#f5f5f5, 8%); - margin: 10px 0; - } - - code { - word-break: keep-all; - } - - // Border around images in issue and MR comments. - img:not(.emoji) { - border: 1px solid $table-border-gray; - padding: 5px; - margin: 5px 0; - max-height: calc(100vh - 100px); - } } } diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss index a3b72ec9574..c58e2ffe7f5 100644 --- a/app/assets/stylesheets/pages/pipelines.scss +++ b/app/assets/stylesheets/pages/pipelines.scss @@ -29,9 +29,18 @@ } } +.pipeline-holder { + width: 100%; + overflow: auto; +} + .table.builds { min-width: 1200px; + &.pipeline { + min-width: 650px; + } + tr { th { padding: 16px 8px; @@ -49,6 +58,14 @@ .commit-link { + .ci-status { + + svg { + top: 1px; + margin-right: 0; + } + } + a:hover { text-decoration: none; } @@ -68,7 +85,7 @@ svg { height: 14px; - width: auto; + width: 14px; vertical-align: middle; fill: $table-text-gray; } @@ -85,7 +102,7 @@ .commit-title { margin-top: 4px; - max-width: 320px; + max-width: 300px; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; @@ -124,6 +141,20 @@ } } + .stage-cell { + + svg { + height: 18px; + width: 18px; + vertical-align: middle; + overflow: visible; + } + + .light { + width: 3px; + } + } + .duration, .finished-at { color: $table-text-gray; @@ -136,7 +167,7 @@ svg { width: 12px; - height: auto; + height: 12px; vertical-align: middle; margin-right: 4px; } diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index ea9f7cf0540..cc3aef5199e 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -129,6 +129,17 @@ color: $layout-link-gray; } + svg { + + path { + fill: $layout-link-gray; + } + + use { + stroke: $layout-link-gray; + } + } + .fa-caret-down { margin-left: 3px; } @@ -322,18 +333,53 @@ a.deploy-project-label { } .fork-namespaces { - .fork-thumbnail { - text-align: center; - margin-bottom: $gl-padding; - - .caption { - padding: $gl-padding 0; - min-height: 30px; - } + .row { + -webkit-flex-wrap: wrap; + display: -webkit-flex; + display: flex; + flex-wrap: wrap; + justify-content: flex-start; + + .fork-thumbnail { + @include border-radius($border-radius-base); + background-color: $white-light; + border: 1px solid $border-white-light; + height: 202px; + margin: $gl-padding; + text-align: center; + width: 169px; + &:hover, &.forked { + background-color: $row-hover; + border-color: $row-hover-border; + } + .no-avatar { + width: 100px; + height: 100px; + background-color: $gray-light; + border: 1px solid $gray-dark; + margin: 0 auto; + @include border-radius(50%); + i { + font-size: 100px; + color: $gray-dark; + } + } + a { + display: block; + width: 100%; + height: 100%; + padding-top: $gl-padding; + color: $gl-gray; + .caption { + min-height: 30px; + padding: $gl-padding 0; + } + } - img { - @include border-radius(50%); - max-width: 100px; + img { + @include border-radius(50%); + max-width: 100px; + } } } } @@ -486,6 +532,11 @@ pre.light-well { > span { margin-left: 10px; } + + svg { + position: relative; + top: 2px; + } } } diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss index c6b053150be..587f2d9f3c1 100644 --- a/app/assets/stylesheets/pages/status.scss +++ b/app/assets/stylesheets/pages/status.scss @@ -15,7 +15,8 @@ border-color: $gl-danger; } - &.ci-success { + &.ci-success, + &.ci-success_with_warnings { color: $gl-success; border-color: $gl-success; } @@ -41,6 +42,15 @@ color: $blue-normal; border-color: $blue-normal; } + + svg { + height: 13px; + width: 13px; + position: relative; + top: 1px; + margin: 0 3px; + overflow: visible; + } } .ci-status-icon-success { @@ -49,9 +59,12 @@ .ci-status-icon-failed { color: $gl-danger; } - .ci-status-icon-pending { + + .ci-status-icon-pending, + .ci-status-icon-success_with_warning { color: $gl-warning; } + .ci-status-icon-running { color: $blue-normal; } @@ -62,3 +75,11 @@ color: $gl-gray; } } + +.visible-xs-inline { + .ci-status-link { + position: relative; + top: 2px; + left: 5px; + } +} diff --git a/app/assets/stylesheets/pages/tags.scss b/app/assets/stylesheets/pages/tags.scss new file mode 100644 index 00000000000..24ebd3e7cfa --- /dev/null +++ b/app/assets/stylesheets/pages/tags.scss @@ -0,0 +1,7 @@ +.tag-buttons { + line-height: 40px; + + .btn:not(.dropdown-toggle) { + margin-left: 10px; + } +} diff --git a/app/controllers/admin/application_settings_controller.rb b/app/controllers/admin/application_settings_controller.rb index 23ba83aba0e..9e1dc15de84 100644 --- a/app/controllers/admin/application_settings_controller.rb +++ b/app/controllers/admin/application_settings_controller.rb @@ -64,6 +64,7 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController params[:application_setting][:disabled_oauth_sign_in_sources] = AuthHelper.button_based_providers.map(&:to_s) - Array(enabled_oauth_sign_in_sources) + params.delete(:domain_blacklist_raw) if params[:domain_blacklist_file] params.require(:application_setting).permit( :default_projects_limit, @@ -83,7 +84,10 @@ class Admin::ApplicationSettingsController < Admin::ApplicationController :default_project_visibility, :default_snippet_visibility, :default_group_visibility, - :restricted_signup_domains_raw, + :domain_whitelist_raw, + :domain_blacklist_enabled, + :domain_blacklist_raw, + :domain_blacklist_file, :version_check_enabled, :admin_notification_email, :user_oauth_applications, diff --git a/app/controllers/admin/groups_controller.rb b/app/controllers/admin/groups_controller.rb index 94b5aaa71d0..f3a88a8e6c8 100644 --- a/app/controllers/admin/groups_controller.rb +++ b/app/controllers/admin/groups_controller.rb @@ -60,6 +60,6 @@ class Admin::GroupsController < Admin::ApplicationController end def group_params - params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level) + params.require(:group).permit(:name, :description, :path, :avatar, :visibility_level, :request_access_enabled) end end diff --git a/app/controllers/admin/services_controller.rb b/app/controllers/admin/services_controller.rb index 46133588332..7c37f3155da 100644 --- a/app/controllers/admin/services_controller.rb +++ b/app/controllers/admin/services_controller.rb @@ -1,4 +1,6 @@ class Admin::ServicesController < Admin::ApplicationController + include ServiceParams + before_action :service, only: [:edit, :update] def index @@ -13,7 +15,7 @@ class Admin::ServicesController < Admin::ApplicationController end def update - if service.update_attributes(application_services_params[:service]) + if service.update_attributes(service_params[:service]) redirect_to admin_application_settings_services_path, notice: 'Application settings saved successfully' else @@ -37,15 +39,4 @@ class Admin::ServicesController < Admin::ApplicationController def service @service ||= Service.where(id: params[:id], template: true).first end - - def application_services_params - application_services_params = params.permit(:id, - service: Projects::ServicesController::ALLOWED_PARAMS) - if application_services_params[:service].is_a?(Hash) - Projects::ServicesController::FILTER_BLANK_PARAMS.each do |param| - application_services_params[:service].delete(param) if application_services_params[:service][param].blank? - end - end - application_services_params - end end diff --git a/app/controllers/concerns/service_params.rb b/app/controllers/concerns/service_params.rb new file mode 100644 index 00000000000..471d15af913 --- /dev/null +++ b/app/controllers/concerns/service_params.rb @@ -0,0 +1,35 @@ +module ServiceParams + extend ActiveSupport::Concern + + ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_url, :api_version, :subdomain, + :room, :recipients, :project_url, :webhook, + :user_key, :device, :priority, :sound, :bamboo_url, :username, :password, + :build_key, :server, :teamcity_url, :drone_url, :build_type, + :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel, + :colorize_messages, :channels, + :push_events, :issues_events, :merge_requests_events, :tag_push_events, + :note_events, :build_events, :wiki_page_events, + :notify_only_broken_builds, :add_pusher, + :send_from_committer_email, :disable_diffs, :external_wiki_url, + :notify, :color, + :server_host, :server_port, :default_irc_uri, :enable_ssl_verification, + :jira_issue_transition_id] + + # Parameters to ignore if no value is specified + FILTER_BLANK_PARAMS = [:password] + + def service_params + dynamic_params = [] + dynamic_params.concat(@service.event_channel_names) + + service_params = params.permit(:id, service: ALLOWED_PARAMS + dynamic_params) + + if service_params[:service].is_a?(Hash) + FILTER_BLANK_PARAMS.each do |param| + service_params[:service].delete(param) if service_params[:service][param].blank? + end + end + + service_params + end +end diff --git a/app/controllers/groups_controller.rb b/app/controllers/groups_controller.rb index a04bf7df722..6780a6d4d87 100644 --- a/app/controllers/groups_controller.rb +++ b/app/controllers/groups_controller.rb @@ -121,7 +121,7 @@ class GroupsController < Groups::ApplicationController end def group_params - params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level, :share_with_group_lock) + params.require(:group).permit(:name, :description, :path, :avatar, :public, :visibility_level, :share_with_group_lock, :request_access_enabled) end def load_events diff --git a/app/controllers/help_controller.rb b/app/controllers/help_controller.rb index d3dd98c8a4e..f7b44099b78 100644 --- a/app/controllers/help_controller.rb +++ b/app/controllers/help_controller.rb @@ -30,7 +30,7 @@ class HelpController < ApplicationController end # Allow access to images in the doc folder - format.any(:png, :gif, :jpeg) do + format.any(:png, :gif, :jpeg, :mp4) do # Note: We are purposefully NOT using `Rails.root.join` path = File.join(Rails.root, 'doc', "#{@path}.#{params[:format]}") diff --git a/app/controllers/projects/badges_controller.rb b/app/controllers/projects/badges_controller.rb index 824aa41db51..a9f482c8787 100644 --- a/app/controllers/projects/badges_controller.rb +++ b/app/controllers/projects/badges_controller.rb @@ -3,11 +3,6 @@ class Projects::BadgesController < Projects::ApplicationController before_action :authorize_admin_project!, only: [:index] before_action :no_cache_headers, except: [:index] - def index - @ref = params[:ref] || @project.default_branch || 'master' - @build_badge = Gitlab::Badge::Build.new(@project, @ref) - end - def build badge = Gitlab::Badge::Build.new(project, params[:ref]) diff --git a/app/controllers/projects/commit_controller.rb b/app/controllers/projects/commit_controller.rb index 727e84b40a1..7ae034f9398 100644 --- a/app/controllers/projects/commit_controller.rb +++ b/app/controllers/projects/commit_controller.rb @@ -115,11 +115,11 @@ class Projects::CommitController < Projects::ApplicationController end def define_note_vars - @grouped_diff_notes = commit.notes.grouped_diff_notes + @grouped_diff_discussions = commit.notes.grouped_diff_discussions @notes = commit.notes.non_diff_notes.fresh Banzai::NoteRenderer.render( - @grouped_diff_notes.values.flatten + @notes, + @grouped_diff_discussions.values.flat_map(&:notes) + @notes, @project, current_user, ) diff --git a/app/controllers/projects/compare_controller.rb b/app/controllers/projects/compare_controller.rb index 5f3ee71444d..8c004724f02 100644 --- a/app/controllers/projects/compare_controller.rb +++ b/app/controllers/projects/compare_controller.rb @@ -15,6 +15,7 @@ class Projects::CompareController < Projects::ApplicationController end def show + apply_diff_view_cookie! end def diff_for_path @@ -53,7 +54,7 @@ class Projects::CompareController < Projects::ApplicationController ) @diff_notes_disabled = true - @grouped_diff_notes = {} + @grouped_diff_discussions = {} end end diff --git a/app/controllers/projects/merge_requests_controller.rb b/app/controllers/projects/merge_requests_controller.rb index df659bb8c3b..594a61464b9 100644 --- a/app/controllers/projects/merge_requests_controller.rb +++ b/app/controllers/projects/merge_requests_controller.rb @@ -97,7 +97,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController else build_merge_request @diff_notes_disabled = true - @grouped_diff_notes = {} + @grouped_diff_discussions = {} end define_commit_vars @@ -286,6 +286,8 @@ class Projects::MergeRequestsController < Projects::ApplicationController status = pipeline.status coverage = pipeline.try(:coverage) + status = "success_with_warnings" if pipeline.success? && pipeline.has_warnings? + status ||= "preparing" else ci_service = @merge_request.source_project.ci_service @@ -376,7 +378,7 @@ class Projects::MergeRequestsController < Projects::ApplicationController # This is not executed lazily @notes = Banzai::NoteRenderer.render( - @discussions.flatten, + @discussions.flat_map(&:notes), @project, current_user, @path, @@ -402,10 +404,10 @@ class Projects::MergeRequestsController < Projects::ApplicationController } @use_legacy_diff_notes = !@merge_request.support_new_diff_notes? - @grouped_diff_notes = @merge_request.notes.grouped_diff_notes + @grouped_diff_discussions = @merge_request.notes.grouped_diff_discussions Banzai::NoteRenderer.render( - @grouped_diff_notes.values.flatten, + @grouped_diff_discussions.values.flat_map(&:notes), @project, current_user, @path, diff --git a/app/controllers/projects/notes_controller.rb b/app/controllers/projects/notes_controller.rb index 3eacdbbd067..766b7e9cf22 100644 --- a/app/controllers/projects/notes_controller.rb +++ b/app/controllers/projects/notes_controller.rb @@ -73,7 +73,7 @@ class Projects::NotesController < Projects::ApplicationController end alias_method :awardable, :note - def note_to_html(note) + def note_html(note) render_to_string( "projects/notes/_note", layout: false, @@ -82,20 +82,20 @@ class Projects::NotesController < Projects::ApplicationController ) end - def note_to_discussion_html(note) - return unless note.diff_note? + def diff_discussion_html(discussion) + return unless discussion.diff_discussion? if params[:view] == 'parallel' - template = "projects/notes/_diff_notes_with_reply_parallel" + template = "discussions/_parallel_diff_discussion" locals = if params[:line_type] == 'old' - { notes_left: [note], notes_right: [] } + { discussion_left: discussion, discussion_right: nil } else - { notes_left: [], notes_right: [note] } + { discussion_left: nil, discussion_right: discussion } end else - template = "projects/notes/_diff_notes_with_reply" - locals = { notes: [note] } + template = "discussions/_diff_discussion" + locals = { discussion: discussion } end render_to_string( @@ -106,14 +106,14 @@ class Projects::NotesController < Projects::ApplicationController ) end - def note_to_discussion_with_diff_html(note) - return unless note.diff_note? + def discussion_html(discussion) + return unless discussion.diff_discussion? render_to_string( - "projects/notes/_discussion", + "discussions/_discussion", layout: false, formats: [:html], - locals: { discussion_notes: [note] } + locals: { discussion: discussion } ) end @@ -132,26 +132,33 @@ class Projects::NotesController < Projects::ApplicationController valid: true, id: note.id, discussion_id: note.discussion_id, - html: note_to_html(note), + html: note_html(note), award: false, - note: note.note, - discussion_html: note_to_discussion_html(note), - discussion_with_diff_html: note_to_discussion_with_diff_html(note) + note: note.note } - # The discussion_id is used to add the comment to the correct discussion - # element on the merge request page. Among other things, the discussion_id - # contains the sha of head commit of the merge request. - # When new commits are pushed into the merge request after the initial - # load of the merge request page, the discussion elements will still have - # the old discussion_ids, with the old head commit sha. The new comment, - # however, will have the new discussion_id with the new commit sha. - # To ensure that these new comments will still end up in the correct - # discussion element, we also send the original discussion_id, with the - # old commit sha, along, and fall back on this value when no discussion - # element with the new discussion_id could be found. - if note.new_diff_note? && note.position != note.original_position - attrs[:original_discussion_id] = note.original_discussion_id + if note.diff_note? + discussion = Discussion.new([note]) + + attrs.merge!( + diff_discussion_html: diff_discussion_html(discussion), + discussion_html: discussion_html(discussion) + ) + + # The discussion_id is used to add the comment to the correct discussion + # element on the merge request page. Among other things, the discussion_id + # contains the sha of head commit of the merge request. + # When new commits are pushed into the merge request after the initial + # load of the merge request page, the discussion elements will still have + # the old discussion_ids, with the old head commit sha. The new comment, + # however, will have the new discussion_id with the new commit sha. + # To ensure that these new comments will still end up in the correct + # discussion element, we also send the original discussion_id, with the + # old commit sha, along, and fall back on this value when no discussion + # element with the new discussion_id could be found. + if note.new_diff_note? && note.position != note.original_position + attrs[:original_discussion_id] = note.original_discussion_id + end end attrs diff --git a/app/controllers/projects/pipelines_settings_controller.rb b/app/controllers/projects/pipelines_settings_controller.rb new file mode 100644 index 00000000000..85ba706e5cd --- /dev/null +++ b/app/controllers/projects/pipelines_settings_controller.rb @@ -0,0 +1,30 @@ +class Projects::PipelinesSettingsController < Projects::ApplicationController + before_action :authorize_admin_pipeline! + + def show + @ref = params[:ref] || @project.default_branch || 'master' + @build_badge = Gitlab::Badge::Build.new(@project, @ref) + end + + def update + if @project.update_attributes(update_params) + flash[:notice] = "CI/CD Pipelines settings for '#{@project.name}' were successfully updated." + redirect_to namespace_project_pipelines_settings_path(@project.namespace, @project) + else + render 'index' + end + end + + private + + def create_params + params.require(:pipeline).permit(:ref) + end + + def update_params + params.require(:project).permit( + :runners_token, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, + :public_builds + ) + end +end diff --git a/app/controllers/projects/refs_controller.rb b/app/controllers/projects/refs_controller.rb index d79f16e6a5a..3602b3d5e58 100644 --- a/app/controllers/projects/refs_controller.rb +++ b/app/controllers/projects/refs_controller.rb @@ -25,7 +25,7 @@ class Projects::RefsController < Projects::ApplicationController when "graphs_commits" commits_namespace_project_graph_path(@project.namespace, @project, @id) when "badges" - namespace_project_badges_path(@project.namespace, @project, ref: @id) + namespace_project_pipelines_settings_path(@project.namespace, @project, ref: @id) else namespace_project_commits_path(@project.namespace, @project, @id) end diff --git a/app/controllers/projects/services_controller.rb b/app/controllers/projects/services_controller.rb index 1b91882048e..6a227d85f6f 100644 --- a/app/controllers/projects/services_controller.rb +++ b/app/controllers/projects/services_controller.rb @@ -1,20 +1,5 @@ class Projects::ServicesController < Projects::ApplicationController - ALLOWED_PARAMS = [:title, :token, :type, :active, :api_key, :api_url, :api_version, :subdomain, - :room, :recipients, :project_url, :webhook, - :user_key, :device, :priority, :sound, :bamboo_url, :username, :password, - :build_key, :server, :teamcity_url, :drone_url, :build_type, - :description, :issues_url, :new_issue_url, :restrict_to_branch, :channel, - :colorize_messages, :channels, - :push_events, :issues_events, :merge_requests_events, :tag_push_events, - :note_events, :build_events, :wiki_page_events, - :notify_only_broken_builds, :add_pusher, - :send_from_committer_email, :disable_diffs, :external_wiki_url, - :notify, :color, - :server_host, :server_port, :default_irc_uri, :enable_ssl_verification, - :jira_issue_transition_id] - - # Parameters to ignore if no value is specified - FILTER_BLANK_PARAMS = [:password] + include ServiceParams # Authorize before_action :authorize_admin_project! @@ -33,7 +18,7 @@ class Projects::ServicesController < Projects::ApplicationController end def update - if @service.update_attributes(service_params) + if @service.update_attributes(service_params[:service]) redirect_to( edit_namespace_project_service_path(@project.namespace, @project, @service.to_param, notice: @@ -64,12 +49,4 @@ class Projects::ServicesController < Projects::ApplicationController def service @service ||= @project.services.find { |service| service.to_param == params[:id] } end - - def service_params - service_params = params.require(:service).permit(ALLOWED_PARAMS) - FILTER_BLANK_PARAMS.each do |param| - service_params.delete(param) if service_params[param].blank? - end - service_params - end end diff --git a/app/controllers/projects/uploads_controller.rb b/app/controllers/projects/uploads_controller.rb index caed064dfbc..e617be8f9fb 100644 --- a/app/controllers/projects/uploads_controller.rb +++ b/app/controllers/projects/uploads_controller.rb @@ -1,6 +1,6 @@ class Projects::UploadsController < Projects::ApplicationController skip_before_action :reject_blocked!, :project, - :repository, if: -> { action_name == 'show' && image? } + :repository, if: -> { action_name == 'show' && image_or_video? } before_action :authorize_upload_file!, only: [:create] @@ -24,7 +24,7 @@ class Projects::UploadsController < Projects::ApplicationController def show return render_404 if uploader.nil? || !uploader.file.exists? - disposition = uploader.image? ? 'inline' : 'attachment' + disposition = uploader.image_or_video? ? 'inline' : 'attachment' send_file uploader.file.path, disposition: disposition end @@ -49,7 +49,7 @@ class Projects::UploadsController < Projects::ApplicationController @uploader end - def image? - uploader && uploader.file.exists? && uploader.image? + def image_or_video? + uploader && uploader.file.exists? && uploader.image_or_video? end end diff --git a/app/controllers/projects_controller.rb b/app/controllers/projects_controller.rb index 4e5bcff9cf8..ec7a2e63b9a 100644 --- a/app/controllers/projects_controller.rb +++ b/app/controllers/projects_controller.rb @@ -296,7 +296,7 @@ class ProjectsController < Projects::ApplicationController :issues_tracker_id, :default_branch, :wiki_enabled, :visibility_level, :import_url, :last_activity_at, :namespace_id, :avatar, :builds_enabled, :build_allow_git_fetch, :build_timeout_in_minutes, :build_coverage_regex, - :public_builds, :only_allow_merge_if_build_succeeds + :public_builds, :only_allow_merge_if_build_succeeds, :request_access_enabled ) end diff --git a/app/helpers/avatars_helper.rb b/app/helpers/avatars_helper.rb new file mode 100644 index 00000000000..6ff40c6b461 --- /dev/null +++ b/app/helpers/avatars_helper.rb @@ -0,0 +1,30 @@ +module AvatarsHelper + + def author_avatar(commit_or_event, options = {}) + user_avatar(options.merge({ + user: commit_or_event.author, + user_name: commit_or_event.author_name, + user_email: commit_or_event.author_email, + })) + end + + private + + def user_avatar(options = {}) + avatar_size = options[:size] || 16 + user_name = options[:user].try(:name) || options[:user_name] + avatar = image_tag( + avatar_icon(options[:user] || options[:user_email], avatar_size), + class: "avatar has-tooltip hidden-xs s#{avatar_size}", + alt: "#{user_name}'s avatar", + title: user_name + ) + + if options[:user] + link_to(avatar, user_path(options[:user])) + elsif options[:user_email] + mail_to(options[:user_email], avatar) + end + end + +end diff --git a/app/helpers/ci_status_helper.rb b/app/helpers/ci_status_helper.rb index e6c99c9959e..ea2f5f9281a 100644 --- a/app/helpers/ci_status_helper.rb +++ b/app/helpers/ci_status_helper.rb @@ -15,8 +15,11 @@ module CiStatusHelper end def ci_label_for_status(status) - if status == 'success' + case status + when 'success' 'passed' + when 'success_with_warnings' + 'passed with warnings' else status end @@ -26,24 +29,26 @@ module CiStatusHelper icon_name = case status when 'success' - 'check' + 'icon_status_success' + when 'success_with_warnings' + 'icon_status_warning' when 'failed' - 'close' + 'icon_status_failed' when 'pending' - 'clock-o' + 'icon_status_pending' when 'running' - 'spinner' + 'icon_status_running' else - 'circle' + 'icon_status_cancel' end - icon(icon_name + ' fw') + custom_icon(icon_name) end - def render_commit_status(commit, tooltip_placement: 'auto left', cssclass: '') + def render_commit_status(commit, tooltip_placement: 'auto left') project = commit.project path = builds_namespace_project_commit_path(project.namespace, project, commit) - render_status_with_link('commit', commit.status, path, tooltip_placement, cssclass: cssclass) + render_status_with_link('commit', commit.status, path, tooltip_placement) end def render_pipeline_status(pipeline, tooltip_placement: 'auto left') diff --git a/app/helpers/commits_helper.rb b/app/helpers/commits_helper.rb index 474041eccbb..052ce56809e 100644 --- a/app/helpers/commits_helper.rb +++ b/app/helpers/commits_helper.rb @@ -16,16 +16,6 @@ module CommitsHelper commit_person_link(commit, options.merge(source: :committer)) end - def commit_author_avatar(commit, options = {}) - options = options.merge(source: :author) - user = commit.send(options[:source]) - - source_email = clean(commit.send "#{options[:source]}_email".to_sym) - person_email = user.try(:email) || source_email - - image_tag(avatar_icon(person_email, options[:size]), class: "avatar #{"s#{options[:size]}" if options[:size]} hidden-xs", width: options[:size], alt: "") - end - def image_diff_class(diff) if diff.deleted_file "deleted" diff --git a/app/helpers/diff_helper.rb b/app/helpers/diff_helper.rb index 75b029365f9..4c031942793 100644 --- a/app/helpers/diff_helper.rb +++ b/app/helpers/diff_helper.rb @@ -54,18 +54,20 @@ module DiffHelper end end - def organize_comments(left, right) - notes_left = notes_right = nil + def parallel_diff_discussions(left, right, diff_file) + discussion_left = discussion_right = nil - unless left[:type].nil? && right[:type] == 'new' - notes_left = @grouped_diff_notes[left[:line_code]] + if left && (left.unchanged? || left.removed?) + line_code = diff_file.line_code(left) + discussion_left = @grouped_diff_discussions[line_code] end - unless left[:type].nil? && right[:type].nil? - notes_right = @grouped_diff_notes[right[:line_code]] + if right && right.added? + line_code = diff_file.line_code(right) + discussion_right = @grouped_diff_discussions[line_code] end - [notes_left, notes_right] + [discussion_left, discussion_right] end def inline_diff_btn diff --git a/app/helpers/external_wiki_helper.rb b/app/helpers/external_wiki_helper.rb index 1f3401f2906..defd87d6bbe 100644 --- a/app/helpers/external_wiki_helper.rb +++ b/app/helpers/external_wiki_helper.rb @@ -1,8 +1,7 @@ module ExternalWikiHelper def get_project_wiki_path(project) - external_wiki_service = project.services. - find { |service| service.to_param == 'external_wiki' } - if external_wiki_service.present? && external_wiki_service.active? + external_wiki_service = project.external_wiki + if external_wiki_service external_wiki_service.properties['external_wiki_url'] else namespace_project_wiki_path(project.namespace, project, :home) diff --git a/app/helpers/notes_helper.rb b/app/helpers/notes_helper.rb index 98143dcee9b..0f60dd828ab 100644 --- a/app/helpers/notes_helper.rb +++ b/app/helpers/notes_helper.rb @@ -1,9 +1,4 @@ module NotesHelper - # Helps to distinguish e.g. commit notes in mr notes list - def note_for_main_target?(note) - @noteable.class.name == note.noteable_type && !note.diff_note? - end - def note_target_fields(note) if note.noteable hidden_field_tag(:target_type, note.noteable.class.name.underscore) + @@ -44,8 +39,8 @@ module NotesHelper # If we didn't, diff notes that would show for the same line on the changes # tab, would show in different discussions on the discussion tab. use_legacy_diff_note ||= begin - line_diff_notes = @grouped_diff_notes[line_code] - line_diff_notes && line_diff_notes.any?(&:legacy_diff_note?) + discussion = @grouped_diff_discussions[line_code] + discussion && discussion.legacy_diff_discussion? end data = { @@ -81,22 +76,10 @@ module NotesHelper data end - def link_to_reply_discussion(note, line_type = nil) + def link_to_reply_discussion(discussion, line_type = nil) return unless current_user - data = { - noteable_type: note.noteable_type, - noteable_id: note.noteable_id, - commit_id: note.commit_id, - discussion_id: note.discussion_id, - line_type: line_type - } - - if note.diff_note? - data[:note_type] = note.type - - data.merge!(note.diff_attributes) - end + data = discussion.reply_attributes.merge(line_type: line_type) content_tag(:div, class: "discussion-reply-holder") do button_tag 'Reply...', class: 'btn btn-text-field js-discussion-reply-button', @@ -114,13 +97,13 @@ module NotesHelper @max_access_by_user_id[full_key] end - def diff_note_path(note) - return unless note.diff_note? + def discussion_diff_path(discussion) + return unless discussion.diff_discussion? - if note.for_merge_request? && note.active? - diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) - elsif note.for_commit? - namespace_project_commit_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) + if discussion.for_merge_request? && discussion.active? + diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code) + elsif discussion.for_commit? + namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code) end end end diff --git a/app/helpers/search_helper.rb b/app/helpers/search_helper.rb index fcb2703e837..a2bba139c17 100644 --- a/app/helpers/search_helper.rb +++ b/app/helpers/search_helper.rb @@ -112,7 +112,8 @@ module SearchHelper search: params[:search], project_id: params[:project_id], group_id: params[:group_id], - scope: params[:scope] + scope: params[:scope], + repository_ref: params[:repository_ref] } options = exist_opts.merge(options) diff --git a/app/helpers/services_helper.rb b/app/helpers/services_helper.rb new file mode 100644 index 00000000000..2dd0bf5d71e --- /dev/null +++ b/app/helpers/services_helper.rb @@ -0,0 +1,25 @@ +module ServicesHelper + def service_event_description(event) + case event + when "push" + "Event will be triggered by a push to the repository" + when "tag_push" + "Event will be triggered when a new tag is pushed to the repository" + when "note" + "Event will be triggered when someone adds a comment" + when "issue" + "Event will be triggered when an issue is created/updated/merged" + when "merge_request" + "Event will be triggered when a merge request is created/updated/merged" + when "build" + "Event will be triggered when a build status changes" + when "wiki_page" + "Event will be triggered when a wiki page is created/updated" + end + end + + def service_event_field_name(event) + event = event.pluralize if %w[merge_request issue].include?(event) + "#{event}_events" + end +end diff --git a/app/helpers/time_helper.rb b/app/helpers/time_helper.rb index 8cb82c2d5cc..790001222f1 100644 --- a/app/helpers/time_helper.rb +++ b/app/helpers/time_helper.rb @@ -1,15 +1,6 @@ module TimeHelper - def duration_in_words(finished_at, started_at) - if finished_at && started_at - interval_in_seconds = finished_at.to_i - started_at.to_i - elsif started_at - interval_in_seconds = Time.now.to_i - started_at.to_i - end - - time_interval_in_words(interval_in_seconds) - end - def time_interval_in_words(interval_in_seconds) + interval_in_seconds = interval_in_seconds.to_i minutes = interval_in_seconds / 60 seconds = interval_in_seconds - minutes * 60 @@ -25,9 +16,19 @@ module TimeHelper end def duration_in_numbers(finished_at, started_at) - diff_in_seconds = finished_at.to_i - started_at.to_i - time_format = diff_in_seconds < 1.hour ? "%M:%S" : "%H:%M:%S" + interval = interval_in_seconds(started_at, finished_at) + time_format = interval < 1.hour ? "%M:%S" : "%H:%M:%S" - Time.at(diff_in_seconds).utc.strftime(time_format) + Time.at(interval).utc.strftime(time_format) + end + + private + + def interval_in_seconds(started_at, finished_at = nil) + if started_at && finished_at + finished_at.to_i - started_at.to_i + elsif started_at + Time.now.to_i - started_at.to_i + end end end diff --git a/app/models/ability.rb b/app/models/ability.rb index 6fd18f2ee24..f33c8d61d3f 100644 --- a/app/models/ability.rb +++ b/app/models/ability.rb @@ -172,7 +172,7 @@ class Ability rules << :read_build if project.public_builds? unless owner || project.team.member?(user) || project_group_member?(project, user) - rules << :request_access + rules << :request_access if project.request_access_enabled end end @@ -373,7 +373,7 @@ class Ability end if group.public? || (group.internal? && !user.external?) - rules << :request_access unless group.users.include?(user) + rules << :request_access if group.request_access_enabled && group.users.exclude?(user) end rules.flatten diff --git a/app/models/application_setting.rb b/app/models/application_setting.rb index c6f77cc055f..8c19d9dc9c8 100644 --- a/app/models/application_setting.rb +++ b/app/models/application_setting.rb @@ -4,12 +4,20 @@ class ApplicationSetting < ActiveRecord::Base add_authentication_token_field :health_check_access_token CACHE_KEY = 'application_setting.last' + DOMAIN_LIST_SEPARATOR = %r{\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace + | # or + \s # any whitespace character + | # or + [\r\n] # any number of newline characters + }x serialize :restricted_visibility_levels serialize :import_sources serialize :disabled_oauth_sign_in_sources, Array - serialize :restricted_signup_domains, Array - attr_accessor :restricted_signup_domains_raw + serialize :domain_whitelist, Array + serialize :domain_blacklist, Array + + attr_accessor :domain_whitelist_raw, :domain_blacklist_raw validates :session_expire_delay, presence: true, @@ -62,6 +70,10 @@ class ApplicationSetting < ActiveRecord::Base validates :enabled_git_access_protocol, inclusion: { in: %w(ssh http), allow_blank: true, allow_nil: true } + validates :domain_blacklist, + presence: { message: 'Domain blacklist cannot be empty if Blacklist is enabled.' }, + if: :domain_blacklist_enabled? + validates_each :restricted_visibility_levels do |record, attr, value| unless value.nil? value.each do |level| @@ -129,7 +141,7 @@ class ApplicationSetting < ActiveRecord::Base session_expire_delay: Settings.gitlab['session_expire_delay'], default_project_visibility: Settings.gitlab.default_projects_features['visibility_level'], default_snippet_visibility: Settings.gitlab.default_projects_features['visibility_level'], - restricted_signup_domains: Settings.gitlab['restricted_signup_domains'], + domain_whitelist: Settings.gitlab['domain_whitelist'], import_sources: %w[github bitbucket gitlab gitorious google_code fogbugz git gitlab_project], shared_runners_enabled: Settings.gitlab_ci['shared_runners_enabled'], max_artifacts_size: Settings.artifacts['max_size'], @@ -150,20 +162,30 @@ class ApplicationSetting < ActiveRecord::Base ActiveRecord::Base.connection.column_exists?(:application_settings, :home_page_url) end - def restricted_signup_domains_raw - self.restricted_signup_domains.join("\n") unless self.restricted_signup_domains.nil? + def domain_whitelist_raw + self.domain_whitelist.join("\n") unless self.domain_whitelist.nil? + end + + def domain_blacklist_raw + self.domain_blacklist.join("\n") unless self.domain_blacklist.nil? + end + + def domain_whitelist_raw=(values) + self.domain_whitelist = [] + self.domain_whitelist = values.split(DOMAIN_LIST_SEPARATOR) + self.domain_whitelist.reject! { |d| d.empty? } + self.domain_whitelist + end + + def domain_blacklist_raw=(values) + self.domain_blacklist = [] + self.domain_blacklist = values.split(DOMAIN_LIST_SEPARATOR) + self.domain_blacklist.reject! { |d| d.empty? } + self.domain_blacklist end - def restricted_signup_domains_raw=(values) - self.restricted_signup_domains = [] - self.restricted_signup_domains = values.split( - /\s*[,;]\s* # comma or semicolon, optionally surrounded by whitespace - | # or - \s # any whitespace character - | # or - [\r\n] # any number of newline characters - /x) - self.restricted_signup_domains.reject! { |d| d.empty? } + def domain_blacklist_file=(file) + self.domain_blacklist_raw = file.read end def runners_registration_token diff --git a/app/models/ci/build.rb b/app/models/ci/build.rb index e02351ce339..cbfa14e81f1 100644 --- a/app/models/ci/build.rb +++ b/app/models/ci/build.rb @@ -12,7 +12,7 @@ module Ci scope :unstarted, ->() { where(runner_id: nil) } scope :ignore_failures, ->() { where(allow_failure: false) } - scope :with_artifacts, ->() { where.not(artifacts_file: nil) } + scope :with_artifacts, ->() { where.not(artifacts_file: [nil, '']) } scope :with_expired_artifacts, ->() { with_artifacts.where('artifacts_expire_at < ?', Time.now) } scope :last_month, ->() { where('created_at > ?', Date.today - 1.month) } scope :manual_actions, ->() { where(when: :manual) } @@ -97,7 +97,7 @@ module Ci end def other_actions - pipeline.manual_actions.where.not(id: self) + pipeline.manual_actions.where.not(name: name) end def playable? @@ -145,7 +145,15 @@ module Ci end def variables - predefined_variables + yaml_variables + project_variables + trigger_variables + variables = predefined_variables + variables += project.predefined_variables + variables += pipeline.predefined_variables + variables += runner.predefined_variables if runner + variables += project.container_registry_variables + variables += yaml_variables + variables += project.secret_variables + variables += trigger_request.user_variables if trigger_request + variables end def merge_request @@ -430,28 +438,23 @@ module Ci self.update(erased_by: user, erased_at: Time.now, artifacts_expire_at: nil) end - def project_variables - project.variables.map do |variable| - { key: variable.key, value: variable.value, public: false } - end - end - - def trigger_variables - if trigger_request && trigger_request.variables - trigger_request.variables.map do |key, value| - { key: key, value: value, public: false } - end - else - [] - end - end - def predefined_variables - variables = [] - variables << { key: :CI_BUILD_TAG, value: ref, public: true } if tag? - variables << { key: :CI_BUILD_NAME, value: name, public: true } - variables << { key: :CI_BUILD_STAGE, value: stage, public: true } - variables << { key: :CI_BUILD_TRIGGERED, value: 'true', public: true } if trigger_request + variables = [ + { key: 'CI', value: 'true', public: true }, + { key: 'GITLAB_CI', value: 'true', public: true }, + { key: 'CI_BUILD_ID', value: id.to_s, public: true }, + { key: 'CI_BUILD_TOKEN', value: token, public: false }, + { key: 'CI_BUILD_REF', value: sha, public: true }, + { key: 'CI_BUILD_BEFORE_SHA', value: before_sha, public: true }, + { key: 'CI_BUILD_REF_NAME', value: ref, public: true }, + { key: 'CI_BUILD_NAME', value: name, public: true }, + { key: 'CI_BUILD_STAGE', value: stage, public: true }, + { key: 'CI_SERVER_NAME', value: 'GitLab', public: true }, + { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true }, + { key: 'CI_SERVER_REVISION', value: Gitlab::REVISION, public: true } + ] + variables << { key: 'CI_BUILD_TAG', value: ref, public: true } if tag? + variables << { key: 'CI_BUILD_TRIGGERED', value: 'true', public: true } if trigger_request variables end diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index aca8607f4e8..bce6a992af6 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -20,6 +20,11 @@ module Ci after_touch :update_state after_save :keep_around_commits + # ref can't be HEAD or SHA, can only be branch/tag name + scope :latest_successful_for, ->(ref = default_branch) do + where(ref: ref).success.order(id: :desc).limit(1) + end + def self.truncate_sha(sha) sha[0...8] end @@ -146,6 +151,10 @@ module Ci end end + def has_warnings? + builds.latest.ignored.any? + end + def config_processor return nil unless ci_yaml_file return @config_processor if defined?(@config_processor) @@ -198,6 +207,12 @@ module Ci Note.for_commit_id(sha) end + def predefined_variables + [ + { key: 'CI_PIPELINE_ID', value: id.to_s, public: true } + ] + end + private def build_builds_for_stages(stages, user, status, trigger_request) @@ -206,8 +221,9 @@ module Ci # build builds only for the first stage that has builds available. # stages.any? do |stage| - CreateBuildsService.new(self) - .execute(stage, user, status, trigger_request).present? + CreateBuildsService.new(self). + execute(stage, user, status, trigger_request). + any?(&:active?) end end @@ -226,7 +242,7 @@ module Ci def keep_around_commits return unless project - + project.repository.keep_around(self.sha) project.repository.keep_around(self.before_sha) end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index b64ec79ec2b..49f05f881a2 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -114,6 +114,14 @@ module Ci tag_list.any? end + def predefined_variables + [ + { key: 'CI_RUNNER_ID', value: id.to_s, public: true }, + { key: 'CI_RUNNER_DESCRIPTION', value: description, public: true }, + { key: 'CI_RUNNER_TAGS', value: tag_list.to_s, public: true } + ] + end + private def tag_constraints diff --git a/app/models/ci/trigger_request.rb b/app/models/ci/trigger_request.rb index fcf2b6dc5e2..fc674871743 100644 --- a/app/models/ci/trigger_request.rb +++ b/app/models/ci/trigger_request.rb @@ -7,5 +7,13 @@ module Ci has_many :builds, class_name: 'Ci::Build' serialize :variables + + def user_variables + return [] unless variables + + variables.map do |key, value| + { key: key, value: value, public: false } + end + end end end diff --git a/app/models/commit_status.rb b/app/models/commit_status.rb index 535db26240a..2d185c28809 100644 --- a/app/models/commit_status.rb +++ b/app/models/commit_status.rb @@ -16,7 +16,11 @@ class CommitStatus < ActiveRecord::Base alias_attribute :author, :user - scope :latest, -> { where(id: unscope(:select).select('max(id)').group(:name, :commit_id)) } + scope :latest, -> do + max_id = unscope(:select).select("max(#{quoted_table_name}.id)") + + where(id: max_id.group(:name, :commit_id)) + end scope :retried, -> { where.not(id: latest) } scope :ordered, -> { order(:name) } scope :ignored, -> { where(allow_failure: true, status: [:failed, :canceled]) } diff --git a/app/models/concerns/note_on_diff.rb b/app/models/concerns/note_on_diff.rb index 2785fbb21c9..4be6a2f621b 100644 --- a/app/models/concerns/note_on_diff.rb +++ b/app/models/concerns/note_on_diff.rb @@ -1,12 +1,6 @@ module NoteOnDiff extend ActiveSupport::Concern - NUMBER_OF_TRUNCATED_DIFF_LINES = 16 - - included do - delegate :blob, :highlighted_diff_lines, to: :diff_file, allow_nil: true - end - def diff_note? true end @@ -30,23 +24,4 @@ module NoteOnDiff def can_be_award_emoji? false end - - # Returns an array of at most 16 highlighted lines above a diff note - def truncated_diff_lines - prev_lines = [] - - highlighted_diff_lines.each do |line| - if line.meta? - prev_lines.clear - else - prev_lines << line - - break if for_line?(line) - - prev_lines.shift if prev_lines.length >= NUMBER_OF_TRUNCATED_DIFF_LINES - end - end - - prev_lines - end end diff --git a/app/models/discussion.rb b/app/models/discussion.rb new file mode 100644 index 00000000000..74facfd1c9c --- /dev/null +++ b/app/models/discussion.rb @@ -0,0 +1,91 @@ +class Discussion + NUMBER_OF_TRUNCATED_DIFF_LINES = 16 + + attr_reader :first_note, :notes + + delegate :created_at, + :project, + :author, + + :noteable, + :for_commit?, + :for_merge_request?, + + :line_code, + :diff_file, + :for_line?, + :active?, + + to: :first_note + + delegate :blob, :highlighted_diff_lines, to: :diff_file, allow_nil: true + + def self.for_notes(notes) + notes.group_by(&:discussion_id).values.map { |notes| new(notes) } + end + + def self.for_diff_notes(notes) + notes.group_by(&:line_code).values.map { |notes| new(notes) } + end + + def initialize(notes) + @first_note = notes.first + @notes = notes + end + + def id + first_note.discussion_id + end + + def diff_discussion? + first_note.diff_note? + end + + def legacy_diff_discussion? + notes.any?(&:legacy_diff_note?) + end + + def for_target?(target) + self.noteable == target && !diff_discussion? + end + + def expanded? + !diff_discussion? || active? + end + + def reply_attributes + data = { + noteable_type: first_note.noteable_type, + noteable_id: first_note.noteable_id, + commit_id: first_note.commit_id, + discussion_id: self.id, + } + + if diff_discussion? + data[:note_type] = first_note.type + + data.merge!(first_note.diff_attributes) + end + + data + end + + # Returns an array of at most 16 highlighted lines above a diff note + def truncated_diff_lines + prev_lines = [] + + highlighted_diff_lines.each do |line| + if line.meta? + prev_lines.clear + else + prev_lines << line + + break if for_line?(line) + + prev_lines.shift if prev_lines.length >= NUMBER_OF_TRUNCATED_DIFF_LINES + end + end + + prev_lines + end +end diff --git a/app/models/issue.rb b/app/models/issue.rb index 60abd47409e..60af8c15340 100644 --- a/app/models/issue.rb +++ b/app/models/issue.rb @@ -52,10 +52,50 @@ class Issue < ActiveRecord::Base attributes end + class << self + private + + # Returns the project that the current scope belongs to if any, nil otherwise. + # + # Examples: + # - my_project.issues.without_due_date.owner_project => my_project + # - Issue.all.owner_project => nil + def owner_project + # No owner if we're not being called from an association + return unless all.respond_to?(:proxy_association) + + owner = all.proxy_association.owner + + # Check if the association is or belongs to a project + if owner.is_a?(Project) + owner + else + begin + owner.association(:project).target + rescue ActiveRecord::AssociationNotFoundError + nil + end + end + end + end + def self.visible_to_user(user) return where('issues.confidential IS NULL OR issues.confidential IS FALSE') if user.blank? return all if user.admin? + # Check if we are scoped to a specific project's issues + if owner_project + if owner_project.authorized_for_user?(user, Gitlab::Access::REPORTER) + # If the project is authorized for the user, they can see all issues in the project + return all + else + # else only non confidential and authored/assigned to them + return where('issues.confidential IS NULL OR issues.confidential IS FALSE + OR issues.author_id = :user_id OR issues.assignee_id = :user_id', + user_id: user.id) + end + end + where(' issues.confidential IS NULL OR issues.confidential IS FALSE diff --git a/app/models/note.rb b/app/models/note.rb index 0ce10c77de9..9b0a7211b4e 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -82,11 +82,12 @@ class Note < ActiveRecord::Base end def discussions - all.group_by(&:discussion_id).values + Discussion.for_notes(all) end - def grouped_diff_notes - diff_notes.select(&:active?).sort_by(&:created_at).group_by(&:line_code) + def grouped_diff_discussions + notes = diff_notes.fresh.select(&:active?) + Discussion.for_diff_notes(notes).map { |d| [d.line_code, d] }.to_h end # Searches for notes matching the given query. diff --git a/app/models/project.rb b/app/models/project.rb index a805f5d97bc..5452d9f768f 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -429,6 +429,17 @@ class Project < ActiveRecord::Base repository.commit(ref) end + # ref can't be HEAD, can only be branch/tag name or SHA + def latest_successful_builds_for(ref = default_branch) + latest_pipeline = pipelines.latest_successful_for(ref).first + + if latest_pipeline + latest_pipeline.builds.latest.with_artifacts + else + builds.none + end + end + def merge_base_commit(first_commit_id, second_commit_id) sha = repository.merge_base(first_commit_id, second_commit_id) repository.commit(sha) if sha @@ -650,6 +661,22 @@ class Project < ActiveRecord::Base update_column(:has_external_issue_tracker, services.external_issue_trackers.any?) end + def external_wiki + if has_external_wiki.nil? + cache_has_external_wiki # Populate + end + + if has_external_wiki + @external_wiki ||= services.external_wikis.first + else + nil + end + end + + def cache_has_external_wiki + update_column(:has_external_wiki, services.external_wikis.any?) + end + def build_missing_services services_templates = Service.where(template: true) @@ -1164,4 +1191,74 @@ class Project < ActiveRecord::Base def ensure_dir_exist gitlab_shell.add_namespace(repository_storage_path, namespace.path) end + + def predefined_variables + [ + { key: 'CI_PROJECT_ID', value: id.to_s, public: true }, + { key: 'CI_PROJECT_NAME', value: path, public: true }, + { key: 'CI_PROJECT_PATH', value: path_with_namespace, public: true }, + { key: 'CI_PROJECT_NAMESPACE', value: namespace.path, public: true }, + { key: 'CI_PROJECT_URL', value: web_url, public: true } + ] + end + + def container_registry_variables + return [] unless Gitlab.config.registry.enabled + + variables = [ + { key: 'CI_REGISTRY', value: Gitlab.config.registry.host_port, public: true } + ] + + if container_registry_enabled? + variables << { key: 'CI_REGISTRY_IMAGE', value: container_registry_repository_url, public: true } + end + + variables + end + + def secret_variables + variables.map do |variable| + { key: variable.key, value: variable.value, public: false } + end + end + + # Checks if `user` is authorized for this project, with at least the + # `min_access_level` (if given). + # + # If you change the logic of this method, please also update `User#authorized_projects` + def authorized_for_user?(user, min_access_level = nil) + return false unless user + + return true if personal? && namespace_id == user.namespace_id + + authorized_for_user_by_group?(user, min_access_level) || + authorized_for_user_by_members?(user, min_access_level) || + authorized_for_user_by_shared_projects?(user, min_access_level) + end + + private + + def authorized_for_user_by_group?(user, min_access_level) + member = user.group_members.find_by(source_id: group) + + member && (!min_access_level || member.access_level >= min_access_level) + end + + def authorized_for_user_by_members?(user, min_access_level) + member = members.find_by(user_id: user) + + member && (!min_access_level || member.access_level >= min_access_level) + end + + def authorized_for_user_by_shared_projects?(user, min_access_level) + shared_projects = user.group_members.joins(group: :shared_projects). + where(project_group_links: { project_id: self }) + + if min_access_level + members_scope = { access_level: Gitlab::Access.values.select { |access| access >= min_access_level } } + shared_projects = shared_projects.where(members: members_scope) + end + + shared_projects.any? + end end diff --git a/app/models/project_services/slack_service.rb b/app/models/project_services/slack_service.rb index cf9e4d5a8b6..abbc780dc1a 100644 --- a/app/models/project_services/slack_service.rb +++ b/app/models/project_services/slack_service.rb @@ -4,6 +4,9 @@ class SlackService < Service validates :webhook, presence: true, url: true, if: :activated? def initialize_properties + # Custom serialized properties initialization + self.supported_events.each { |event| self.class.prop_accessor(event_channel_name(event)) } + if properties.nil? self.properties = {} self.notify_only_broken_builds = true @@ -29,13 +32,15 @@ class SlackService < Service end def fields - [ - { type: 'text', name: 'webhook', - placeholder: 'https://hooks.slack.com/services/...' }, - { type: 'text', name: 'username', placeholder: 'username' }, - { type: 'text', name: 'channel', placeholder: '#channel' }, - { type: 'checkbox', name: 'notify_only_broken_builds' }, - ] + default_fields = + [ + { type: 'text', name: 'webhook', placeholder: 'https://hooks.slack.com/services/...' }, + { type: 'text', name: 'username', placeholder: 'username' }, + { type: 'text', name: 'channel', placeholder: "#general" }, + { type: 'checkbox', name: 'notify_only_broken_builds' }, + ] + + default_fields + build_event_channels end def supported_events @@ -74,7 +79,10 @@ class SlackService < Service end opt = {} - opt[:channel] = channel if channel + + event_channel = get_channel_field(object_kind) || channel + + opt[:channel] = event_channel if event_channel opt[:username] = username if username if message @@ -83,8 +91,35 @@ class SlackService < Service end end + def event_channel_names + supported_events.map { |event| event_channel_name(event) } + end + + def event_field(event) + fields.find { |field| field[:name] == event_channel_name(event) } + end + + def global_fields + fields.reject { |field| field[:name].end_with?('channel') } + end + private + def get_channel_field(event) + field_name = event_channel_name(event) + self.public_send(field_name) + end + + def build_event_channels + supported_events.reduce([]) do |channels, event| + channels << { type: 'text', name: event_channel_name(event), placeholder: "#general" } + end + end + + def event_channel_name(event) + "#{event}_channel" + end + def project_name project.name_with_namespace.gsub(/\s/, '') end diff --git a/app/models/project_team.rb b/app/models/project_team.rb index 0b700930641..9d312a53790 100644 --- a/app/models/project_team.rb +++ b/app/models/project_team.rb @@ -173,7 +173,7 @@ class ProjectTeam invited_members = [] if project.invited_groups.any? && project.allowed_to_share_with_group? - project.project_group_links.each do |group_link| + project.project_group_links.includes(group: [:group_members]).each do |group_link| invited_group = group_link.group im = invited_group.members diff --git a/app/models/repository.rb b/app/models/repository.rb index 61f6d52dfd3..d94bb0711fb 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -11,16 +11,6 @@ class Repository attr_accessor :path_with_namespace, :project - def self.clean_old_archives - Gitlab::Metrics.measure(:clean_old_archives) do - repository_downloads_path = Gitlab.config.gitlab.repository_downloads_path - - return unless File.directory?(repository_downloads_path) - - Gitlab::Popen.popen(%W(find #{repository_downloads_path} -not -path #{repository_downloads_path} -mmin +120 -delete)) - end - end - def initialize(path_with_namespace, project) @path_with_namespace = path_with_namespace @project = project @@ -80,7 +70,12 @@ class Repository def commit(ref = 'HEAD') return nil unless exists? - commit = Gitlab::Git::Commit.find(raw_repository, ref) + commit = + if ref.is_a?(Gitlab::Git::Commit) + ref + else + Gitlab::Git::Commit.find(raw_repository, ref) + end commit = ::Commit.new(commit, @project) if commit commit rescue Rugged::OdbError @@ -216,11 +211,20 @@ class Repository return if kept_around?(sha) - rugged.references.create(keep_around_ref_name(sha), sha) + # This will still fail if the file is corrupted (e.g. 0 bytes) + begin + rugged.references.create(keep_around_ref_name(sha), sha, force: true) + rescue Rugged::ReferenceError => ex + Rails.logger.error "Unable to create keep-around reference for repository #{path}: #{ex}" + end end def kept_around?(sha) - ref_exists?(keep_around_ref_name(sha)) + begin + ref_exists?(keep_around_ref_name(sha)) + rescue Rugged::ReferenceError + false + end end def tag_names @@ -257,10 +261,10 @@ class Repository # Rugged seems to throw a `ReferenceError` when given branch_names rather # than SHA-1 hashes number_commits_behind = raw_repository. - count_commits_between(branch.target, root_ref_hash) + count_commits_between(branch.target.sha, root_ref_hash) number_commits_ahead = raw_repository. - count_commits_between(root_ref_hash, branch.target) + count_commits_between(root_ref_hash, branch.target.sha) { behind: number_commits_behind, ahead: number_commits_ahead } end @@ -392,6 +396,11 @@ class Repository expire_cache if exists? + # expire cache that don't depend on repository data (when expiring) + expire_tags_cache + expire_tag_count_cache + expire_branches_cache + expire_branch_count_cache expire_root_ref_cache expire_emptiness_caches expire_exists_cache @@ -681,9 +690,7 @@ class Repository end def local_branches - @local_branches ||= rugged.branches.each(:local).map do |branch| - Gitlab::Git::Branch.new(branch.name, branch.target) - end + @local_branches ||= raw_repository.local_branches end alias_method :branches, :local_branches @@ -824,7 +831,7 @@ class Repository end def revert(user, commit, base_branch, revert_tree_id = nil) - source_sha = find_branch(base_branch).target + source_sha = find_branch(base_branch).target.sha revert_tree_id ||= check_revert_content(commit, base_branch) return false unless revert_tree_id @@ -841,7 +848,7 @@ class Repository end def cherry_pick(user, commit, base_branch, cherry_pick_tree_id = nil) - source_sha = find_branch(base_branch).target + source_sha = find_branch(base_branch).target.sha cherry_pick_tree_id ||= check_cherry_pick_content(commit, base_branch) return false unless cherry_pick_tree_id @@ -862,7 +869,7 @@ class Repository end def check_revert_content(commit, base_branch) - source_sha = find_branch(base_branch).target + source_sha = find_branch(base_branch).target.sha args = [commit.id, source_sha] args << { mainline: 1 } if commit.merge_commit? @@ -876,7 +883,7 @@ class Repository end def check_cherry_pick_content(commit, base_branch) - source_sha = find_branch(base_branch).target + source_sha = find_branch(base_branch).target.sha args = [commit.id, source_sha] args << 1 if commit.merge_commit? @@ -1041,7 +1048,7 @@ class Repository end def tags_sorted_by_committed_date - tags.sort_by { |tag| commit(tag.target).committed_date } + tags.sort_by { |tag| tag.target.committed_date } end def keep_around_ref_name(sha) diff --git a/app/models/service.rb b/app/models/service.rb index 5432f8c7ab4..40cd9b861f0 100644 --- a/app/models/service.rb +++ b/app/models/service.rb @@ -17,6 +17,7 @@ class Service < ActiveRecord::Base after_commit :reset_updated_properties after_commit :cache_project_has_external_issue_tracker + after_commit :cache_project_has_external_wiki belongs_to :project, inverse_of: :services has_one :service_hook @@ -25,6 +26,7 @@ class Service < ActiveRecord::Base scope :visible, -> { where.not(type: ['GitlabIssueTrackerService', 'GitlabCiService']) } scope :issue_trackers, -> { where(category: 'issue_tracker') } + scope :external_wikis, -> { where(type: 'ExternalWikiService').active } scope :active, -> { where(active: true) } scope :without_defaults, -> { where(default: false) } @@ -80,6 +82,18 @@ class Service < ActiveRecord::Base Gitlab::PushDataBuilder.build_sample(project, user) end + def event_channel_names + [] + end + + def event_field(event) + nil + end + + def global_fields + fields + end + def supported_events %w(push tag_push issue merge_request wiki_page) end @@ -212,4 +226,10 @@ class Service < ActiveRecord::Base project.cache_has_external_issue_tracker end end + + def cache_project_has_external_wiki + if project && !project.destroyed? + project.cache_has_external_wiki + end + end end diff --git a/app/models/user.rb b/app/models/user.rb index 3d0a033785c..db747434959 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -111,7 +111,7 @@ class User < ActiveRecord::Base validates :avatar, file_size: { maximum: 200.kilobytes.to_i } before_validation :generate_password, on: :create - before_validation :restricted_signup_domains, on: :create + before_validation :signup_domain_valid?, on: :create before_validation :sanitize_attrs before_validation :set_notification_email, if: ->(user) { user.email_changed? } before_validation :set_public_email, if: ->(user) { user.public_email_changed? } @@ -412,6 +412,8 @@ class User < ActiveRecord::Base end # Returns projects user is authorized to access. + # + # If you change the logic of this method, please also update `Project#authorized_for_user` def authorized_projects(min_access_level = nil) Project.where("projects.id IN (#{projects_union(min_access_level).to_sql})") end @@ -760,29 +762,6 @@ class User < ActiveRecord::Base Project.where(id: events) end - def restricted_signup_domains - email_domains = current_application_settings.restricted_signup_domains - - unless email_domains.blank? - match_found = email_domains.any? do |domain| - escaped = Regexp.escape(domain).gsub('\*', '.*?') - regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE - email_domain = Mail::Address.new(self.email).domain - email_domain =~ regexp - end - - unless match_found - self.errors.add :email, - 'is not whitelisted. ' + - 'Email domains valid for registration are: ' + - email_domains.join(', ') - return false - end - end - - true - end - def can_be_removed? !solo_owned_groups.present? end @@ -854,7 +833,7 @@ class User < ActiveRecord::Base groups.joins(:shared_projects).select(:project_id)] if min_access_level - scope = { access_level: Gitlab::Access.values.select { |access| access >= min_access_level } } + scope = { access_level: Gitlab::Access.all_values.select { |access| access >= min_access_level } } relations = [relations.shift] + relations.map { |relation| relation.where(members: scope) } end @@ -881,4 +860,40 @@ class User < ActiveRecord::Base self.can_create_group = false self.projects_limit = 0 end + + def signup_domain_valid? + valid = true + error = nil + + if current_application_settings.domain_blacklist_enabled? + blocked_domains = current_application_settings.domain_blacklist + if domain_matches?(blocked_domains, self.email) + error = 'is not from an allowed domain.' + valid = false + end + end + + allowed_domains = current_application_settings.domain_whitelist + unless allowed_domains.blank? + if domain_matches?(allowed_domains, self.email) + valid = true + else + error = "is not whitelisted. Email domains valid for registration are: #{allowed_domains.join(', ')}" + valid = false + end + end + + self.errors.add(:email, error) unless valid + + valid + end + + def domain_matches?(email_domains, email) + signup_domain = Mail::Address.new(email).domain + email_domains.any? do |domain| + escaped = Regexp.escape(domain).gsub('\*', '.*?') + regexp = Regexp.new "^#{escaped}$", Regexp::IGNORECASE + signup_domain =~ regexp + end + end end diff --git a/app/services/delete_branch_service.rb b/app/services/delete_branch_service.rb index 332c55581a1..87f066edb6f 100644 --- a/app/services/delete_branch_service.rb +++ b/app/services/delete_branch_service.rb @@ -40,6 +40,6 @@ class DeleteBranchService < BaseService def build_push_data(branch) Gitlab::PushDataBuilder - .build(project, current_user, branch.target, Gitlab::Git::BLANK_SHA, "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}", []) + .build(project, current_user, branch.target.sha, Gitlab::Git::BLANK_SHA, "#{Gitlab::Git::BRANCH_REF_PREFIX}#{branch.name}", []) end end diff --git a/app/services/delete_tag_service.rb b/app/services/delete_tag_service.rb index 1e41fbe34b6..32e0eed6b63 100644 --- a/app/services/delete_tag_service.rb +++ b/app/services/delete_tag_service.rb @@ -34,6 +34,6 @@ class DeleteTagService < BaseService def build_push_data(tag) Gitlab::PushDataBuilder - .build(project, current_user, tag.target, Gitlab::Git::BLANK_SHA, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", []) + .build(project, current_user, tag.target.sha, Gitlab::Git::BLANK_SHA, "#{Gitlab::Git::TAG_REF_PREFIX}#{tag.name}", []) end end diff --git a/app/services/git_tag_push_service.rb b/app/services/git_tag_push_service.rb index 58573078048..969530c4fdc 100644 --- a/app/services/git_tag_push_service.rb +++ b/app/services/git_tag_push_service.rb @@ -26,8 +26,8 @@ class GitTagPushService < BaseService unless Gitlab::Git.blank_ref?(params[:newrev]) tag_name = Gitlab::Git.ref_name(params[:ref]) tag = project.repository.find_tag(tag_name) - - if tag && tag.target == params[:newrev] + + if tag && tag.object_sha == params[:newrev] commit = project.commit(tag.target) commits = [commit].compact message = tag.message diff --git a/app/services/repository_archive_clean_up_service.rb b/app/services/repository_archive_clean_up_service.rb new file mode 100644 index 00000000000..0b56b09738d --- /dev/null +++ b/app/services/repository_archive_clean_up_service.rb @@ -0,0 +1,33 @@ +class RepositoryArchiveCleanUpService + LAST_MODIFIED_TIME_IN_MINUTES = 120 + + def initialize(mmin = LAST_MODIFIED_TIME_IN_MINUTES) + @mmin = mmin + @path = Gitlab.config.gitlab.repository_downloads_path + end + + def execute + Gitlab::Metrics.measure(:repository_archive_clean_up) do + return unless File.directory?(path) + + clean_up_old_archives + clean_up_empty_directories + end + end + + private + + attr_reader :mmin, :path + + def clean_up_old_archives + run(%W(find #{path} -not -path #{path} -type f \( -name \*.tar -o -name \*.bz2 -o -name \*.tar.gz -o -name \*.zip \) -maxdepth 2 -mmin +#{mmin} -delete)) + end + + def clean_up_empty_directories + run(%W(find #{path} -not -path #{path} -type d -empty -name \*.git -maxdepth 1 -delete)) + end + + def run(cmd) + Gitlab::Popen.popen(cmd) + end +end diff --git a/app/uploaders/file_uploader.rb b/app/uploaders/file_uploader.rb index 1af9e9b0edb..2f5f49f7de7 100644 --- a/app/uploaders/file_uploader.rb +++ b/app/uploaders/file_uploader.rb @@ -33,16 +33,15 @@ class FileUploader < CarrierWave::Uploader::Base end def to_h - filename = image? ? self.file.basename : self.file.filename + filename = image_or_video? ? self.file.basename : self.file.filename escaped_filename = filename.gsub("]", "\\]") markdown = "[#{escaped_filename}](#{self.secure_url})" - markdown.prepend("!") if image? + markdown.prepend("!") if image_or_video? { alt: filename, url: self.secure_url, - is_image: image?, markdown: markdown } end diff --git a/app/uploaders/uploader_helper.rb b/app/uploaders/uploader_helper.rb index 5ef440f3367..b10ad71d052 100644 --- a/app/uploaders/uploader_helper.rb +++ b/app/uploaders/uploader_helper.rb @@ -1,16 +1,37 @@ # Extra methods for uploader module UploaderHelper + IMAGE_EXT = %w[png jpg jpeg gif bmp tiff] + # We recommend using the .mp4 format over .mov. Videos in .mov format can + # still be used but you really need to make sure they are served with the + # proper MIME type video/mp4 and not video/quicktime or your videos won't play + # on IE >= 9. + # http://archive.sublimevideo.info/20150912/docs.sublimevideo.net/troubleshooting.html + VIDEO_EXT = %w[mp4 m4v mov webm ogv] + def image? - img_ext = %w(png jpg jpeg gif bmp tiff) - if file.respond_to?(:extension) - img_ext.include?(file.extension.downcase) - else - # Not all CarrierWave storages respond to :extension - ext = file.path.split('.').last.downcase - img_ext.include?(ext) - end - rescue - false + extension_match?(IMAGE_EXT) + end + + def video? + extension_match?(VIDEO_EXT) + end + + def image_or_video? + image? || video? + end + + def extension_match?(extensions) + return false unless file + + extension = + if file.respond_to?(:extension) + file.extension + else + # Not all CarrierWave storages respond to :extension + File.extname(file.path).delete('.') + end + + extensions.include?(extension.downcase) end def file_storage? diff --git a/app/views/admin/application_settings/_form.html.haml b/app/views/admin/application_settings/_form.html.haml index 538d8176ce7..23b52d08df7 100644 --- a/app/views/admin/application_settings/_form.html.haml +++ b/app/views/admin/application_settings/_form.html.haml @@ -109,7 +109,7 @@ Newly registered users will by default be external %fieldset - %legend Sign-in Restrictions + %legend Sign-up Restrictions .form-group .col-sm-offset-2.col-sm-10 .checkbox @@ -123,6 +123,49 @@ = f.check_box :send_user_confirmation_email Send confirmation email on sign-up .form-group + = f.label :domain_whitelist, 'Whitelisted domains for sign-ups', class: 'control-label col-sm-2' + .col-sm-10 + = f.text_area :domain_whitelist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8 + .help-block ONLY users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com + .form-group + = f.label :domain_blacklist_enabled, 'Domain Blacklist', class: 'control-label col-sm-2' + .col-sm-10 + .checkbox + = f.label :domain_blacklist_enabled do + = f.check_box :domain_blacklist_enabled + Enable domain blacklist for sign ups + .form-group + .col-sm-offset-2.col-sm-10 + .radio + = label_tag :blacklist_type_file do + = radio_button_tag :blacklist_type, :file + .option-title + Upload blacklist file + .radio + = label_tag :blacklist_type_raw do + = radio_button_tag :blacklist_type, :raw, @application_setting.domain_blacklist.present? || @application_setting.domain_blacklist.blank? + .option-title + Enter blacklist manually + .form-group.blacklist-file + = f.label :domain_blacklist_file, 'Blacklist file', class: 'control-label col-sm-2' + .col-sm-10 + = f.file_field :domain_blacklist_file, class: 'form-control', accept: '.txt,.conf' + .help-block Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines or commas for multiple entries. + .form-group.blacklist-raw + = f.label :domain_blacklist, 'Blacklisted domains for sign-ups', class: 'control-label col-sm-2' + .col-sm-10 + = f.text_area :domain_blacklist_raw, placeholder: 'domain.com', class: 'form-control', rows: 8 + .help-block Users with e-mail addresses that match these domain(s) will NOT be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com + + .form-group + = f.label :after_sign_up_text, class: 'control-label col-sm-2' + .col-sm-10 + = f.text_area :after_sign_up_text, class: 'form-control', rows: 4 + .help-block Markdown enabled + + %fieldset + %legend Sign-in Restrictions + .form-group .col-sm-offset-2.col-sm-10 .checkbox = f.label :signin_enabled do @@ -148,11 +191,6 @@ = f.number_field :two_factor_grace_period, min: 0, class: 'form-control', placeholder: '0' .help-block Amount of time (in hours) that users are allowed to skip forced configuration of two-factor authentication .form-group - = f.label :restricted_signup_domains, 'Restricted domains for sign-ups', class: 'control-label col-sm-2' - .col-sm-10 - = f.text_area :restricted_signup_domains_raw, placeholder: 'domain.com', class: 'form-control' - .help-block Only users with e-mail addresses that match these domain(s) will be able to sign-up. Wildcards allowed. Use separate lines for multiple entries. Ex: domain.com, *.domain.com - .form-group = f.label :home_page_url, 'Home page URL', class: 'control-label col-sm-2' .col-sm-10 = f.text_field :home_page_url, class: 'form-control', placeholder: 'http://company.example.com', :'aria-describedby' => 'home_help_block' @@ -168,11 +206,6 @@ = f.text_area :sign_in_text, class: 'form-control', rows: 4 .help-block Markdown enabled .form-group - = f.label :after_sign_up_text, class: 'control-label col-sm-2' - .col-sm-10 - = f.text_area :after_sign_up_text, class: 'form-control', rows: 4 - .help-block Markdown enabled - .form-group = f.label :help_page_text, class: 'control-label col-sm-2' .col-sm-10 = f.text_area :help_page_text, class: 'form-control', rows: 4 @@ -352,4 +385,4 @@ .form-actions - = f.submit 'Save', class: 'btn btn-save' + = f.submit 'Save', class: 'btn btn-save'
\ No newline at end of file diff --git a/app/views/admin/groups/_form.html.haml b/app/views/admin/groups/_form.html.haml index 0cc405401cf..5f7fdfdb011 100644 --- a/app/views/admin/groups/_form.html.haml +++ b/app/views/admin/groups/_form.html.haml @@ -9,6 +9,10 @@ = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group + .form-group + .col-sm-offset-2.col-sm-10 + = render 'shared/allow_request_access', form: f + - if @group.new_record? .form-group .col-sm-offset-2.col-sm-10 diff --git a/app/views/discussions/_diff_discussion.html.haml b/app/views/discussions/_diff_discussion.html.haml new file mode 100644 index 00000000000..fa1ad9efa73 --- /dev/null +++ b/app/views/discussions/_diff_discussion.html.haml @@ -0,0 +1,6 @@ +%tr.notes_holder + %td.notes_line{ colspan: 2 } + %td.notes_content + %ul.notes{ data: { discussion_id: discussion.id } } + = render partial: "projects/notes/note", collection: discussion.notes, as: :note + = link_to_reply_discussion(discussion) diff --git a/app/views/discussions/_diff_with_notes.html.haml b/app/views/discussions/_diff_with_notes.html.haml new file mode 100644 index 00000000000..02b159ffd45 --- /dev/null +++ b/app/views/discussions/_diff_with_notes.html.haml @@ -0,0 +1,14 @@ +- diff_file = discussion.diff_file +- blob = discussion.blob + +.diff-file.file-holder + .file-title + = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_file.content_commit, project: discussion.project, url: discussion_diff_path(discussion) + + .diff-content.code.js-syntax-highlight + %table + - discussion.truncated_diff_lines.each do |line| + = render "projects/diffs/line", line: line, diff_file: diff_file, plain: true + + - if discussion.for_line?(line) + = render "discussions/diff_discussion", discussion: discussion diff --git a/app/views/discussions/_discussion.html.haml b/app/views/discussions/_discussion.html.haml new file mode 100644 index 00000000000..49702e048aa --- /dev/null +++ b/app/views/discussions/_discussion.html.haml @@ -0,0 +1,45 @@ +- expanded = discussion.expanded? +%li.note.note-discussion.timeline-entry + .timeline-entry-inner + .timeline-icon + = link_to user_path(discussion.author) do + = image_tag avatar_icon(discussion.author), class: "avatar s40" + .timeline-content + .discussion.js-toggle-container{ class: discussion.id } + .discussion-header + = link_to_member(@project, discussion.author, avatar: false) + + .inline.discussion-headline-light + = discussion.author.to_reference + started a discussion on + + - if discussion.for_commit? + - commit = discussion.noteable + - if commit + commit + = link_to commit.short_id, namespace_project_commit_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code), class: 'monospace' + - else + a deleted commit + - else + - if discussion.active? + = link_to diffs_namespace_project_merge_request_path(discussion.project.namespace, discussion.project, discussion.noteable, anchor: discussion.line_code) do + the diff + - else + an outdated diff + + = time_ago_with_tooltip(discussion.created_at, placement: "bottom", html_class: "note-created-ago") + + .discussion-actions + = link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do + - if expanded + = icon("chevron-up") + - else + = icon("chevron-down") + + Toggle discussion + + .discussion-body.js-toggle-content{ class: ("hide" unless expanded) } + - if discussion.diff_discussion? && discussion.diff_file + = render "discussions/diff_with_notes", discussion: discussion + - else + = render "discussions/notes", discussion: discussion diff --git a/app/views/discussions/_notes.html.haml b/app/views/discussions/_notes.html.haml new file mode 100644 index 00000000000..a2642b839f6 --- /dev/null +++ b/app/views/discussions/_notes.html.haml @@ -0,0 +1,5 @@ +.panel.panel-default + .notes{ data: { discussion_id: discussion.id } } + %ul.notes.timeline + = render partial: "projects/notes/note", collection: discussion.notes, as: :note + = link_to_reply_discussion(discussion) diff --git a/app/views/discussions/_parallel_diff_discussion.html.haml b/app/views/discussions/_parallel_diff_discussion.html.haml new file mode 100644 index 00000000000..a798c438ea0 --- /dev/null +++ b/app/views/discussions/_parallel_diff_discussion.html.haml @@ -0,0 +1,22 @@ +%tr.notes_holder + - if discussion_left + %td.notes_line.old + %td.notes_content.parallel.old + %ul.notes{ data: { discussion_id: discussion_left.id } } + = render partial: "projects/notes/note", collection: discussion_left.notes, as: :note + + = link_to_reply_discussion(discussion_left, 'old') + - else + %td.notes_line.old= "" + %td.notes_content.parallel.old= "" + + - if discussion_right + %td.notes_line.new + %td.notes_content.parallel.new + %ul.notes{ data: { discussion_id: discussion_right.id } } + = render partial: "projects/notes/note", collection: discussion_right.notes, as: :note + + = link_to_reply_discussion(discussion_right, 'new') + - else + %td.notes_line.new= "" + %td.notes_content.parallel.new= "" diff --git a/app/views/events/_event.html.haml b/app/views/events/_event.html.haml index e4629bae0e6..5c318cd3b8b 100644 --- a/app/views/events/_event.html.haml +++ b/app/views/events/_event.html.haml @@ -4,11 +4,7 @@ #{time_ago_with_tooltip(event.created_at)} = cache [event, current_application_settings, "v2.2"] do - - if event.author - = link_to user_path(event.author) do - = image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:'' - - else - = image_tag avatar_icon(event.author_email, 40), class: "avatar s40", alt:'' + = author_avatar(event, size: 40) - if event.created_project? = render "events/event/created_project", event: event diff --git a/app/views/events/_event_scope.html.haml b/app/views/events/_event_scope.html.haml new file mode 100644 index 00000000000..8f7da7d8c4f --- /dev/null +++ b/app/views/events/_event_scope.html.haml @@ -0,0 +1,7 @@ +%span.event-scope + = event_preposition(event) + - if event.project + = link_to_project event.project + - else + = event.project_name + diff --git a/app/views/events/event/_common.html.haml b/app/views/events/event/_common.html.haml index 2e2403347c1..bba6e0d2c20 100644 --- a/app/views/events/event/_common.html.haml +++ b/app/views/events/event/_common.html.haml @@ -1,6 +1,6 @@ .event-title %span.author_name= link_to_author event - %span.event_label{class: event.action_name} + %span{class: event.action_name} - if event.target = event.action_name %strong @@ -10,12 +10,7 @@ - else = event_action_name(event) - = event_preposition(event) - - - if event.project - = link_to_project event.project - - else - = event.project_name + = render "events/event_scope", event: event - if event.target.respond_to?(:title) .event-body diff --git a/app/views/events/event/_created_project.html.haml b/app/views/events/event/_created_project.html.haml index 5a2a469ba62..aba64dd17d0 100644 --- a/app/views/events/event/_created_project.html.haml +++ b/app/views/events/event/_created_project.html.haml @@ -1,6 +1,6 @@ .event-title %span.author_name= link_to_author event - %span.event_label{class: event.action_name} + %span{class: event.action_name} = event_action_name(event) - if event.project diff --git a/app/views/events/event/_note.html.haml b/app/views/events/event/_note.html.haml index 830fec0b4ab..f08c96df309 100644 --- a/app/views/events/event/_note.html.haml +++ b/app/views/events/event/_note.html.haml @@ -1,14 +1,9 @@ .event-title %span.author_name= link_to_author event - %span.event_label - = event.action_name - = event_note_title_html(event) - at + = event.action_name + = event_note_title_html(event) - - if event.project - = link_to_project event.project - - else - = event.project_name + = render "events/event_scope", event: event .event-body .event-note diff --git a/app/views/events/event/_push.html.haml b/app/views/events/event/_push.html.haml index ea54ef226ec..44fff49d99c 100644 --- a/app/views/events/event/_push.html.haml +++ b/app/views/events/event/_push.html.haml @@ -2,14 +2,14 @@ .event-title %span.author_name= link_to_author event - %span.event_label.pushed #{event.action_name} #{event.ref_type} + %span.pushed #{event.action_name} #{event.ref_type} - if event.rm_ref? %strong= event.ref_name - else %strong = link_to event.ref_name, namespace_project_commits_path(project.namespace, project, event.ref_name), title: h(event.target_title) - at - = link_to_project project + + = render "events/event_scope", event: event - if event.push_with_commits? .event-body diff --git a/app/views/groups/edit.html.haml b/app/views/groups/edit.html.haml index 92cd4c553d0..decb89b2fd6 100644 --- a/app/views/groups/edit.html.haml +++ b/app/views/groups/edit.html.haml @@ -22,6 +22,10 @@ = render 'shared/visibility_level', f: f, visibility_level: @group.visibility_level, can_change_visibility_level: can_change_group_visibility_level?(@group), form_model: @group .form-group + .col-sm-offset-2.col-sm-10 + = render 'shared/allow_request_access', form: f + + .form-group %hr = f.label :share_with_group_lock, class: 'control-label' do Share with group lock diff --git a/app/views/layouts/_init_auto_complete.html.haml b/app/views/layouts/_init_auto_complete.html.haml index 12e7ed0e792..351100f3523 100644 --- a/app/views/layouts/_init_auto_complete.html.haml +++ b/app/views/layouts/_init_auto_complete.html.haml @@ -1,7 +1,7 @@ - project = @target_project || @project +- noteable_class = @noteable.class if @noteable.present? -- if @noteable - :javascript - GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: @noteable.class, type_id: params[:id])}" - GitLab.GfmAutoComplete.cachedData = undefined; - GitLab.GfmAutoComplete.setup(); +:javascript + GitLab.GfmAutoComplete.dataSource = "#{autocomplete_sources_namespace_project_path(project.namespace, project, type: noteable_class, type_id: params[:id])}" + GitLab.GfmAutoComplete.cachedData = undefined; + GitLab.GfmAutoComplete.setup(); diff --git a/app/views/layouts/nav/_dashboard.html.haml b/app/views/layouts/nav/_dashboard.html.haml index 21668698814..3a14751ea8e 100644 --- a/app/views/layouts/nav/_dashboard.html.haml +++ b/app/views/layouts/nav/_dashboard.html.haml @@ -30,7 +30,7 @@ %span Merge Requests %span.count= number_with_delimiter(current_user.assigned_merge_requests.opened.count) - = nav_link(controller: :snippets) do + = nav_link(controller: 'dashboard/snippets') do = link_to dashboard_snippets_path, title: 'Snippets' do %span Snippets diff --git a/app/views/layouts/nav/_explore.html.haml b/app/views/layouts/nav/_explore.html.haml index 3b40006a0cc..e5bda7b3a6f 100644 --- a/app/views/layouts/nav/_explore.html.haml +++ b/app/views/layouts/nav/_explore.html.haml @@ -1,21 +1,17 @@ %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('bookmark fw') %span Projects = nav_link(controller: [:groups, 'groups/milestones', 'groups/group_members']) do = link_to explore_groups_path, title: 'Groups' do - = icon('group fw') %span Groups = nav_link(controller: :snippets) do = link_to explore_snippets_path, title: 'Snippets' do - = icon('clipboard fw') %span Snippets = nav_link(controller: :help) do = link_to help_path, title: 'Help' do - = icon('question-circle fw') %span Help diff --git a/app/views/layouts/nav/_project_settings.html.haml b/app/views/layouts/nav/_project_settings.html.haml index 51a54b4f262..52a5bdc1a1b 100644 --- a/app/views/layouts/nav/_project_settings.html.haml +++ b/app/views/layouts/nav/_project_settings.html.haml @@ -39,7 +39,7 @@ = link_to namespace_project_triggers_path(@project.namespace, @project), title: 'Triggers' do %span Triggers - = nav_link(controller: :badges) do - = link_to namespace_project_badges_path(@project.namespace, @project), title: 'Badges' do + = nav_link(controller: :pipelines_settings) do + = link_to namespace_project_pipelines_settings_path(@project.namespace, @project), title: 'CI/CD Pipelines' do %span - Badges + CI/CD Pipelines diff --git a/app/views/layouts/project.html.haml b/app/views/layouts/project.html.haml index 2049b204956..d03d5e2ca6a 100644 --- a/app/views/layouts/project.html.haml +++ b/app/views/layouts/project.html.haml @@ -6,7 +6,7 @@ - content_for :scripts_body_top do - project = @target_project || @project - if @project_wiki && @page - - markdown_preview_path = namespace_project_wiki_markdown_preview_path(project.namespace, project, params[:id]) + - markdown_preview_path = namespace_project_wiki_markdown_preview_path(project.namespace, project, @page.title) - else - markdown_preview_path = markdown_preview_namespace_project_path(project.namespace, project) - if current_user diff --git a/app/views/profiles/_head.html.haml b/app/views/profiles/_head.html.haml index 003884a5bd9..943ebdaeffe 100644 --- a/app/views/profiles/_head.html.haml +++ b/app/views/profiles/_head.html.haml @@ -1,3 +1,3 @@ - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/cropper.js') - = page_specific_javascript_tag('profile/application.js') + = page_specific_javascript_tag('profile/profile_bundle.js') diff --git a/app/views/projects/_activity.html.haml b/app/views/projects/_activity.html.haml index 48b0dd6b121..ac50ce83f6a 100644 --- a/app/views/projects/_activity.html.haml +++ b/app/views/projects/_activity.html.haml @@ -5,7 +5,8 @@ %i.fa.fa-rss = render 'shared/event_filter' -.content_list{:"data-href" => activity_project_path(@project)} + +.content_list.project-activity{:"data-href" => activity_project_path(@project)} = spinner :javascript diff --git a/app/views/projects/_builds_settings.html.haml b/app/views/projects/_builds_settings.html.haml deleted file mode 100644 index fff30f11d82..00000000000 --- a/app/views/projects/_builds_settings.html.haml +++ /dev/null @@ -1,65 +0,0 @@ -%fieldset.builds-feature - %h5.prepend-top-0 - Builds - - unless @repository.gitlab_ci_yml - .form-group - %p Builds need to be configured before you can begin using Continuous Integration. - = link_to 'Get started with Builds', help_page_path('ci/quick_start/README'), class: 'btn btn-info' - .form-group - %p Get recent application code using the following command: - .radio - = f.label :build_allow_git_fetch_false do - = f.radio_button :build_allow_git_fetch, 'false' - %strong git clone - %br - %span.descr Slower but makes sure you have a clean dir before every build - .radio - = f.label :build_allow_git_fetch_true do - = f.radio_button :build_allow_git_fetch, 'true' - %strong git fetch - %br - %span.descr Faster - - .form-group - = f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light' - = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0' - %p.help-block per build in minutes - .form-group - = f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light' - .input-group - %span.input-group-addon / - = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' - %span.input-group-addon / - %p.help-block - We will use this regular expression to find test coverage output in build trace. - Leave blank if you want to disable this feature - .bs-callout.bs-callout-info - %p Below are examples of regex for existing tools: - %ul - %li - Simplecov (Ruby) - - %code \(\d+.\d+\%\) covered - %li - pytest-cov (Python) - - %code \d+\%\s*$ - %li - phpunit --coverage-text --colors=never (PHP) - - %code ^\s*Lines:\s*\d+.\d+\% - %li - gcovr (C/C++) - - %code ^TOTAL.*\s+(\d+\%)$ - %li - tap --coverage-report=text-summary (Node.js) - - %code ^Statements\s*:\s*([^%]+) - - .form-group - .checkbox - = f.label :public_builds do - = f.check_box :public_builds - %strong Public builds - .help-block Allow everyone to access builds for Public and Internal projects - - .form-group.append-bottom-0 - = f.label :runners_token, "Runners token", class: 'label-light' - = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89' - %p.help-block The secure token used to checkout project. diff --git a/app/views/projects/badges/index.html.haml b/app/views/projects/badges/index.html.haml deleted file mode 100644 index ac80951dd4f..00000000000 --- a/app/views/projects/badges/index.html.haml +++ /dev/null @@ -1,23 +0,0 @@ -- page_title 'Badges' -- badges_path = namespace_project_badges_path(@project.namespace, @project) - -.prepend-top-10 - .panel.panel-default - .panel-heading - %b Builds badge · - = @build_badge.to_html - .pull-right - = render 'shared/ref_switcher', destination: 'badges', align_right: true - .panel-body - .row - .col-md-2.text-center - Markdown - .col-md-10.code.js-syntax-highlight - = highlight('.md', @build_badge.to_markdown) - .row - %hr - .row - .col-md-2.text-center - HTML - .col-md-10.code.js-syntax-highlight - = highlight('.html', @build_badge.to_html) diff --git a/app/views/projects/branches/_commit.html.haml b/app/views/projects/branches/_commit.html.haml index 9fe65cbb104..d54c76ff9c8 100644 --- a/app/views/projects/branches/_commit.html.haml +++ b/app/views/projects/branches/_commit.html.haml @@ -1,5 +1,5 @@ .branch-commit - = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-id monospace" + = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-id monospace" · %span.str-truncated = link_to_gfm commit.title, namespace_project_commit_path(project.namespace, project, commit.id), class: "commit-row-message" diff --git a/app/views/projects/builds/_sidebar.html.haml b/app/views/projects/builds/_sidebar.html.haml index 396cc4ad925..dc57b49f27a 100644 --- a/app/views/projects/builds/_sidebar.html.haml +++ b/app/views/projects/builds/_sidebar.html.haml @@ -49,7 +49,7 @@ - if @build.duration %p.build-detail-row %span.build-light-text Duration: - #{duration_in_words(@build.finished_at, @build.started_at)} + = time_interval_in_words(@build.duration) - if @build.finished_at %p.build-detail-row %span.build-light-text Finished: diff --git a/app/views/projects/buttons/_fork.html.haml b/app/views/projects/buttons/_fork.html.haml index a9eaed4c5f6..a098a082854 100644 --- a/app/views/projects/buttons/_fork.html.haml +++ b/app/views/projects/buttons/_fork.html.haml @@ -2,7 +2,7 @@ - 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 - = icon('code-fork fw') + = custom_icon('icon_fork') Fork %div.count-with-arrow %span.arrow @@ -10,7 +10,7 @@ = @project.forks_count - else = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn has-tooltip' do - = icon('code-fork fw') + = custom_icon('icon_fork') Fork %div.count-with-arrow %span.arrow diff --git a/app/views/projects/ci/builds/_build.html.haml b/app/views/projects/ci/builds/_build.html.haml index 9264289987d..a9fb3c58431 100644 --- a/app/views/projects/ci/builds/_build.html.haml +++ b/app/views/projects/ci/builds/_build.html.haml @@ -14,16 +14,19 @@ %span ##{build.id} - if build.stuck? - = icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.') + .icon-container + = icon('warning', class: 'text-warning has-tooltip', title: 'Build is stuck. Check runners.') - if defined?(retried) && retried - = icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.') + .icon-container + = icon('warning', class: 'text-warning has-tooltip', title: 'Build was retried.') - if defined?(ref) && ref - if build.ref = link_to build.ref, namespace_project_commits_path(build.project.namespace, build.project, build.ref), class: "monospace branch-name" - else .light none - = custom_icon("icon_commit") + .icon-container + = custom_icon("icon_commit") - if defined?(commit_sha) && commit_sha = link_to build.short_sha, namespace_project_commit_path(build.project.namespace, build.project, build.sha), class: "commit-id monospace" @@ -88,4 +91,3 @@ - elsif build.playable? = link_to play_namespace_project_build_path(build.project.namespace, build.project, build, return_to: request.original_url), method: :post, title: 'Play', class: 'btn btn-build' do = icon('play') - diff --git a/app/views/projects/ci/pipelines/_pipeline.html.haml b/app/views/projects/ci/pipelines/_pipeline.html.haml index 996c9073770..2f7d54f0bdd 100644 --- a/app/views/projects/ci/pipelines/_pipeline.html.haml +++ b/app/views/projects/ci/pipelines/_pipeline.html.haml @@ -27,7 +27,7 @@ %p.commit-title - if commit = pipeline.commit - = commit_author_avatar(commit, size: 20) + = author_avatar(commit, size: 20) = link_to_gfm truncate(commit.title, length: 60), namespace_project_commit_path(@project.namespace, @project, commit.id), class: "commit-row-message" - else Cant find HEAD commit for this branch @@ -35,7 +35,7 @@ - stages_status = pipeline.statuses.latest.stages_status - stages.each do |stage| - %td + %td.stage-cell - status = stages_status[stage] - tooltip = "#{stage.titleize}: #{status || 'not found'}" - if status diff --git a/app/views/projects/commit/_pipeline.html.haml b/app/views/projects/commit/_pipeline.html.haml index 41fd5459429..540689f4a61 100644 --- a/app/views/projects/commit/_pipeline.html.haml +++ b/app/views/projects/commit/_pipeline.html.haml @@ -35,8 +35,8 @@ .bs-callout.bs-callout-warning \.gitlab-ci.yml not found in this commit -.table-holder - %table.table.builds +.table-holder.pipeline-holder + %table.table.builds.pipeline %thead %tr %th Status diff --git a/app/views/projects/commits/_commit.html.haml b/app/views/projects/commits/_commit.html.haml index c8c7b858baa..fd888f41b1e 100644 --- a/app/views/projects/commits/_commit.html.haml +++ b/app/views/projects/commits/_commit.html.haml @@ -9,7 +9,8 @@ = cache(cache_key) do %li.commit.js-toggle-container{ id: "commit-#{commit.short_id}" } - = commit_author_avatar(commit, size: 36) + = author_avatar(commit, size: 36) + .commit-info-block .commit-row-title %span.item-title @@ -18,13 +19,14 @@ · = commit.short_id - if commit.status - = render_commit_status(commit, cssclass: 'visible-xs-inline') + .visible-xs-inline + = render_commit_status(commit) - if commit.description? %a.text-expander.hidden-xs.js-toggle-button ... .commit-actions.hidden-xs - if commit.status - = render_commit_status(commit, cssclass: 'btn btn-transparent') + = render_commit_status(commit) = clipboard_button(clipboard_text: commit.id) = link_to commit.short_id, namespace_project_commit_path(project.namespace, project, commit), class: "commit-short-id btn btn-transparent" = link_to_browse_code(project, commit) diff --git a/app/views/projects/deployments/_actions.haml b/app/views/projects/deployments/_actions.haml index 65d68aa2985..f70dba224fa 100644 --- a/app/views/projects/deployments/_actions.haml +++ b/app/views/projects/deployments/_actions.haml @@ -17,6 +17,6 @@ - if local_assigns.fetch(:allow_rollback, false) = link_to [:retry, @project.namespace.becomes(Namespace), @project, deployment.deployable], method: :post, class: 'btn btn-build' do - if deployment.last? - Retry + Re-deploy - else Rollback diff --git a/app/views/projects/diffs/_match_line_parallel.html.haml b/app/views/projects/diffs/_match_line_parallel.html.haml deleted file mode 100644 index b9c0d9dcdfd..00000000000 --- a/app/views/projects/diffs/_match_line_parallel.html.haml +++ /dev/null @@ -1,4 +0,0 @@ -%td.old_line.diff-line-num.empty-cell -%td.line_content.parallel.match= line -%td.new_line.diff-line-num.empty-cell -%td.line_content.parallel.match= line diff --git a/app/views/projects/diffs/_parallel_view.html.haml b/app/views/projects/diffs/_parallel_view.html.haml index d208fcee10b..7f30faa20d8 100644 --- a/app/views/projects/diffs/_parallel_view.html.haml +++ b/app/views/projects/diffs/_parallel_view.html.haml @@ -5,32 +5,35 @@ - left = line[:left] - right = line[:right] %tr.line_holder.parallel - - if left[:type] == 'match' - = render "projects/diffs/match_line_parallel", { line: left[:text] } - - elsif left[:type] == 'nonewline' - %td.old_line.diff-line-num.empty-cell - %td.line_content.parallel.match= left[:text] - %td.new_line.diff-line-num.empty-cell - %td.line_content.parallel.match= left[:text] + - if left + - if left.meta? + %td.old_line.diff-line-num.empty-cell + %td.line_content.parallel.match= left.text + - else + - left_line_code = diff_file.line_code(left) + - left_position = diff_file.position(left) + %td.old_line.diff-line-num{id: left_line_code, class: left.type, data: { linenumber: left.old_pos }} + %a{href: "##{left_line_code}" }= raw(left.old_pos) + %td.line_content.parallel.noteable_line{class: left.type, data: diff_view_line_data(left_line_code, left_position, 'old')}= diff_line_content(left.text) - else - %td.old_line.diff-line-num{id: left[:line_code], class: [left[:type], ('empty-cell' unless left[:number])], data: { linenumber: left[:number] }} - %a{href: "##{left[:line_code]}" }= raw(left[:number]) - %td.line_content.parallel.noteable_line{class: [left[:type], ('empty-cell' if left[:text].empty?)], data: diff_view_line_data(left[:line_code], left[:position], 'old')}= diff_line_content(left[:text]) + %td.old_line.diff-line-num.empty-cell + %td.line_content.parallel - - if right[:type] == 'new' - - new_line_type = 'new' - - new_line_code = right[:line_code] - - new_position = right[:position] + - if right + - if right.meta? + %td.old_line.diff-line-num.empty-cell + %td.line_content.parallel.match= left.text - else - - new_line_type = nil - - new_line_code = left[:line_code] - - new_position = left[:position] - - %td.new_line.diff-line-num{id: new_line_code, class: [new_line_type, ('empty-cell' unless right[:number])], data: { linenumber: right[:number] }} - %a{href: "##{new_line_code}" }= raw(right[:number]) - %td.line_content.parallel.noteable_line{class: [new_line_type, ('empty-cell' if right[:text].empty?)], data: diff_view_line_data(new_line_code, new_position, 'new')}= diff_line_content(right[:text]) + - right_line_code = diff_file.line_code(right) + - right_position = diff_file.position(right) + %td.new_line.diff-line-num{id: right_line_code, class: right.type, data: { linenumber: right.new_pos }} + %a{href: "##{right_line_code}" }= raw(right.new_pos) + %td.line_content.parallel.noteable_line{class: right.type, data: diff_view_line_data(right_line_code, right_position, 'new')}= diff_line_content(right.text) + - else + %td.old_line.diff-line-num.empty-cell + %td.line_content.parallel - unless @diff_notes_disabled - - notes_left, notes_right = organize_comments(left, right) - - if notes_left.present? || notes_right.present? - = render "projects/notes/diff_notes_with_reply_parallel", notes_left: notes_left, notes_right: notes_right + - discussion_left, discussion_right = parallel_diff_discussions(left, right, diff_file) + - if discussion_left || discussion_right + = render "discussions/parallel_diff_discussion", discussion_left: discussion_left, discussion_right: discussion_right diff --git a/app/views/projects/diffs/_text_file.html.haml b/app/views/projects/diffs/_text_file.html.haml index 196f8122db3..5970b9abf2b 100644 --- a/app/views/projects/diffs/_text_file.html.haml +++ b/app/views/projects/diffs/_text_file.html.haml @@ -11,9 +11,9 @@ - unless @diff_notes_disabled - line_code = diff_file.line_code(line) - - diff_notes = @grouped_diff_notes[line_code] if line_code - - if diff_notes - = render "projects/notes/diff_notes_with_reply", notes: diff_notes + - discussion = @grouped_diff_discussions[line_code] if line_code + - if discussion + = render "discussions/diff_discussion", discussion: discussion - if last_line > 0 = render "projects/diffs/match_line", { line: "", diff --git a/app/views/projects/edit.html.haml b/app/views/projects/edit.html.haml index 57af167180b..921155e970b 100644 --- a/app/views/projects/edit.html.haml +++ b/app/views/projects/edit.html.haml @@ -32,6 +32,10 @@ %strong = visibility_level_label(@project.visibility_level) .light= visibility_level_description(@project.visibility_level, @project) + + .form-group + = render 'shared/allow_request_access', form: f + .form-group = f.label :tag_list, "Tags", class: 'label-light' = f.text_field :tag_list, value: @project.tag_list.to_s, maxlength: 2000, class: "form-control" @@ -86,8 +90,6 @@ %hr = render 'merge_request_settings', f: f %hr - = render 'builds_settings', f: f - %hr %fieldset.features.append-bottom-default %h5.prepend-top-0 Project avatar diff --git a/app/views/projects/forks/index.html.haml b/app/views/projects/forks/index.html.haml index dbe9ddfde2f..a1d79bdabda 100644 --- a/app/views/projects/forks/index.html.haml +++ b/app/views/projects/forks/index.html.haml @@ -31,11 +31,11 @@ - 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 btn-new' do - = icon('code-fork fw') + = custom_icon('icon_fork') Fork - else = link_to new_namespace_project_fork_path(@project.namespace, @project), title: "Fork project", class: 'btn btn-new' do - = icon('code-fork fw') + = custom_icon('icon_fork') Fork diff --git a/app/views/projects/forks/new.html.haml b/app/views/projects/forks/new.html.haml index 73a7fc0e1ac..5242bc72b71 100644 --- a/app/views/projects/forks/new.html.haml +++ b/app/views/projects/forks/new.html.haml @@ -1,45 +1,54 @@ - page_title "Fork project" -- if @namespaces.present? - %h3.page-title Fork project - %p.lead - Click to fork the project to a user or group - %hr - .fork-namespaces - - @namespaces.in_groups_of(6, false) do |group| - .row - - group.each do |namespace| - .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 - = image_tag namespace_icon(namespace, 100) - .caption - %strong - = namespace.human_name - %div.text-primary - Already forked - - - 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 - = image_tag namespace_icon(namespace, 100) - .caption - %strong - = namespace.human_name - - %p.light - Fork is a copy of a project repository. +.row.prepend-top-default + .col-lg-3 + %h4.prepend-top-0 + Fork project + %p + A fork is a copy of a project. %br - Forking a repository allows you to do changes without affecting the original project. -- else - %h3 No available namespaces to fork the project - %p.slead - You must have permission to create a project in a namespace before forking. + Forking a repository allows you to make changes without affecting the original project. + .col-lg-9 + .fork-namespaces + - if @namespaces.present? + %label.label-light + %span + Click to fork the project to a user or group + - @namespaces.in_groups_of(6, false) do |group| + .row + - group.each do |namespace| + - avatar = namespace_icon(namespace, 100) + - if fork = namespace.find_fork_of(@project) + .fork-thumbnail.forked + = link_to project_path(fork) do + - if /no_((\w*)_)*avatar/.match(avatar) + .no-avatar + = icon 'question' + - else + = image_tag avatar + .caption + = namespace.human_name + - else + .fork-thumbnail + = link_to namespace_project_forks_path(@project.namespace, @project, namespace_key: namespace.id), method: "POST" do + - if /no_((\w*)_)*avatar/.match(avatar) + .no-avatar + = icon 'question' + - else + = image_tag avatar + .caption + = namespace.human_name + - else + %label.label-light + %span + No available namespaces to fork the project. + %br + %small + You must have permission to create a project in a namespace before forking. -.save-project-loader.hide - .center - %h2 - %i.fa.fa-spinner.fa-spin - Forking repository - %p Please wait a moment, this page will automatically refresh when ready. + .save-project-loader.hide + .center + %h2 + %i.fa.fa-spinner.fa-spin + Forking repository + %p Please wait a moment, this page will automatically refresh when ready. diff --git a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml index 542827b2f15..331dc1fcc29 100644 --- a/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml +++ b/app/views/projects/generic_commit_statuses/_generic_commit_status.html.haml @@ -51,7 +51,7 @@ %td.duration - if generic_commit_status.duration = icon("clock-o") - #{duration_in_words(generic_commit_status.finished_at, generic_commit_status.started_at)} + = time_interval_in_words(generic_commit_status.duration) %td.timestamp - if generic_commit_status.finished_at diff --git a/app/views/projects/graphs/_head.html.haml b/app/views/projects/graphs/_head.html.haml index ca347406dfe..45e51389c00 100644 --- a/app/views/projects/graphs/_head.html.haml +++ b/app/views/projects/graphs/_head.html.haml @@ -3,7 +3,7 @@ - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/chart.js') - = page_specific_javascript_tag('graphs/application.js') + = page_specific_javascript_tag('graphs/graphs_bundle.js') = nav_link(action: :show) do = link_to 'Contributors', namespace_project_graph_path = nav_link(action: :commits) do diff --git a/app/views/projects/merge_requests/widget/_heading.html.haml b/app/views/projects/merge_requests/widget/_heading.html.haml index 489c632ae22..6ef640bb654 100644 --- a/app/views/projects/merge_requests/widget/_heading.html.haml +++ b/app/views/projects/merge_requests/widget/_heading.html.haml @@ -1,6 +1,6 @@ - if @pipeline .mr-widget-heading - - %w[success skipped canceled failed running pending].each do |status| + - %w[success success_with_warnings skipped canceled failed running pending].each do |status| .ci_widget{ class: "ci-#{status}", style: ("display:none" unless @pipeline.status == status) } = ci_icon_for_status(status) %span diff --git a/app/views/projects/network/show.html.haml b/app/views/projects/network/show.html.haml index 091af4df4a1..b2ece44d966 100644 --- a/app/views/projects/network/show.html.haml +++ b/app/views/projects/network/show.html.haml @@ -1,7 +1,7 @@ - page_title "Network", @ref - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/raphael.js') - = page_specific_javascript_tag('network/application.js') + = page_specific_javascript_tag('network/network_bundle.js') = render "projects/commits/head" = render "head" %div{ class: container_class } diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index c72d0140bb9..facdfcc9447 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -89,9 +89,9 @@ = link_to "#", class: 'btn js-toggle-button import_git' do %i.fa.fa-git %span Repo by URL - %div + %div{ class: 'import_gitlab_project' } - if gitlab_project_import_enabled? - = link_to new_import_gitlab_project_path, class: 'btn import_gitlab_project project-submit' do + = link_to new_import_gitlab_project_path, class: 'btn btn_import_gitlab_project project-submit' do %i.fa.fa-gitlab %span GitLab export @@ -130,29 +130,29 @@ $(".modal").hide(); }); - $('.import_gitlab_project').bind('click', function() { - var _href = $("a.import_gitlab_project").attr("href"); - $(".import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val()); + $('.btn_import_gitlab_project').bind('click', function() { + var _href = $("a.btn_import_gitlab_project").attr("href"); + $(".btn_import_gitlab_project").attr("href", _href + '?namespace_id=' + $("#project_namespace_id").val() + '&path=' + $("#project_path").val()); }); - $('.import_gitlab_project').attr('disabled',true) - $('.import_gitlab_project').attr('title', 'Project path required.'); + $('.btn_import_gitlab_project').attr('disabled',true) + $('.import_gitlab_project').attr('title', 'Project path and name required.'); $('.import_gitlab_project').click(function( event ) { - if($('.import_gitlab_project').attr('disabled')) { + if($('.btn_import_gitlab_project').attr('disabled')) { event.preventDefault(); - new Flash("Please enter a path for the project to be imported to."); + new Flash("Please enter path and name for the project to be imported to."); } }); $('#project_path').keyup(function(){ if($(this).val().length !=0) { - $('.import_gitlab_project').attr('disabled', false); + $('.btn_import_gitlab_project').attr('disabled', false); $('.import_gitlab_project').attr('title',''); $(".flash-container").html("") } else { - $('.import_gitlab_project').attr('disabled',true); - $('.import_gitlab_project').attr('title', 'Project path required.'); + $('.btn_import_gitlab_project').attr('disabled',true); + $('.import_gitlab_project').attr('title', 'Project path and name required.'); } }); diff --git a/app/views/projects/notes/_diff_notes_with_reply.html.haml b/app/views/projects/notes/_diff_notes_with_reply.html.haml deleted file mode 100644 index ec6c4938efc..00000000000 --- a/app/views/projects/notes/_diff_notes_with_reply.html.haml +++ /dev/null @@ -1,7 +0,0 @@ -- note = notes.first -%tr.notes_holder - %td.notes_line{ colspan: 2 } - %td.notes_content - %ul.notes{ data: { discussion_id: note.discussion_id } } - = render partial: "projects/notes/note", collection: notes, as: :note - = link_to_reply_discussion(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 deleted file mode 100644 index e50a4f86d03..00000000000 --- a/app/views/projects/notes/_diff_notes_with_reply_parallel.html.haml +++ /dev/null @@ -1,25 +0,0 @@ -- note_left = notes_left.present? ? notes_left.first : nil -- note_right = notes_right.present? ? notes_right.first : nil - -%tr.notes_holder - - if note_left - %td.notes_line.old - %td.notes_content.parallel.old - %ul.notes{ data: { discussion_id: note_left.discussion_id } } - = render partial: "projects/notes/note", collection: notes_left, as: :note - - = link_to_reply_discussion(note_left, 'old') - - else - %td.notes_line.old= "" - %td.notes_content.parallel.old= "" - - - if note_right - %td.notes_line.new - %td.notes_content.parallel.new - %ul.notes{ data: { discussion_id: note_right.discussion_id } } - = render partial: "projects/notes/note", collection: notes_right, as: :note - - = link_to_reply_discussion(note_right, 'new') - - else - %td.notes_line.new= "" - %td.notes_content.parallel.new= "" diff --git a/app/views/projects/notes/_discussion.html.haml b/app/views/projects/notes/_discussion.html.haml deleted file mode 100644 index 7869d6413d8..00000000000 --- a/app/views/projects/notes/_discussion.html.haml +++ /dev/null @@ -1,46 +0,0 @@ -- note = discussion_notes.first -- expanded = !note.diff_note? || note.active? -%li.note.note-discussion.timeline-entry - .timeline-entry-inner - .timeline-icon - = link_to user_path(note.author) do - = image_tag avatar_icon(note.author), class: "avatar s40" - .timeline-content - .discussion.js-toggle-container{ class: note.discussion_id } - .discussion-header - = link_to_member(@project, note.author, avatar: false) - - .inline.discussion-headline-light - = note.author.to_reference - started a discussion on - - - if note.for_commit? - - commit = note.noteable - - if commit - commit - = link_to commit.short_id, namespace_project_commit_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code), class: 'monospace' - - else - a deleted commit - - else - - if note.active? - = link_to diffs_namespace_project_merge_request_path(note.project.namespace, note.project, note.noteable, anchor: note.line_code) do - the diff - - else - an outdated diff - - = time_ago_with_tooltip(note.created_at, placement: "bottom", html_class: "note-created-ago") - - .discussion-actions - = link_to "#", class: "note-action-button discussion-toggle-button js-toggle-button" do - - if expanded - = icon("chevron-up") - - else - = icon("chevron-down") - - Toggle discussion - - .discussion-body.js-toggle-content{ class: ("hide" unless expanded) } - - if note.diff_note? - = render "projects/notes/discussions/diff_with_notes", discussion_notes: discussion_notes - - else - = render "projects/notes/discussions/notes", discussion_notes: discussion_notes diff --git a/app/views/projects/notes/_note.html.haml b/app/views/projects/notes/_note.html.haml index af0046886fb..71da8ac9d7c 100644 --- a/app/views/projects/notes/_note.html.haml +++ b/app/views/projects/notes/_note.html.haml @@ -30,7 +30,7 @@ = link_to namespace_project_note_path(note.project.namespace, note.project, note), title: 'Remove comment', method: :delete, data: { confirm: 'Are you sure you want to remove this comment?' }, remote: true, class: 'note-action-button hidden-xs js-note-delete danger' do = icon('trash-o') .note-body{class: note_editable ? 'js-task-list-container' : ''} - .note-text + .note-text.md = preserve do = note.note_html = edited_time_ago_with_tooltip(note, placement: 'bottom', html_class: 'note_edited_ago', include_author: true) diff --git a/app/views/projects/notes/_notes.html.haml b/app/views/projects/notes/_notes.html.haml index ebf7e8a9cb3..022578bd6db 100644 --- a/app/views/projects/notes/_notes.html.haml +++ b/app/views/projects/notes/_notes.html.haml @@ -1,10 +1,8 @@ - if @discussions.present? - - @discussions.each do |discussion_notes| - - note = discussion_notes.first - - if note_for_main_target?(note) - = render partial: "projects/notes/note", object: note, as: :note + - @discussions.each do |discussion| + - if discussion.for_target?(@noteable) + = render partial: "projects/notes/note", object: discussion.first_note, as: :note - else - = render 'projects/notes/discussion', discussion_notes: discussion_notes + = render 'discussions/discussion', discussion: discussion - else - - @notes.each do |note| - = render partial: "projects/notes/note", object: note, as: :note + = render partial: "projects/notes/note", collection: @notes, as: :note diff --git a/app/views/projects/notes/discussions/_diff_with_notes.html.haml b/app/views/projects/notes/discussions/_diff_with_notes.html.haml deleted file mode 100644 index 4a69b8f8840..00000000000 --- a/app/views/projects/notes/discussions/_diff_with_notes.html.haml +++ /dev/null @@ -1,17 +0,0 @@ -- note = discussion_notes.first -- diff_file = note.diff_file -- return unless diff_file - -- blob = note.blob - -.diff-file.file-holder - .file-title - = render "projects/diffs/file_header", diff_file: diff_file, blob: blob, diff_commit: diff_file.content_commit, project: note.project, url: diff_note_path(note) - - .diff-content.code.js-syntax-highlight - %table - - note.truncated_diff_lines.each do |line| - = render "projects/diffs/line", line: line, diff_file: diff_file, plain: true - - - if note.for_line?(line) - = render "projects/notes/diff_notes_with_reply", notes: discussion_notes diff --git a/app/views/projects/notes/discussions/_notes.html.haml b/app/views/projects/notes/discussions/_notes.html.haml deleted file mode 100644 index a785149549d..00000000000 --- a/app/views/projects/notes/discussions/_notes.html.haml +++ /dev/null @@ -1,6 +0,0 @@ -- note = discussion_notes.first -.panel.panel-default - .notes{ data: { discussion_id: note.discussion_id } } - %ul.notes.timeline - = render partial: "projects/notes/note", collection: discussion_notes, as: :note - = link_to_reply_discussion(note) diff --git a/app/views/projects/pipelines_settings/show.html.haml b/app/views/projects/pipelines_settings/show.html.haml new file mode 100644 index 00000000000..228bad36ebd --- /dev/null +++ b/app/views/projects/pipelines_settings/show.html.haml @@ -0,0 +1,103 @@ +- page_title "CI/CD Pipelines" + +.row.prepend-top-default + .col-lg-3.profile-settings-sidebar + %h4.prepend-top-0 + = page_title + .col-lg-9 + %h5.prepend-top-0 + Pipelines + = form_for @project, url: namespace_project_pipelines_settings_path(@project.namespace.becomes(Namespace), @project), remote: true, authenticity_token: true do |f| + %fieldset.builds-feature + - unless @repository.gitlab_ci_yml + .form-group + %p Pipelines need to be configured before you can begin using Continuous Integration. + = link_to 'Get started with CI/CD Pipelines', help_page_path('ci/quick_start/README'), class: 'btn btn-info' + .form-group + %p Get recent application code using the following command: + .radio + = f.label :build_allow_git_fetch_false do + = f.radio_button :build_allow_git_fetch, 'false' + %strong git clone + %br + %span.descr Slower but makes sure you have a clean dir before every build + .radio + = f.label :build_allow_git_fetch_true do + = f.radio_button :build_allow_git_fetch, 'true' + %strong git fetch + %br + %span.descr Faster + + .form-group + = f.label :build_timeout_in_minutes, 'Timeout', class: 'label-light' + = f.number_field :build_timeout_in_minutes, class: 'form-control', min: '0' + %p.help-block per build in minutes + .form-group + = f.label :build_coverage_regex, "Test coverage parsing", class: 'label-light' + .input-group + %span.input-group-addon / + = f.text_field :build_coverage_regex, class: 'form-control', placeholder: '\(\d+.\d+\%\) covered' + %span.input-group-addon / + %p.help-block + We will use this regular expression to find test coverage output in build trace. + Leave blank if you want to disable this feature + .bs-callout.bs-callout-info + %p Below are examples of regex for existing tools: + %ul + %li + Simplecov (Ruby) - + %code \(\d+.\d+\%\) covered + %li + pytest-cov (Python) - + %code \d+\%\s*$ + %li + phpunit --coverage-text --colors=never (PHP) - + %code ^\s*Lines:\s*\d+.\d+\% + %li + gcovr (C/C++) - + %code ^TOTAL.*\s+(\d+\%)$ + %li + tap --coverage-report=text-summary (Node.js) - + %code ^Statements\s*:\s*([^%]+) + + .form-group + .checkbox + = f.label :public_builds do + = f.check_box :public_builds + %strong Public pipelines + .help-block Allow everyone to access pipelines for Public and Internal projects + + .form-group.append-bottom-default + = f.label :runners_token, "Runners token", class: 'label-light' + = f.text_field :runners_token, class: "form-control", placeholder: 'xEeFCaDAB89' + %p.help-block The secure token used to checkout project. + + = f.submit 'Save changes', class: "btn btn-save" + +%hr + +.row.prepend-top-default + .col-lg-3.profile-settings-sidebar + %h4.prepend-top-0 + Builds Badge + .col-lg-9 + .prepend-top-10 + .panel.panel-default + .panel-heading + %b Builds badge · + = @build_badge.to_html + .pull-right + = render 'shared/ref_switcher', destination: 'badges', align_right: true + .panel-body + .row + .col-md-2.text-center + Markdown + .col-md-10.code.js-syntax-highlight + = highlight('.md', @build_badge.to_markdown) + .row + %hr + .row + .col-md-2.text-center + HTML + .col-md-10.code.js-syntax-highlight + = highlight('.html', @build_badge.to_html) diff --git a/app/views/projects/protected_branches/index.html.haml b/app/views/projects/protected_branches/index.html.haml index 151e1d64851..950df740bbc 100644 --- a/app/views/projects/protected_branches/index.html.haml +++ b/app/views/projects/protected_branches/index.html.haml @@ -6,12 +6,13 @@ = page_title %p Keep stable branches secure and force developers to use merge requests. %p.prepend-top-20 - Protected branches are designed to: + By default, protected branches are designed to: %ul - %li prevent pushes from everybody except #{link_to "masters", help_page_path("user/permissions"), class: "vlink"} - %li prevent anyone from force pushing to the branch - %li prevent anyone from deleting the branch - %p.append-bottom-0 Read more about #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"} + %li prevent their creation, if not already created, from everybody except Masters + %li prevent pushes from everybody except Masters + %li prevent <strong>anyone</strong> from force pushing to the branch + %li prevent <strong>anyone</strong> from deleting the branch + %p.append-bottom-0 Read more about #{link_to "protected branches", help_page_path("user/project/protected_branches"), class: "underlined-link"} and #{link_to "project permissions", help_page_path("user/permissions"), class: "underlined-link"}. .col-lg-9 %h5.prepend-top-0 Protect a branch @@ -23,7 +24,7 @@ = f.label :name, "Branch", class: "label-light" = render partial: "dropdown", locals: { f: f } %p.help-block - = link_to "Wildcards", help_page_path('workflow/protected_branches', anchor: "wildcard-protected-branches") + = link_to "Wildcards", help_page_path('user/project/protected_branches', anchor: "wildcard-protected-branches") such as %code *-stable or diff --git a/app/views/projects/services/_form.html.haml b/app/views/projects/services/_form.html.haml index 166dc4a01fc..752fbc21a11 100644 --- a/app/views/projects/services/_form.html.haml +++ b/app/views/projects/services/_form.html.haml @@ -8,6 +8,7 @@ .col-lg-9 = form_for(@service, as: :service, url: namespace_project_service_path(@project.namespace, @project, @service.to_param), method: :put, html: { class: 'form-horizontal' }) do |form| = render 'shared/service_settings', form: form + = form.submit 'Save changes', class: 'btn btn-save' - if @service.valid? && @service.activated? diff --git a/app/views/projects/tags/show.html.haml b/app/views/projects/tags/show.html.haml index b7d7d5c5382..395d7af6cbb 100644 --- a/app/views/projects/tags/show.html.haml +++ b/app/views/projects/tags/show.html.haml @@ -1,36 +1,39 @@ +- @no_container = true - page_title @tag.name, "Tags" = render "projects/commits/head" -.row-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 - = icon("pencil") - = 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 - = 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 - %i.fa.fa-trash-o - .title - %span.item-title= @tag.name - - if @commit - = render 'projects/branches/commit', commit: @commit, project: @project - - else - Cant find HEAD commit for this tag - - if @tag.message.present? - %pre.body - = strip_gpg_signature(@tag.message) +%div{ class: container_class } + .sub-header-block + .pull-right.tag-buttons + - if can?(current_user, :push_code, @project) + = link_to edit_namespace_project_tag_release_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Edit release notes' do + = icon("pencil") + = link_to namespace_project_tree_path(@project.namespace, @project, @tag.name), class: 'btn has-tooltip', title: 'Browse files' do + = icon('files-o') + = link_to namespace_project_commits_path(@project.namespace, @project, @tag.name), class: 'btn 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 + %i.fa.fa-trash-o + .tag-info.append-bottom-10 + .title + %span.item-title= @tag.name + - if @commit + = render 'projects/branches/commit', commit: @commit, project: @project + - else + Cant find HEAD commit for this tag + - if @tag.message.present? + %pre.body + = strip_gpg_signature(@tag.message) -.append-bottom-default.prepend-top-default - - if @release.description.present? - .description - .wiki - = preserve do - = markdown @release.description - - else - This tag has no release notes. + .append-bottom-default.prepend-top-default + - if @release.description.present? + .description + .wiki + = preserve do + = markdown @release.description + - else + This tag has no release notes. diff --git a/app/views/shared/_allow_request_access.html.haml b/app/views/shared/_allow_request_access.html.haml new file mode 100644 index 00000000000..53a99a736c0 --- /dev/null +++ b/app/views/shared/_allow_request_access.html.haml @@ -0,0 +1,6 @@ +.checkbox + = form.label :request_access_enabled do + = form.check_box :request_access_enabled + %strong Allow users to request access + %br + %span.descr Allow users to request access if visibility is public or internal. diff --git a/app/views/shared/_service_settings.html.haml b/app/views/shared/_service_settings.html.haml index 4eaf7c2a025..5254d265918 100644 --- a/app/views/shared/_service_settings.html.haml +++ b/app/views/shared/_service_settings.html.haml @@ -10,69 +10,28 @@ .col-sm-10 = form.check_box :active -- if @service.supported_events.length > 1 - .form-group - = form.label :url, "Trigger", class: 'control-label' - .col-sm-10 - - if @service.supported_events.include?("push") - %div - = form.check_box :push_events, class: 'pull-left' - .prepend-left-20 - = form.label :push_events, class: 'list-label' do - %strong Push events - %p.light - This url will be triggered by a push to the repository - - if @service.supported_events.include?("tag_push") - %div - = form.check_box :tag_push_events, class: 'pull-left' - .prepend-left-20 - = form.label :tag_push_events, class: 'list-label' do - %strong Tag push events - %p.light - This url will be triggered when a new tag is pushed to the repository - - if @service.supported_events.include?("note") - %div - = form.check_box :note_events, class: 'pull-left' - .prepend-left-20 - = form.label :note_events, class: 'list-label' do - %strong Comments - %p.light - This url will be triggered when someone adds a comment - - if @service.supported_events.include?("issue") - %div - = form.check_box :issues_events, class: 'pull-left' - .prepend-left-20 - = form.label :issues_events, class: 'list-label' do - %strong Issues events - %p.light - This url will be triggered when an issue is created/updated/merged - - if @service.supported_events.include?("merge_request") - %div - = form.check_box :merge_requests_events, class: 'pull-left' - .prepend-left-20 - = form.label :merge_requests_events, class: 'list-label' do - %strong Merge Request events - %p.light - This url will be triggered when a merge request is created/updated/merged - - if @service.supported_events.include?("build") - %div - = form.check_box :build_events, class: 'pull-left' - .prepend-left-20 - = form.label :build_events, class: 'list-label' do - %strong Build events - %p.light - This url will be triggered when a build status changes - - if @service.supported_events.include?("wiki_page") - %div - = form.check_box :wiki_page_events, class: 'pull-left' - .prepend-left-20 - = form.label :wiki_page_events, class: 'list-label' do - %strong Wiki Page events - %p.light - This url will be triggered when a wiki page is created/updated +.form-group + = form.label :url, "Trigger", class: 'control-label' + + .col-sm-10 + - @service.supported_events.each do |event| + %div + = form.check_box service_event_field_name(event), class: 'pull-left' + .prepend-left-20 + = form.label service_event_field_name(event), class: 'list-label' do + %strong + = event.humanize + + - field = @service.event_field(event) + + - if field + %p + = form.text_field field[:name], class: "form-control", placeholder: field[:placeholder] + %p.light + = service_event_description(event) -- @service.fields.each do |field| +- @service.global_fields.each do |field| - type = field[:type] - if type == 'fieldset' diff --git a/app/views/shared/icons/_icon_fork.svg b/app/views/shared/icons/_icon_fork.svg new file mode 100644 index 00000000000..a21f8f3a951 --- /dev/null +++ b/app/views/shared/icons/_icon_fork.svg @@ -0,0 +1,3 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="40" height="40" viewBox="0 0 40 40"> + <path fill="#7E7E7E" fill-rule="evenodd" d="M22,29.5351288 L22,22.7193602 C26.1888699,21.5098039 29.3985457,16.802989 29.3985457,16.802989 C29.740988,16.3567547 30,15.5559546 30,15.0081969 L30,10.4648712 C31.1956027,9.77325238 32,8.48056471 32,7 C32,4.790861 30.209139,3 28,3 C25.790861,3 24,4.790861 24,7 C24,8.48056471 24.8043973,9.77325238 26,10.4648712 L26,14.7083871 C26,14.8784435 25.9055559,15.0987329 25.7890533,15.2104147 C25.7890533,15.2104147 24.5373893,16.4126202 23.9488702,16.9515733 C22.5015398,18.2770075 21.1191354,19 20.090554,19 C19.0477772,19 17.6172728,18.2608988 16.1128852,16.9142923 C15.5030182,16.3683886 14.3672121,15.3403307 14.3672121,15.3403307 C14.1659605,15.1583364 14.0000086,14.7846305 14.0000192,14.5088473 C14.0000192,14.5088473 14.0000932,12.7539451 14.0001308,10.4647956 C15.1956614,9.77315812 16,8.48051074 16,7 C16,4.790861 14.209139,3 12,3 C9.790861,3 8,4.790861 8,7 C8,8.48056471 8.80439726,9.77325238 10,10.4648712 L10,15.0081969 C10,15.5446944 10.2736352,16.3534183 10.6111812,16.7893819 C10.6111812,16.7893819 13.8599776,21.3779363 18,22.6668724 L18,29.5351288 C16.8043973,30.2267476 16,31.5194353 16,33 C16,35.209139 17.790861,37 20,37 C22.209139,37 24,35.209139 24,33 C24,31.5194353 23.1956027,30.2267476 22,29.5351288 Z M14,7 C14,5.8954305 13.1045695,5 12,5 C10.8954305,5 10,5.8954305 10,7 C10,8.1045695 10.8954305,9 12,9 C13.1045695,9 14,8.1045695 14,7 Z M30,7 C30,5.8954305 29.1045695,5 28,5 C26.8954305,5 26,5.8954305 26,7 C26,8.1045695 26.8954305,9 28,9 C29.1045695,9 30,8.1045695 30,7 Z M22,33 C22,31.8954305 21.1045695,31 20,31 C18.8954305,31 18,31.8954305 18,33 C18,34.1045695 18.8954305,35 20,35 C21.1045695,35 22,34.1045695 22,33 Z"/> +</svg> diff --git a/app/views/shared/icons/_icon_status_cancel.svg b/app/views/shared/icons/_icon_status_cancel.svg new file mode 100644 index 00000000000..6a0bc1490c4 --- /dev/null +++ b/app/views/shared/icons/_icon_status_cancel.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <use stroke="#5C5C5C" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + <rect width="10" height="1" x="2" y="6.5" fill="#5C5C5C" transform="rotate(45 7 7)" rx=".3"/> + </g> +</svg> diff --git a/app/views/shared/icons/_icon_status_failed.svg b/app/views/shared/icons/_icon_status_failed.svg new file mode 100644 index 00000000000..c41ca18cae7 --- /dev/null +++ b/app/views/shared/icons/_icon_status_failed.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <use stroke="#D22852" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + <path fill="#D22852" d="M7.5,6.5 L7.5,4.30578971 C7.5,4.12531853 7.36809219,4 7.20537567,4 L6.79462433,4 C6.63904572,4 6.5,4.13690672 6.5,4.30578971 L6.5,6.5 L4.30578971,6.5 C4.12531853,6.5 4,6.63190781 4,6.79462433 L4,7.20537567 C4,7.36095428 4.13690672,7.5 4.30578971,7.5 L6.5,7.5 L6.5,9.69421029 C6.5,9.87468147 6.63190781,10 6.79462433,10 L7.20537567,10 C7.36095428,10 7.5,9.86309328 7.5,9.69421029 L7.5,7.5 L9.69421029,7.5 C9.87468147,7.5 10,7.36809219 10,7.20537567 L10,6.79462433 C10,6.63904572 9.86309328,6.5 9.69421029,6.5 L7.5,6.5 Z" transform="rotate(45 7 7)"/> + </g> +</svg> diff --git a/app/views/shared/icons/_icon_status_pending.svg b/app/views/shared/icons/_icon_status_pending.svg new file mode 100644 index 00000000000..035cd8b4ccc --- /dev/null +++ b/app/views/shared/icons/_icon_status_pending.svg @@ -0,0 +1,13 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <use stroke="#E75E40" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + <rect width="1" height="4" x="5" y="5" fill="#E75E40" rx=".3"/> + <rect width="1" height="4" x="8" y="5" fill="#E75E40" rx=".3"/> + </g> +</svg> diff --git a/app/views/shared/icons/_icon_status_running.svg b/app/views/shared/icons/_icon_status_running.svg new file mode 100644 index 00000000000..a48b3a25099 --- /dev/null +++ b/app/views/shared/icons/_icon_status_running.svg @@ -0,0 +1,12 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <use stroke="#2D9FD8" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + <path fill="#2D9FD8" d="M7,3.00800862 C9.09023405,3.13960661 10.7448145,4.87657932 10.7448145,7 C10.7448145,9.209139 8.95395346,11 6.74481446,11 C5.4560962,11 4.30972054,10.3905589 3.57817301,9.44416214 L7,7 L7,3.00800862 Z"/> + </g> +</svg> diff --git a/app/views/shared/icons/_icon_status_success.svg b/app/views/shared/icons/_icon_status_success.svg new file mode 100644 index 00000000000..260eab013a3 --- /dev/null +++ b/app/views/shared/icons/_icon_status_success.svg @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <use stroke="#31AF64" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + <g fill="#31AF64" transform="rotate(45 -.13 10.953)"> + <rect width="1" height="5" x="2" rx=".3"/> + <rect width="3" height="1" y="4" rx=".3"/> + </g> + </g> +</svg> diff --git a/app/views/shared/icons/_icon_status_warning.svg b/app/views/shared/icons/_icon_status_warning.svg new file mode 100644 index 00000000000..d47e7a1c93f --- /dev/null +++ b/app/views/shared/icons/_icon_status_warning.svg @@ -0,0 +1,15 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="14" height="14" viewBox="0 0 14 14" xmlns:xlink="http://www.w3.org/1999/xlink"> + <defs> + <circle id="a" cx="7" cy="7" r="7"/> + <mask id="b" width="14" height="14" x="0" y="0" fill="white"> + <use xlink:href="#a"/> + </mask> + </defs> + <g fill="none" fill-rule="evenodd"> + <g fill="#FF8A24" transform="translate(6 3)"> + <rect width="2" height="5" rx=".5"/> + <rect width="2" height="2" y="6" rx=".5"/> + </g> + <use stroke="#FF8A24" stroke-width="2" mask="url(#b)" xlink:href="#a"/> + </g> +</svg> diff --git a/app/views/shared/issuable/_sidebar.html.haml b/app/views/shared/issuable/_sidebar.html.haml index e020a7d4d00..8e2fcbdfab8 100644 --- a/app/views/shared/issuable/_sidebar.html.haml +++ b/app/views/shared/issuable/_sidebar.html.haml @@ -156,7 +156,7 @@ - project_ref = cross_project_reference(@project, issuable) .block.project-reference - .sidebar-collapsed-icon + .sidebar-collapsed-icon.dont-change-state = clipboard_button(clipboard_text: project_ref) .cross-project-reference.hide-collapsed %span diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index db2b4885861..c7f39868e71 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -2,7 +2,7 @@ - page_description @user.bio - content_for :page_specific_javascripts do = page_specific_javascript_tag('lib/d3.js') - = page_specific_javascript_tag('users/application.js') + = page_specific_javascript_tag('users/users_bundle.js') - header_title @user.name, user_path(@user) - @no_container = true diff --git a/app/workers/emails_on_push_worker.rb b/app/workers/emails_on_push_worker.rb index 8551288e2f2..0b6a01a3200 100644 --- a/app/workers/emails_on_push_worker.rb +++ b/app/workers/emails_on_push_worker.rb @@ -28,12 +28,12 @@ class EmailsOnPushWorker :push end - merge_base_sha = project.merge_base_commit(before_sha, after_sha).try(:sha) - diff_refs = nil compare = nil reverse_compare = false + if action == :push + merge_base_sha = project.merge_base_commit(before_sha, after_sha).try(:sha) compare = Gitlab::Git::Compare.new(project.repository.raw_repository, before_sha, after_sha) diff_refs = Gitlab::Diff::DiffRefs.new( diff --git a/app/workers/repository_archive_cache_worker.rb b/app/workers/repository_archive_cache_worker.rb index 47c5a670ed4..a2e49c61f59 100644 --- a/app/workers/repository_archive_cache_worker.rb +++ b/app/workers/repository_archive_cache_worker.rb @@ -4,6 +4,6 @@ class RepositoryArchiveCacheWorker sidekiq_options queue: :default def perform - Repository.clean_old_archives + RepositoryArchiveCleanUpService.new.execute end end |