diff options
author | Luke Bennett <lukeeeebennettplus@gmail.com> | 2018-06-06 10:50:13 +0300 |
---|---|---|
committer | Luke Bennett <lukeeeebennettplus@gmail.com> | 2018-06-06 10:50:13 +0300 |
commit | 0af7333ef9bea81f2a9d45f14a2ed4518b383d6d (patch) | |
tree | eda1fc06f17a7a3f657f6d52449a0bbc3cc664fd /app | |
parent | bb7f44aa8cf61d590bf45efb20c4ce19234ce03d (diff) | |
parent | a93e34acc3e52ccf81369d3345faaa48fee08581 (diff) |
Merge remote-tracking branch 'origin/master' into 39549-label-list-page-redesign-with-draggable-labels
Diffstat (limited to 'app')
62 files changed, 440 insertions, 283 deletions
diff --git a/app/assets/javascripts/importer_status.js b/app/assets/javascripts/importer_status.js index b469e1e2adc..52455885248 100644 --- a/app/assets/javascripts/importer_status.js +++ b/app/assets/javascripts/importer_status.js @@ -58,7 +58,7 @@ class ImporterStatus { job.find('.import-target').html(`<a href="${data.full_path}">${data.full_path}</a>`); $('table.import-jobs tbody').prepend(job); - job.addClass('active'); + job.addClass('table-active'); const connectingVerb = this.ciCdOnly ? __('connecting') : __('importing'); job.find('.import-actions').html(sprintf( _.escape(__('%{loadingIcon} Started')), { @@ -81,7 +81,7 @@ class ImporterStatus { switch (job.import_status) { case 'finished': - jobItem.removeClass('active').addClass('success'); + jobItem.removeClass('table-active').addClass('table-success'); statusField.html(`<span><i class="fa fa-check"></i> ${__('Done')}</span>`); break; case 'scheduled': diff --git a/app/assets/javascripts/performance_bar/components/detailed_metric.vue b/app/assets/javascripts/performance_bar/components/detailed_metric.vue index db8a0055acd..96189e7033a 100644 --- a/app/assets/javascripts/performance_bar/components/detailed_metric.vue +++ b/app/assets/javascripts/performance_bar/components/detailed_metric.vue @@ -56,6 +56,7 @@ export default { <gl-modal :id="`modal-peek-${metric}-details`" :header-title-text="header" + modal-size="lg" class="performance-bar-modal" > <table @@ -70,7 +71,7 @@ export default { <td v-for="key in keys" :key="key" - class="break-word" + class="break-word all-words" > {{ item[key] }} </td> diff --git a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue index f69fe03fcb3..c20d07a169d 100644 --- a/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue +++ b/app/assets/javascripts/vue_merge_request_widget/mr_widget_options.vue @@ -265,10 +265,10 @@ export default { /> <section - v-if="mr.maintainerEditAllowed" + v-if="mr.allowCollaboration" class="mr-info-list mr-links" > - {{ s__("mrWidget|Allows edits from maintainers") }} + {{ s__("mrWidget|Allows commits from members who can merge to the target branch") }} </section> <mr-widget-related-links diff --git a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js index e5b7e1f1c68..134aaacf9d2 100644 --- a/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js +++ b/app/assets/javascripts/vue_merge_request_widget/stores/mr_widget_store.js @@ -83,7 +83,7 @@ export default class MergeRequestStore { this.canBeMerged = data.can_be_merged || false; this.isMergeAllowed = data.mergeable || false; this.mergeOngoing = data.merge_ongoing; - this.maintainerEditAllowed = data.allow_maintainer_to_push; + this.allowCollaboration = data.allow_collaboration; // Cherry-pick and Revert actions related this.canCherryPickInCurrentMR = currentUser.can_cherry_pick_on_current_merge_request || false; diff --git a/app/assets/javascripts/vue_shared/components/gl_modal.vue b/app/assets/javascripts/vue_shared/components/gl_modal.vue index d5d5a7d3798..7ba58bd5959 100644 --- a/app/assets/javascripts/vue_shared/components/gl_modal.vue +++ b/app/assets/javascripts/vue_shared/components/gl_modal.vue @@ -1,15 +1,21 @@ <script> const buttonVariants = ['danger', 'primary', 'success', 'warning']; +const sizeVariants = ['sm', 'md', 'lg']; export default { name: 'GlModal', - props: { id: { type: String, required: false, default: null, }, + modalSize: { + type: String, + required: false, + default: 'md', + validator: value => sizeVariants.includes(value), + }, headerTitleText: { type: String, required: false, @@ -27,7 +33,11 @@ export default { default: '', }, }, - + computed: { + modalSizeClass() { + return this.modalSize === 'md' ? '' : `modal-${this.modalSize}`; + }, + }, methods: { emitCancel(event) { this.$emit('cancel', event); @@ -48,6 +58,7 @@ export default { > <div class="modal-dialog" + :class="modalSizeClass" role="document" > <div class="modal-content"> diff --git a/app/assets/javascripts/vue_shared/components/table_pagination.vue b/app/assets/javascripts/vue_shared/components/table_pagination.vue index 22fc5757447..6f231619f26 100644 --- a/app/assets/javascripts/vue_shared/components/table_pagination.vue +++ b/app/assets/javascripts/vue_shared/components/table_pagination.vue @@ -124,15 +124,18 @@ break; } }, + hideOnSmallScreen(item) { + return !item.first && !item.last && !item.next && !item.prev && !item.active; + }, }, }; </script> <template> <div v-if="showPagination" - class="gl-pagination" + class="gl-pagination prepend-top-default" > - <ul class="pagination clearfix"> + <ul class="pagination justify-content-center"> <li v-for="(item, index) in getItems" :key="index" @@ -142,12 +145,17 @@ 'js-next-button': item.next, 'js-last-button': item.last, 'js-first-button': item.first, + 'd-none d-md-block': hideOnSmallScreen(item), separator: item.separator, active: item.active, - disabled: item.disabled + disabled: item.disabled || item.separator }" + class="page-item" > - <a @click.prevent="changePage(item.title, item.disabled)"> + <a + @click.prevent="changePage(item.title, item.disabled)" + class="page-link" + > {{ item.title }} </a> </li> diff --git a/app/assets/stylesheets/bootstrap_migration.scss b/app/assets/stylesheets/bootstrap_migration.scss index d5679177f8f..3785aaa43f0 100644 --- a/app/assets/stylesheets/bootstrap_migration.scss +++ b/app/assets/stylesheets/bootstrap_migration.scss @@ -24,16 +24,54 @@ html { font-size: 14px; } +legend { + border-bottom: 1px solid $border-color; + margin-bottom: 20px; +} + button, html [type="button"], [type="reset"], -[type="submit"] { +[type="submit"], +[role="button"] { // Override bootstrap reboot -webkit-appearance: inherit; + cursor: pointer; } -[role="button"] { - cursor: pointer; +h1, +h2, +h3, +h4, +h5, +h6 { + color: $gl-text-color; + font-weight: 600; +} + +h1, +.h1, +h2, +.h2, +h3, +.h3 { + margin-top: 20px; + margin-bottom: 10px; +} + +h4, +.h4, +h5, +.h5, +h6, +.h6 { + margin-top: 10px; + margin-bottom: 10px; +} + +h5, +.h5 { + font-size: $gl-font-size; } input[type="file"] { @@ -59,6 +97,10 @@ a { } } +kbd { + display: inline-block; +} + code { padding: 2px 4px; color: $red-600; @@ -108,6 +150,16 @@ table { color: $gl-text-color-secondary !important; } +.bg-success, +.bg-primary, +.bg-info, +.bg-danger, +.bg-warning { + .card-header { + color: $white-light; + } +} + // Polyfill deprecated selectors .hidden { @@ -182,8 +234,13 @@ table { } .nav-tabs { + // Override bootstrap's default border + border-bottom: 0; + .nav-link { - border: 0; + border-top: 0; + border-left: 0; + border-right: 0; } .nav-item { diff --git a/app/assets/stylesheets/framework/common.scss b/app/assets/stylesheets/framework/common.scss index 1e7b9534275..996e5c1512d 100644 --- a/app/assets/stylesheets/framework/common.scss +++ b/app/assets/stylesheets/framework/common.scss @@ -448,6 +448,10 @@ img.emoji { .break-word { word-wrap: break-word; + + &.all-words { + word-break: break-word; + } } /** COMMON CLASSES **/ diff --git a/app/assets/stylesheets/framework/pagination.scss b/app/assets/stylesheets/framework/pagination.scss index d3e013590b6..50a1b1c446d 100644 --- a/app/assets/stylesheets/framework/pagination.scss +++ b/app/assets/stylesheets/framework/pagination.scss @@ -1,91 +1,6 @@ .gl-pagination { - text-align: center; - border-top: 1px solid $border-color; - margin: 0; - margin-top: 0; - - .pagination { - padding: 0; - margin: 20px 0; - - a { - cursor: pointer; - } - - .separator, - .separator:hover { - a { - cursor: default; - background-color: $gray-light; - padding: $gl-vert-padding; - } - } - } - - - .gap, - .gap:hover { - background-color: $gray-light; - padding: $gl-vert-padding; - cursor: default; - } -} - -.card > .gl-pagination { - margin: 0; -} - -/** - * Extra-small screen pagination. - */ -@media (max-width: 320px) { - .gl-pagination { - .first, - .last { - display: none; - } - - .page-item { - display: none; - - &.active { - display: inline; - } - } - } -} - -/** - * Small screen pagination - */ -@include media-breakpoint-down(xs) { - .gl-pagination { - .pagination li a { - padding: 6px 10px; - } - - .page-item { - display: none; - - &.active { - display: inline; - } - } - } -} - -/** - * Medium screen pagination - */ -@media (min-width: map-get($grid-breakpoints, xs)) and (max-width: map-get($grid-breakpoints, sm)) { - .gl-pagination { - .page-item { - display: none; - - &.active, - &.sibling { - display: inline; - } - } + a { + color: inherit; + text-decoration: none; } } diff --git a/app/assets/stylesheets/pages/issuable.scss b/app/assets/stylesheets/pages/issuable.scss index 4aea9740735..b42c232fd91 100644 --- a/app/assets/stylesheets/pages/issuable.scss +++ b/app/assets/stylesheets/pages/issuable.scss @@ -485,6 +485,15 @@ .sidebar-collapsed-user { padding-bottom: 0; margin-bottom: 10px; + + .author_link { + padding-left: 0; + + .avatar { + position: static; + margin: 0; + } + } } .issuable-header-btn { diff --git a/app/assets/stylesheets/pages/settings.scss b/app/assets/stylesheets/pages/settings.scss index 43c39c43bc1..16e999341da 100644 --- a/app/assets/stylesheets/pages/settings.scss +++ b/app/assets/stylesheets/pages/settings.scss @@ -102,10 +102,6 @@ .form-text.text-muted { margin-top: 0; } - - .label-light { - margin-bottom: 0; - } } .settings-list-icon { diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index db8a8cdc0d2..bc60a0a02e8 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -130,12 +130,17 @@ class ApplicationController < ActionController::Base end def access_denied!(message = nil) + # If we display a custom access denied message to the user, we don't want to + # hide existence of the resource, rather tell them they cannot access it using + # the provided message + status = message.present? ? :forbidden : :not_found + respond_to do |format| - format.any { head :not_found } + format.any { head status } format.html do render "errors/access_denied", layout: "errors", - status: 404, + status: status, locals: { message: message } end end diff --git a/app/controllers/groups/group_members_controller.rb b/app/controllers/groups/group_members_controller.rb index ef3eba80154..ef5d5e5c742 100644 --- a/app/controllers/groups/group_members_controller.rb +++ b/app/controllers/groups/group_members_controller.rb @@ -3,8 +3,12 @@ class Groups::GroupMembersController < Groups::ApplicationController include MembersPresentation include SortingHelper + def self.admin_not_required_endpoints + %i[index leave request_access] + end + # Authorize - before_action :authorize_admin_group_member!, except: [:index, :leave, :request_access] + before_action :authorize_admin_group_member!, except: admin_not_required_endpoints skip_cross_project_access_check :index, :create, :update, :destroy, :request_access, :approve_access_request, :leave, :resend_invite, diff --git a/app/controllers/groups/milestones_controller.rb b/app/controllers/groups/milestones_controller.rb index 5903689dc62..9bd51de7e97 100644 --- a/app/controllers/groups/milestones_controller.rb +++ b/app/controllers/groups/milestones_controller.rb @@ -76,12 +76,15 @@ class Groups::MilestonesController < Groups::ApplicationController def milestones milestones = MilestonesFinder.new(search_params).execute - legacy_milestones = GroupMilestone.build_collection(group, group_projects, params) @sort = params[:sort] || 'due_date_asc' MilestoneArray.sort(milestones + legacy_milestones, @sort) end + def legacy_milestones + GroupMilestone.build_collection(group, group_projects, params) + end + def milestone @milestone = if params[:title] diff --git a/app/controllers/projects/merge_requests/application_controller.rb b/app/controllers/projects/merge_requests/application_controller.rb index 29632bef7e5..8e4aeec16dc 100644 --- a/app/controllers/projects/merge_requests/application_controller.rb +++ b/app/controllers/projects/merge_requests/application_controller.rb @@ -15,7 +15,7 @@ class Projects::MergeRequests::ApplicationController < Projects::ApplicationCont def merge_request_params_attributes [ - :allow_maintainer_to_push, + :allow_collaboration, :assignee_id, :description, :force_remove_source_branch, diff --git a/app/controllers/projects/pipelines_controller.rb b/app/controllers/projects/pipelines_controller.rb index 6b40fc2fe68..768595ceeb4 100644 --- a/app/controllers/projects/pipelines_controller.rb +++ b/app/controllers/projects/pipelines_controller.rb @@ -23,8 +23,6 @@ class Projects::PipelinesController < Projects::ApplicationController @finished_count = limited_pipelines_count(project, 'finished') @pipelines_count = limited_pipelines_count(project) - Gitlab::Ci::Pipeline::Preloader.preload(@pipelines) - respond_to do |format| format.html format.json do @@ -34,7 +32,7 @@ class Projects::PipelinesController < Projects::ApplicationController pipelines: PipelineSerializer .new(project: @project, current_user: @current_user) .with_pagination(request, response) - .represent(@pipelines, disable_coverage: true), + .represent(@pipelines, disable_coverage: true, preload: true), count: { all: @pipelines_count, running: @running_count, diff --git a/app/controllers/users/terms_controller.rb b/app/controllers/users/terms_controller.rb index ab685b9106e..f7c6d1d59db 100644 --- a/app/controllers/users/terms_controller.rb +++ b/app/controllers/users/terms_controller.rb @@ -13,6 +13,10 @@ module Users def index @redirect = redirect_path + + if @term.accepted_by_user?(current_user) + flash.now[:notice] = "You have already accepted the Terms of Service as #{current_user.to_reference}" + end end def accept diff --git a/app/helpers/merge_requests_helper.rb b/app/helpers/merge_requests_helper.rb index 74251c260f0..5ff06b3e0fc 100644 --- a/app/helpers/merge_requests_helper.rb +++ b/app/helpers/merge_requests_helper.rb @@ -126,8 +126,8 @@ module MergeRequestsHelper link_to(url[merge_request.project, merge_request], data: data_attrs, &block) end - def allow_maintainer_push_unavailable_reason(merge_request) - return if merge_request.can_allow_maintainer_to_push?(current_user) + def allow_collaboration_unavailable_reason(merge_request) + return if merge_request.can_allow_collaboration?(current_user) minimum_visibility = [merge_request.target_project.visibility_level, merge_request.source_project.visibility_level].min diff --git a/app/helpers/projects_helper.rb b/app/helpers/projects_helper.rb index dfca799a53d..cdbb572f80a 100644 --- a/app/helpers/projects_helper.rb +++ b/app/helpers/projects_helper.rb @@ -389,11 +389,11 @@ module ProjectsHelper def project_status_css_class(status) case status when "started" - "active" + "table-active" when "failed" - "danger" + "table-danger" when "finished" - "success" + "table-success" end end @@ -412,7 +412,10 @@ module ProjectsHelper exports_path = File.join(Settings.shared['path'], 'tmp/project_exports') filtered_message = message.strip.gsub(exports_path, "[REPO EXPORT PATH]") - disk_path = Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path + disk_path = Gitlab::GitalyClient::StorageSettings.allow_disk_access do + Gitlab.config.repositories.storages[project.repository_storage].legacy_disk_path + end + filtered_message.gsub(disk_path.chomp('/'), "[REPOS PATH]") end diff --git a/app/helpers/workhorse_helper.rb b/app/helpers/workhorse_helper.rb index 9f78b80c71d..a82271ce0ee 100644 --- a/app/helpers/workhorse_helper.rb +++ b/app/helpers/workhorse_helper.rb @@ -6,7 +6,7 @@ module WorkhorseHelper headers.store(*Gitlab::Workhorse.send_git_blob(repository, blob)) headers['Content-Disposition'] = 'inline' headers['Content-Type'] = safe_content_type(blob) - head :ok # 'render nothing: true' messes up the Content-Type + render plain: "" end # Send a Git diff through Workhorse diff --git a/app/models/application_setting/term.rb b/app/models/application_setting/term.rb index e8ce0ccbb71..3b1dfe7e4ef 100644 --- a/app/models/application_setting/term.rb +++ b/app/models/application_setting/term.rb @@ -1,6 +1,7 @@ class ApplicationSetting class Term < ActiveRecord::Base include CacheMarkdownField + has_many :term_agreements validates :terms, presence: true @@ -9,5 +10,10 @@ class ApplicationSetting def self.latest order(:id).last end + + def accepted_by_user?(user) + user.accepted_term_id == id || + term_agreements.accepted.where(user: user).exists? + end end end diff --git a/app/models/ci/group.rb b/app/models/ci/group.rb index 87898b086c6..9c1046e8715 100644 --- a/app/models/ci/group.rb +++ b/app/models/ci/group.rb @@ -31,6 +31,14 @@ module Ci end end + def self.fabricate(stage) + stage.statuses.ordered.latest + .sort_by(&:sortable_name).group_by(&:group_name) + .map do |group_name, grouped_statuses| + self.new(stage, name: group_name, jobs: grouped_statuses) + end + end + private def commit_statuses diff --git a/app/models/ci/legacy_stage.rb b/app/models/ci/legacy_stage.rb index 9b536af672b..ce691875e42 100644 --- a/app/models/ci/legacy_stage.rb +++ b/app/models/ci/legacy_stage.rb @@ -16,11 +16,7 @@ module Ci end def groups - @groups ||= statuses.ordered.latest - .sort_by(&:sortable_name).group_by(&:group_name) - .map do |group_name, grouped_statuses| - Ci::Group.new(self, name: group_name, jobs: grouped_statuses) - end + @groups ||= Ci::Group.fabricate(self) end def to_param diff --git a/app/models/ci/pipeline.rb b/app/models/ci/pipeline.rb index 5eb30f4aaa0..eecd86349e4 100644 --- a/app/models/ci/pipeline.rb +++ b/app/models/ci/pipeline.rb @@ -18,7 +18,7 @@ module Ci s&.project&.pipelines&.maximum(:iid) || s&.project&.pipelines&.count end - has_many :stages + has_many :stages, -> { order(position: :asc) }, inverse_of: :pipeline has_many :statuses, class_name: 'CommitStatus', foreign_key: :commit_id, inverse_of: :pipeline has_many :builds, foreign_key: :commit_id, inverse_of: :pipeline has_many :trigger_requests, dependent: :destroy, foreign_key: :commit_id # rubocop:disable Cop/ActiveRecordDependent @@ -254,6 +254,20 @@ module Ci stage unless stage.statuses_count.zero? end + ## + # TODO We do not completely switch to persisted stages because of + # race conditions with setting statuses gitlab-ce#23257. + # + def ordered_stages + return legacy_stages unless complete? + + if Feature.enabled?('ci_pipeline_persisted_stages') + stages + else + legacy_stages + end + end + def legacy_stages # TODO, this needs refactoring, see gitlab-ce#26481. @@ -416,7 +430,7 @@ module Ci def number_of_warnings BatchLoader.for(id).batch(default_value: 0) do |pipeline_ids, loader| - Build.where(commit_id: pipeline_ids) + ::Ci::Build.where(commit_id: pipeline_ids) .latest .failed_but_allowed .group(:commit_id) @@ -508,7 +522,8 @@ module Ci def update_status retry_optimistic_lock(self) do - case latest_builds_status + case latest_builds_status.to_s + when 'created' then nil when 'pending' then enqueue when 'running' then run when 'success' then succeed @@ -516,6 +531,9 @@ module Ci when 'canceled' then cancel when 'skipped' then skip when 'manual' then block + else + raise HasStatus::UnknownStatusError, + "Unknown status `#{latest_builds_status}`" end end end diff --git a/app/models/ci/runner.rb b/app/models/ci/runner.rb index 57edd6a4956..8c9aacca8de 100644 --- a/app/models/ci/runner.rb +++ b/app/models/ci/runner.rb @@ -219,10 +219,8 @@ module Ci cache_attributes(values) - if persist_cached_data? - self.assign_attributes(values) - self.save if self.changed? - end + # We save data without validation, it will always change due to `contacted_at` + self.update_columns(values) if persist_cached_data? end def pick_build!(build) diff --git a/app/models/ci/stage.rb b/app/models/ci/stage.rb index 5a1eeb966aa..ea07f37e6c1 100644 --- a/app/models/ci/stage.rb +++ b/app/models/ci/stage.rb @@ -68,16 +68,44 @@ module Ci def update_status retry_optimistic_lock(self) do case statuses.latest.status + when 'created' then nil when 'pending' then enqueue when 'running' then run when 'success' then succeed when 'failed' then drop when 'canceled' then cancel when 'manual' then block - when 'skipped' then skip - else skip + when 'skipped', nil then skip + else + raise HasStatus::UnknownStatusError, + "Unknown status `#{statuses.latest.status}`" end end end + + def groups + @groups ||= Ci::Group.fabricate(self) + end + + def has_warnings? + number_of_warnings.positive? + end + + def number_of_warnings + BatchLoader.for(id).batch(default_value: 0) do |stage_ids, loader| + ::Ci::Build.where(stage_id: stage_ids) + .latest + .failed_but_allowed + .group(:stage_id) + .count + .each { |id, amount| loader.call(id, amount) } + end + end + + def detailed_status(current_user) + Gitlab::Ci::Status::Stage::Factory + .new(self, current_user) + .fabricate! + end end end diff --git a/app/models/concerns/avatarable.rb b/app/models/concerns/avatarable.rb index 13246a774e3..095897b08e3 100644 --- a/app/models/concerns/avatarable.rb +++ b/app/models/concerns/avatarable.rb @@ -4,11 +4,14 @@ module Avatarable included do prepend ShadowMethods include ObjectStorage::BackgroundMove + include Gitlab::Utils::StrongMemoize validate :avatar_type, if: ->(user) { user.avatar.present? && user.avatar_changed? } validates :avatar, file_size: { maximum: 200.kilobytes.to_i } mount_uploader :avatar, AvatarUploader + + after_initialize :add_avatar_to_batch end module ShadowMethods @@ -18,6 +21,17 @@ module Avatarable avatar_path(only_path: args.fetch(:only_path, true)) || super end + + def retrieve_upload(identifier, paths) + upload = retrieve_upload_from_batch(identifier) + + # This fallback is needed when deleting an upload, because we may have + # already been removed from the DB. We have to check an explicit `#nil?` + # because it's a BatchLoader instance. + upload = super if upload.nil? + + upload + end end def avatar_type @@ -52,4 +66,37 @@ module Avatarable url_base + avatar.local_url end + + # Path that is persisted in the tracking Upload model. Used to fetch the + # upload from the model. + def upload_paths(identifier) + avatar_mounter.blank_uploader.store_dirs.map { |store, path| File.join(path, identifier) } + end + + private + + def retrieve_upload_from_batch(identifier) + BatchLoader.for(identifier: identifier, model: self).batch(key: self.class) do |upload_params, loader, args| + model_class = args[:key] + paths = upload_params.flat_map do |params| + params[:model].upload_paths(params[:identifier]) + end + + Upload.where(uploader: AvatarUploader, path: paths).find_each do |upload| + model = model_class.instantiate('id' => upload.model_id) + + loader.call({ model: model, identifier: File.basename(upload.path) }, upload) + end + end + end + + def add_avatar_to_batch + return unless avatar_mounter + + avatar_mounter.read_identifiers.each { |identifier| retrieve_upload_from_batch(identifier) } + end + + def avatar_mounter + strong_memoize(:avatar_mounter) { _mounter(:avatar) } + end end diff --git a/app/models/concerns/has_status.rb b/app/models/concerns/has_status.rb index 7c3ed96bc28..72c236a0fc7 100644 --- a/app/models/concerns/has_status.rb +++ b/app/models/concerns/has_status.rb @@ -11,6 +11,8 @@ module HasStatus STATUSES_ENUM = { created: 0, pending: 1, running: 2, success: 3, failed: 4, canceled: 5, skipped: 6, manual: 7 }.freeze + UnknownStatusError = Class.new(StandardError) + class_methods do def status_sql scope_relevant = respond_to?(:exclude_ignored) ? exclude_ignored : all diff --git a/app/models/concerns/with_uploads.rb b/app/models/concerns/with_uploads.rb index e7cfffb775b..4245d083a49 100644 --- a/app/models/concerns/with_uploads.rb +++ b/app/models/concerns/with_uploads.rb @@ -36,4 +36,8 @@ module WithUploads upload.destroy end end + + def retrieve_upload(_identifier, paths) + uploads.find_by(path: paths) + end end diff --git a/app/models/merge_request.rb b/app/models/merge_request.rb index 4c1628d2bdb..535a2c362f2 100644 --- a/app/models/merge_request.rb +++ b/app/models/merge_request.rb @@ -1125,21 +1125,21 @@ class MergeRequest < ActiveRecord::Base project.merge_requests.merged.where(author_id: author_id).empty? end - def allow_maintainer_to_push - maintainer_push_possible? && super + def allow_collaboration + collaborative_push_possible? && super end - alias_method :allow_maintainer_to_push?, :allow_maintainer_to_push + alias_method :allow_collaboration?, :allow_collaboration - def maintainer_push_possible? + def collaborative_push_possible? source_project.present? && for_fork? && target_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE && source_project.visibility_level > Gitlab::VisibilityLevel::PRIVATE && !ProtectedBranch.protected?(source_project, source_branch) end - def can_allow_maintainer_to_push?(user) - maintainer_push_possible? && + def can_allow_collaboration?(user) + collaborative_push_possible? && Ability.allowed?(user, :push_code, source_project) end diff --git a/app/models/note.rb b/app/models/note.rb index 02f7a9b1e4f..41c04ae0571 100644 --- a/app/models/note.rb +++ b/app/models/note.rb @@ -435,6 +435,10 @@ class Note < ActiveRecord::Base super.merge(noteable: noteable) end + def retrieve_upload(_identifier, paths) + Upload.find_by(model: self, path: paths) + end + private def keep_around_commit diff --git a/app/models/personal_snippet.rb b/app/models/personal_snippet.rb index 82c1c4de3a0..355624fd552 100644 --- a/app/models/personal_snippet.rb +++ b/app/models/personal_snippet.rb @@ -1,2 +1,3 @@ class PersonalSnippet < Snippet + include WithUploads end diff --git a/app/models/project.rb b/app/models/project.rb index a094dbcb747..562198e2369 100644 --- a/app/models/project.rb +++ b/app/models/project.rb @@ -228,6 +228,7 @@ class Project < ActiveRecord::Base has_many :commit_statuses has_many :pipelines, class_name: 'Ci::Pipeline', inverse_of: :project + has_many :stages, class_name: 'Ci::Stage', inverse_of: :project # Ci::Build objects store data on the file system such as artifact files and # build traces. Currently there's no efficient way of removing this data in @@ -1425,8 +1426,14 @@ class Project < ActiveRecord::Base Ci::Runner.from("(#{union.to_sql}) ci_runners") end + def active_runners + strong_memoize(:active_runners) do + all_runners.active + end + end + def any_runners?(&block) - all_runners.active.any?(&block) + active_runners.any?(&block) end def valid_runners_token?(token) @@ -1968,18 +1975,18 @@ class Project < ActiveRecord::Base .limit(1) .select(1) source_of_merge_requests.opened - .where(allow_maintainer_to_push: true) + .where(allow_collaboration: true) .where('EXISTS (?)', developer_access_exists) end - def branch_allows_maintainer_push?(user, branch_name) + def branch_allows_collaboration?(user, branch_name) return false unless user cache_key = "user:#{user.id}:#{branch_name}:branch_allows_push" - memoized_results = strong_memoize(:branch_allows_maintainer_push) do + memoized_results = strong_memoize(:branch_allows_collaboration) do Hash.new do |result, cache_key| - result[cache_key] = fetch_branch_allows_maintainer_push?(user, branch_name) + result[cache_key] = fetch_branch_allows_collaboration?(user, branch_name) end end @@ -2121,18 +2128,18 @@ class Project < ActiveRecord::Base raise ex end - def fetch_branch_allows_maintainer_push?(user, branch_name) + def fetch_branch_allows_collaboration?(user, branch_name) check_access = -> do next false if empty_repo? merge_request = source_of_merge_requests.opened - .where(allow_maintainer_to_push: true) + .where(allow_collaboration: true) .find_by(source_branch: branch_name) merge_request&.can_be_merged_by?(user) end if RequestStore.active? - RequestStore.fetch("project-#{id}:branch-#{branch_name}:user-#{user.id}:branch_allows_maintainer_push") do + RequestStore.fetch("project-#{id}:branch-#{branch_name}:user-#{user.id}:branch_allows_collaboration") do check_access.call end else diff --git a/app/models/repository.rb b/app/models/repository.rb index 82cf47ba04e..0784891d1bf 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -270,6 +270,16 @@ class Repository end end + def archive_metadata(ref, storage_path, format = "tar.gz", append_sha:) + raw_repository.archive_metadata( + ref, + storage_path, + project.path, + format, + append_sha: append_sha + ) + end + def expire_tags_cache expire_method_caches(%i(tag_names tag_count)) @tags = nil diff --git a/app/models/term_agreement.rb b/app/models/term_agreement.rb index 8458a231bbd..c317bd0c90b 100644 --- a/app/models/term_agreement.rb +++ b/app/models/term_agreement.rb @@ -2,5 +2,7 @@ class TermAgreement < ActiveRecord::Base belongs_to :term, class_name: 'ApplicationSetting::Term' belongs_to :user + scope :accepted, -> { where(accepted: true) } + validates :user, :term, presence: true end diff --git a/app/policies/ci/build_policy.rb b/app/policies/ci/build_policy.rb index 8b65758f3e8..1c0cc7425ec 100644 --- a/app/policies/ci/build_policy.rb +++ b/app/policies/ci/build_policy.rb @@ -14,8 +14,8 @@ module Ci @subject.triggered_by?(@user) end - condition(:branch_allows_maintainer_push) do - @subject.project.branch_allows_maintainer_push?(@user, @subject.ref) + condition(:branch_allows_collaboration) do + @subject.project.branch_allows_collaboration?(@user, @subject.ref) end rule { protected_ref }.policy do @@ -25,7 +25,7 @@ module Ci rule { can?(:admin_build) | (can?(:update_build) & owner_of_job) }.enable :erase_build - rule { can?(:public_access) & branch_allows_maintainer_push }.policy do + rule { can?(:public_access) & branch_allows_collaboration }.policy do enable :update_build enable :update_commit_status end diff --git a/app/policies/ci/pipeline_policy.rb b/app/policies/ci/pipeline_policy.rb index 540e4235299..b81329d0625 100644 --- a/app/policies/ci/pipeline_policy.rb +++ b/app/policies/ci/pipeline_policy.rb @@ -4,13 +4,13 @@ module Ci condition(:protected_ref) { ref_protected?(@user, @subject.project, @subject.tag?, @subject.ref) } - condition(:branch_allows_maintainer_push) do - @subject.project.branch_allows_maintainer_push?(@user, @subject.ref) + condition(:branch_allows_collaboration) do + @subject.project.branch_allows_collaboration?(@user, @subject.ref) end rule { protected_ref }.prevent :update_pipeline - rule { can?(:public_access) & branch_allows_maintainer_push }.policy do + rule { can?(:public_access) & branch_allows_collaboration }.policy do enable :update_pipeline end diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb index 141070aef45..8260c6c7b84 100644 --- a/app/serializers/merge_request_widget_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -13,7 +13,7 @@ class MergeRequestWidgetEntity < IssuableEntity expose :squash expose :target_branch expose :target_project_id - expose :allow_maintainer_to_push + expose :allow_collaboration expose :should_be_rebased?, as: :should_be_rebased expose :ff_only_enabled do |merge_request| diff --git a/app/serializers/pipeline_details_entity.rb b/app/serializers/pipeline_details_entity.rb index 130968a44c1..8ba9cac53c4 100644 --- a/app/serializers/pipeline_details_entity.rb +++ b/app/serializers/pipeline_details_entity.rb @@ -1,6 +1,6 @@ class PipelineDetailsEntity < PipelineEntity expose :details do - expose :legacy_stages, as: :stages, using: StageEntity + expose :ordered_stages, as: :stages, using: StageEntity expose :artifacts, using: BuildArtifactEntity expose :manual_actions, using: BuildActionEntity end diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb index 7181f8a6b04..17a022539bb 100644 --- a/app/serializers/pipeline_serializer.rb +++ b/app/serializers/pipeline_serializer.rb @@ -1,14 +1,11 @@ class PipelineSerializer < BaseSerializer include WithPagination - - InvalidResourceError = Class.new(StandardError) - entity PipelineDetailsEntity def represent(resource, opts = {}) if resource.is_a?(ActiveRecord::Relation) - resource = resource.preload([ + :stages, :retryable_builds, :cancelable_statuses, :trigger_requests, @@ -20,10 +17,14 @@ class PipelineSerializer < BaseSerializer end if paginated? - super(@paginator.paginate(resource), opts) - else - super(resource, opts) + resource = paginator.paginate(resource) end + + if opts.delete(:preload) + resource = Gitlab::Ci::Pipeline::Preloader.preload!(resource) + end + + super(resource, opts) end def represent_status(resource) @@ -36,7 +37,7 @@ class PipelineSerializer < BaseSerializer def represent_stages(resource) return {} unless resource.present? - data = represent(resource, { only: [{ details: [:stages] }] }) + data = represent(resource, { only: [{ details: [:stages] }], preload: true }) data.dig(:details, :stages) || [] end end diff --git a/app/services/application_settings/update_service.rb b/app/services/application_settings/update_service.rb index e70445cfb67..7bcb8f49d0d 100644 --- a/app/services/application_settings/update_service.rb +++ b/app/services/application_settings/update_service.rb @@ -1,5 +1,7 @@ module ApplicationSettings class UpdateService < ApplicationSettings::BaseService + attr_reader :params, :application_setting + def execute update_terms(@params.delete(:terms)) diff --git a/app/services/applications/create_service.rb b/app/services/applications/create_service.rb index 35d45f25a71..e67af929954 100644 --- a/app/services/applications/create_service.rb +++ b/app/services/applications/create_service.rb @@ -2,8 +2,7 @@ module Applications class CreateService def initialize(current_user, params) @current_user = current_user - @params = params - @ip_address = @params.delete(:ip_address) + @params = params.except(:ip_address) end def execute(request = nil) diff --git a/app/services/merge_requests/base_service.rb b/app/services/merge_requests/base_service.rb index 231ab76fde4..4c420b38258 100644 --- a/app/services/merge_requests/base_service.rb +++ b/app/services/merge_requests/base_service.rb @@ -38,8 +38,8 @@ module MergeRequests def filter_params(merge_request) super - unless merge_request.can_allow_maintainer_to_push?(current_user) - params.delete(:allow_maintainer_to_push) + unless merge_request.can_allow_collaboration?(current_user) + params.delete(:allow_collaboration) end end diff --git a/app/services/test_hooks/project_service.rb b/app/services/test_hooks/project_service.rb index 01d5d774cd5..65183e84cce 100644 --- a/app/services/test_hooks/project_service.rb +++ b/app/services/test_hooks/project_service.rb @@ -1,11 +1,13 @@ module TestHooks class ProjectService < TestHooks::BaseService - private + attr_writer :project def project @project ||= hook.project end + private + def push_events_data throw(:validation_error, 'Ensure the project has at least one commit.') if project.empty_repo? diff --git a/app/uploaders/object_storage.rb b/app/uploaders/object_storage.rb index 3bb2e1ea63a..5aa1bc7227c 100644 --- a/app/uploaders/object_storage.rb +++ b/app/uploaders/object_storage.rb @@ -33,7 +33,7 @@ module ObjectStorage unless current_upload_satisfies?(paths, model) # the upload we already have isn't right, find the correct one - self.upload = uploads.find_by(model: model, path: paths) + self.upload = model&.retrieve_upload(identifier, paths) end super @@ -46,7 +46,7 @@ module ObjectStorage end def upload=(upload) - return unless upload + return if upload.nil? self.object_store = upload.store super diff --git a/app/views/admin/projects/show.html.haml b/app/views/admin/projects/show.html.haml index 0a22a142858..ccba1c461fc 100644 --- a/app/views/admin/projects/show.html.haml +++ b/app/views/admin/projects/show.html.haml @@ -116,8 +116,8 @@ .card-body = form_for @project, url: transfer_admin_project_path(@project), method: :put do |f| .form-group.row - = f.label :new_namespace_id, "Namespace", class: 'col-form-label col-sm-2' - .col-sm-10 + = f.label :new_namespace_id, "Namespace", class: 'col-form-label col-sm-3' + .col-sm-9 .dropdown = dropdown_toggle('Search for Namespace', { toggle: 'dropdown', field_name: 'new_namespace_id' }, { toggle_class: 'js-namespace-select large' }) .dropdown-menu.dropdown-select @@ -127,7 +127,7 @@ = dropdown_loading .form-group.row - .offset-sm-2.col-sm-10 + .offset-sm-3.col-sm-9 = f.submit 'Transfer', class: 'btn btn-primary' .card.repository-check diff --git a/app/views/admin/runners/index.html.haml b/app/views/admin/runners/index.html.haml index f38aeb151df..8dfd176f1b7 100644 --- a/app/views/admin/runners/index.html.haml +++ b/app/views/admin/runners/index.html.haml @@ -67,7 +67,7 @@ %th Projects %th Jobs %th Tags - %th= link_to 'Last contact', admin_runners_path(params.slice(:search).merge(sort: 'contacted_asc')) + %th= link_to 'Last contact', admin_runners_path(safe_params.slice(:search).merge(sort: 'contacted_asc')) %th - @runners.each do |runner| diff --git a/app/views/groups/_activities.html.haml b/app/views/groups/_activities.html.haml index fd6e7111f38..577c63503a8 100644 --- a/app/views/groups/_activities.html.haml +++ b/app/views/groups/_activities.html.haml @@ -1,4 +1,4 @@ -.nav-block +.nav-block.activities .controls = link_to group_path(@group, rss_url_options), class: 'btn rss-btn has-tooltip' , title: 'Subscribe' do %i.fa.fa-rss diff --git a/app/views/help/_shortcuts.html.haml b/app/views/help/_shortcuts.html.haml index 29db29235c1..c23fe0b5c49 100644 --- a/app/views/help/_shortcuts.html.haml +++ b/app/views/help/_shortcuts.html.haml @@ -18,71 +18,71 @@ %th Global Shortcuts %tr %td.shortcut - .key s + %kbd s %td Focus Search %tr %td.shortcut - .key f + %kbd f %td Focus Filter - if performance_bar_enabled? %tr %td.shortcut - .key p b + %kbd p b %td Show/hide the Performance Bar %tr %td.shortcut - .key ? + %kbd ? %td Show/hide this dialog %tr %td.shortcut - if browser.platform.mac? - .key ⌘ shift p + %kbd ⌘ shift p - else - .key ctrl shift p + %kbd ctrl shift p %td Toggle Markdown preview %tr %td.shortcut - .key + %kbd %i.fa.fa-arrow-up %td Edit last comment (when focused on an empty textarea) %tr %td.shortcut - .key shift t + %kbd shift t %td Go to todos %tr %td.shortcut - .key shift a + %kbd shift a %td Go to the activity feed %tr %td.shortcut - .key shift p + %kbd shift p %td Go to projects %tr %td.shortcut - .key shift i + %kbd shift i %td Go to issues %tr %td.shortcut - .key shift m + %kbd shift m %td Go to merge requests %tr %td.shortcut - .key shift g + %kbd shift g %td Go to groups %tr %td.shortcut - .key shift l + %kbd shift l %td Go to milestones %tr %td.shortcut - .key shift s + %kbd shift s %td Go to snippets %tbody @@ -91,21 +91,21 @@ %th Finding Project File %tr %td.shortcut - .key + %kbd %i.fa.fa-arrow-up %td Move selection up %tr %td.shortcut - .key + %kbd %i.fa.fa-arrow-down %td Move selection down %tr %td.shortcut - .key enter + %kbd enter %td Open Selection %tr %td.shortcut - .key esc + %kbd esc %td Go back .col-lg-4 %table.shortcut-mappings @@ -115,95 +115,95 @@ %th Project %tr %td.shortcut - .key g - .key p + %kbd g + %kbd p %td Go to the project's overview page %tr %td.shortcut - .key g - .key v + %kbd g + %kbd v %td Go to the project's activity feed %tr %td.shortcut - .key g - .key f + %kbd g + %kbd f %td Go to files %tr %td.shortcut - .key g - .key c + %kbd g + %kbd c %td Go to commits %tr %td.shortcut - .key g - .key j + %kbd g + %kbd j %td Go to jobs %tr %td.shortcut - .key g - .key n + %kbd g + %kbd n %td Go to network graph %tr %td.shortcut - .key g - .key d + %kbd g + %kbd d %td Go to repository charts %tr %td.shortcut - .key g - .key i + %kbd g + %kbd i %td Go to issues %tr %td.shortcut - .key g - .key b + %kbd g + %kbd b %td Go to issue boards %tr %td.shortcut - .key g - .key m + %kbd g + %kbd m %td Go to merge requests %tr %td.shortcut - .key g - .key e + %kbd g + %kbd e %td Go to environments %tr %td.shortcut - .key g - .key k + %kbd g + %kbd k %td Go to kubernetes %tr %td.shortcut - .key g - .key s + %kbd g + %kbd s %td Go to snippets %tr %td.shortcut - .key g - .key w + %kbd g + %kbd w %td Go to wiki %tr %td.shortcut - .key t + %kbd t %td Go to finding file %tr %td.shortcut - .key i + %kbd i %td New issue %tbody @@ -212,17 +212,17 @@ %th Project Files browsing %tr %td.shortcut - .key + %kbd %i.fa.fa-arrow-up %td Move selection up %tr %td.shortcut - .key + %kbd %i.fa.fa-arrow-down %td Move selection down %tr %td.shortcut - .key enter + %kbd enter %td Open Selection %tbody %tr @@ -230,7 +230,7 @@ %th Project File %tr %td.shortcut - .key y + %kbd y %td Go to file permalink %tbody %tr @@ -239,115 +239,115 @@ %tr %td.shortcut - if browser.platform.mac? - .key ⌘ p + %kbd ⌘ p - else - .key ctrl p + %kbd ctrl p %td Go to file .col-lg-4 %table.shortcut-mappings - %tbody.hidden-shortcut.network{ style: 'display:none' } + %tbody.hidden-shortcut{ style: 'display:none' } %tr %th %th Network Graph %tr %td.shortcut - .key + %kbd %i.fa.fa-arrow-left \/ - .key h + %kbd h %td Scroll left %tr %td.shortcut - .key + %kbd %i.fa.fa-arrow-right \/ - .key l + %kbd l %td Scroll right %tr %td.shortcut - .key + %kbd %i.fa.fa-arrow-up \/ - .key k + %kbd k %td Scroll up %tr %td.shortcut - .key + %kbd %i.fa.fa-arrow-down \/ - .key j + %kbd j %td Scroll down %tr %td.shortcut - .key + %kbd shift %i.fa.fa-arrow-up \/ - .key + %kbd shift k %td Scroll to top %tr %td.shortcut - .key + %kbd shift %i.fa.fa-arrow-down \/ - .key + %kbd shift j %td Scroll to bottom - %tbody.hidden-shortcut.issues{ style: 'display:none' } + %tbody.hidden-shortcut{ style: 'display:none' } %tr %th %th Issues %tr %td.shortcut - .key a + %kbd a %td Change assignee %tr %td.shortcut - .key m + %kbd m %td Change milestone %tr %td.shortcut - .key r + %kbd r %td Reply (quoting selected text) %tr %td.shortcut - .key e + %kbd e %td Edit issue %tr %td.shortcut - .key l + %kbd l %td Change Label - %tbody.hidden-shortcut.merge_requests{ style: 'display:none' } + %tbody.hidden-shortcut{ style: 'display:none' } %tr %th %th Merge Requests %tr %td.shortcut - .key a + %kbd a %td Change assignee %tr %td.shortcut - .key m + %kbd m %td Change milestone %tr %td.shortcut - .key r + %kbd r %td Reply (quoting selected text) %tr %td.shortcut - .key e + %kbd e %td Edit merge request %tr %td.shortcut - .key l + %kbd l %td Change Label - %tbody.hidden-shortcut.wiki{ style: 'display:none' } + %tbody.hidden-shortcut{ style: 'display:none' } %tr %th %th Wiki pages %tr %td.shortcut - .key e + %kbd e %td Edit wiki page diff --git a/app/views/kaminari/gitlab/_first_page.html.haml b/app/views/kaminari/gitlab/_first_page.html.haml index 369165da02a..3b7d4a1c578 100644 --- a/app/views/kaminari/gitlab/_first_page.html.haml +++ b/app/views/kaminari/gitlab/_first_page.html.haml @@ -5,5 +5,5 @@ -# total_pages: total number of pages -# per_page: number of items to fetch per page -# remote: data-remote -%li.first.page-item +%li.page-item.js-first-button = link_to_unless current_page.first?, raw(t 'views.pagination.first'), url, remote: remote, class: 'page-link' diff --git a/app/views/kaminari/gitlab/_gap.html.haml b/app/views/kaminari/gitlab/_gap.html.haml index 6eec30212d1..849f92fdc95 100644 --- a/app/views/kaminari/gitlab/_gap.html.haml +++ b/app/views/kaminari/gitlab/_gap.html.haml @@ -4,5 +4,5 @@ -# total_pages: total number of pages -# per_page: number of items to fetch per page -# remote: data-remote -%li.page-item.disabled +%li.page-item.disabled.d-none.d-md-block = link_to raw(t 'views.pagination.truncate'), '#', class: 'page-link' diff --git a/app/views/kaminari/gitlab/_last_page.html.haml b/app/views/kaminari/gitlab/_last_page.html.haml index 8b49db58281..7836e17f877 100644 --- a/app/views/kaminari/gitlab/_last_page.html.haml +++ b/app/views/kaminari/gitlab/_last_page.html.haml @@ -5,5 +5,5 @@ -# total_pages: total number of pages -# per_page: number of items to fetch per page -# remote: data-remote -%li.last.page-item +%li.page-item.js-last-button = link_to_unless current_page.last?, raw(t 'views.pagination.last'), url, {remote: remote, class: 'page-link'} diff --git a/app/views/kaminari/gitlab/_next_page.html.haml b/app/views/kaminari/gitlab/_next_page.html.haml index 05f151555ad..a7fa1a21a6c 100644 --- a/app/views/kaminari/gitlab/_next_page.html.haml +++ b/app/views/kaminari/gitlab/_next_page.html.haml @@ -8,5 +8,5 @@ - page_url = current_page.last? ? '#' : url -%li.page-item{ class: ('disabled' if current_page.last?) } +%li.page-item.js-next-button{ class: ('disabled' if current_page.last?) } = link_to raw(t 'views.pagination.next'), page_url, rel: 'next', remote: remote, class: 'page-link' diff --git a/app/views/kaminari/gitlab/_page.html.haml b/app/views/kaminari/gitlab/_page.html.haml index 8a40e13a537..d0dc1784540 100644 --- a/app/views/kaminari/gitlab/_page.html.haml +++ b/app/views/kaminari/gitlab/_page.html.haml @@ -6,5 +6,5 @@ -# total_pages: total number of pages -# per_page: number of items to fetch per page -# remote: data-remote -%li.page-item.js-pagination-page{ class: [active_when(page.current?), ('sibling' if page.next? || page.prev?)] } +%li.page-item.js-pagination-page{ class: [active_when(page.current?), ('sibling' if page.next? || page.prev?), ('d-none d-md-block' if !page.current?) ] } = link_to page, url, { remote: remote, rel: page.next? ? 'next' : page.prev? ? 'prev' : nil, class: 'page-link' } diff --git a/app/views/kaminari/gitlab/_paginator.html.haml b/app/views/kaminari/gitlab/_paginator.html.haml index a6435deb4bf..ac9e274dbc7 100644 --- a/app/views/kaminari/gitlab/_paginator.html.haml +++ b/app/views/kaminari/gitlab/_paginator.html.haml @@ -6,7 +6,7 @@ -# remote: data-remote -# paginator: the paginator that renders the pagination tags inside = paginator.render do - .gl-pagination + .gl-pagination.prepend-top-default %ul.pagination.justify-content-center - unless current_page.first? = first_page_tag unless total_pages < 5 # As kaminari will always show the first 5 pages diff --git a/app/views/kaminari/gitlab/_prev_page.html.haml b/app/views/kaminari/gitlab/_prev_page.html.haml index f4a11a449b7..12b0e106a62 100644 --- a/app/views/kaminari/gitlab/_prev_page.html.haml +++ b/app/views/kaminari/gitlab/_prev_page.html.haml @@ -8,5 +8,5 @@ - page_url = current_page.first? ? '#' : url -%li.page-item{ class: ('disabled' if current_page.first?) } +%li.page-item.js-previous-button{ class: ('disabled' if current_page.first?) } = link_to raw(t 'views.pagination.previous'), page_url, rel: 'prev', remote: remote, class: 'page-link' diff --git a/app/views/kaminari/gitlab/_without_count.html.haml b/app/views/kaminari/gitlab/_without_count.html.haml index 1425a809052..f780400ebcb 100644 --- a/app/views/kaminari/gitlab/_without_count.html.haml +++ b/app/views/kaminari/gitlab/_without_count.html.haml @@ -1,5 +1,5 @@ -.gl-pagination - %ul.pagination.clearfix +.gl-pagination.prepend-top-default + %ul.pagination.justify-content-center - if previous_path %li.page-item.prev = link_to(t('views.pagination.previous'), previous_path, rel: 'prev', class: 'page-link') diff --git a/app/views/projects/new.html.haml b/app/views/projects/new.html.haml index 35a09f06bfa..5bb1bfb7059 100644 --- a/app/views/projects/new.html.haml +++ b/app/views/projects/new.html.haml @@ -29,16 +29,16 @@ .col-lg-9.js-toggle-container %ul.nav.nav-tabs.nav-links.gitlab-tabs{ role: 'tablist' } - %li{ class: active_when(active_tab == 'blank'), role: 'presentation' } - %a{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab' }, role: 'tab' } + %li.nav-item{ role: 'presentation' } + %a.nav-link.active{ href: '#blank-project-pane', id: 'blank-project-tab', data: { toggle: 'tab' }, role: 'tab' } %span.d-none.d-sm-block Blank project %span.d-block.d-sm-none Blank - %li{ class: active_when(active_tab == 'template'), role: 'presentation' } - %a{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab' }, role: 'tab' } + %li.nav-item{ role: 'presentation' } + %a.nav-link{ href: '#create-from-template-pane', id: 'create-from-template-tab', data: { toggle: 'tab' }, role: 'tab' } %span.d-none.d-sm-block Create from template %span.d-block.d-sm-none Template - %li{ class: active_when(active_tab == 'import'), role: 'presentation' } - %a{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' } + %li.nav-item{ role: 'presentation' } + %a.nav-link{ href: '#import-project-pane', id: 'import-project-tab', data: { toggle: 'tab' }, role: 'tab' } %span.d-none.d-sm-block Import project %span.d-block.d-sm-none Import diff --git a/app/views/projects/pages/_destroy.haml b/app/views/projects/pages/_destroy.haml index 4ada19a1368..9b77c4e3494 100644 --- a/app/views/projects/pages/_destroy.haml +++ b/app/views/projects/pages/_destroy.haml @@ -5,7 +5,7 @@ .errors-holder .card-body %p - Removing the pages will prevent from exposing them to outside world. + Removing pages will prevent them from being exposed to the outside world. .form-actions = link_to 'Remove pages', project_pages_path(@project), data: { confirm: 'Are you sure?'}, method: :delete, class: "btn btn-remove" - else diff --git a/app/views/shared/issuable/form/_contribution.html.haml b/app/views/shared/issuable/form/_contribution.html.haml index b34549240e0..519b5fae846 100644 --- a/app/views/shared/issuable/form/_contribution.html.haml +++ b/app/views/shared/issuable/form/_contribution.html.haml @@ -12,9 +12,9 @@ = _('Contribution') .col-sm-10 .form-check - = form.check_box :allow_maintainer_to_push, disabled: !issuable.can_allow_maintainer_to_push?(current_user), class: 'form-check-input' - = form.label :allow_maintainer_to_push, class: 'form-check-label' do - = _('Allow edits from maintainers.') - = link_to 'About this feature', help_page_path('user/project/merge_requests/maintainer_access') + = form.check_box :allow_collaboration, disabled: !issuable.can_allow_collaboration?(current_user), class: 'form-check-input' + = form.label :allow_collaboration, class: 'form-check-label' do + = _('Allow commits from members who can merge to the target branch.') + = link_to 'About this feature', help_page_path('user/project/merge_requests/allow_collaboration') .form-text.text-muted - = allow_maintainer_push_unavailable_reason(issuable) + = allow_collaboration_unavailable_reason(issuable) diff --git a/app/views/shared/projects/_edit_information.html.haml b/app/views/shared/projects/_edit_information.html.haml index ec9dc8f62c2..9230e045a81 100644 --- a/app/views/shared/projects/_edit_information.html.haml +++ b/app/views/shared/projects/_edit_information.html.haml @@ -1,6 +1,6 @@ - unless can?(current_user, :push_code, @project) .inline.prepend-left-10 - - if @project.branch_allows_maintainer_push?(current_user, selected_branch) + - if @project.branch_allows_collaboration?(current_user, selected_branch) = commit_in_single_accessible_branch - else = commit_in_fork_help diff --git a/app/views/users/terms/index.html.haml b/app/views/users/terms/index.html.haml index b9f25a71170..33cddf63952 100644 --- a/app/views/users/terms/index.html.haml +++ b/app/views/users/terms/index.html.haml @@ -7,6 +7,10 @@ .float-right = button_to accept_term_path(@term, redirect_params), class: 'btn btn-success prepend-left-8' do = _('Accept terms') + - else + .pull-right + = link_to root_path, class: 'btn btn-success prepend-left-8' do + = _('Continue') - if can?(current_user, :decline_terms, @term) .float-right = button_to decline_term_path(@term, redirect_params), class: 'btn btn-default prepend-left-8' do |