diff options
32 files changed, 533 insertions, 187 deletions
diff --git a/app/assets/javascripts/commit/image_file.js b/app/assets/javascripts/commit/image_file.js index 6c04e0beb4d..60c2059a876 100644 --- a/app/assets/javascripts/commit/image_file.js +++ b/app/assets/javascripts/commit/image_file.js @@ -1,4 +1,4 @@ -/* eslint-disable func-names, no-var, no-else-return, consistent-return, one-var, no-return-assign, no-unused-expressions, no-sequences */ +/* eslint-disable func-names, no-var, no-else-return, consistent-return, one-var, no-return-assign */ import $ from 'jquery'; @@ -9,40 +9,29 @@ const viewModes = ['two-up', 'swipe']; export default class ImageFile { constructor(file) { this.file = file; - this.requestImageInfo( - $('.two-up.view .frame.deleted img', this.file), - (function(_this) { - return function() { - return _this.requestImageInfo($('.two-up.view .frame.added img', _this.file), () => { - _this.initViewModes(); - - // Load two-up view after images are loaded - // so that we can display the correct width and height information - const $images = $('.two-up.view img', _this.file); - - $images.waitForImages(() => { - _this.initView('two-up'); - }); - }); - }; - })(this), + this.requestImageInfo($('.two-up.view .frame.deleted img', this.file), () => + this.requestImageInfo($('.two-up.view .frame.added img', this.file), () => { + this.initViewModes(); + + // Load two-up view after images are loaded + // so that we can display the correct width and height information + const $images = $('.two-up.view img', this.file); + + $images.waitForImages(() => { + this.initView('two-up'); + }); + }), ); } initViewModes() { const viewMode = viewModes[0]; $('.view-modes', this.file).removeClass('hide'); - $('.view-modes-menu', this.file).on( - 'click', - 'li', - (function(_this) { - return function(event) { - if (!$(event.currentTarget).hasClass('active')) { - return _this.activateViewMode(event.currentTarget.className); - } - }; - })(this), - ); + $('.view-modes-menu', this.file).on('click', 'li', event => { + if (!$(event.currentTarget).hasClass('active')) { + return this.activateViewMode(event.currentTarget.className); + } + }); return this.activateViewMode(viewMode); } @@ -51,15 +40,10 @@ export default class ImageFile { .removeClass('active') .filter(`.${viewMode}`) .addClass('active'); - return $(`.view:visible:not(.${viewMode})`, this.file).fadeOut( - 200, - (function(_this) { - return function() { - $(`.view.${viewMode}`, _this.file).fadeIn(200); - return _this.initView(viewMode); - }; - })(this), - ); + return $(`.view:visible:not(.${viewMode})`, this.file).fadeOut(200, () => { + $(`.view.${viewMode}`, this.file).fadeIn(200); + return this.initView(viewMode); + }); } initView(viewMode) { @@ -103,22 +87,18 @@ export default class ImageFile { .on('touchmove', dragMove); } - prepareFrames(view) { + static prepareFrames(view) { var maxHeight, maxWidth; maxWidth = 0; maxHeight = 0; $('.frame', view) - .each( - (function() { - return function(index, frame) { - var height, width; - width = $(frame).width(); - height = $(frame).height(); - maxWidth = width > maxWidth ? width : maxWidth; - return (maxHeight = height > maxHeight ? height : maxHeight); - }; - })(this), - ) + .each((index, frame) => { + var height, width; + width = $(frame).width(); + height = $(frame).height(); + maxWidth = width > maxWidth ? width : maxWidth; + return (maxHeight = height > maxHeight ? height : maxHeight); + }) .css({ width: maxWidth, height: maxHeight, @@ -128,104 +108,95 @@ export default class ImageFile { views = { 'two-up': function() { - return $('.two-up.view .wrap', this.file).each( - (function(_this) { - return function(index, wrap) { - $('img', wrap).each(function() { - var currentWidth; - currentWidth = $(this).width(); - if (currentWidth > availWidth / 2) { - return $(this).width(availWidth / 2); - } - }); - return _this.requestImageInfo($('img', wrap), (width, height) => { - $('.image-info .meta-width', wrap).text(`${width}px`); - $('.image-info .meta-height', wrap).text(`${height}px`); - return $('.image-info', wrap).removeClass('hide'); - }); - }; - })(this), - ); + return $('.two-up.view .wrap', this.file).each((index, wrap) => { + $('img', wrap).each(function() { + var currentWidth; + currentWidth = $(this).width(); + if (currentWidth > availWidth / 2) { + return $(this).width(availWidth / 2); + } + }); + return this.requestImageInfo($('img', wrap), (width, height) => { + $('.image-info .meta-width', wrap).text(`${width}px`); + $('.image-info .meta-height', wrap).text(`${height}px`); + return $('.image-info', wrap).removeClass('hide'); + }); + }); }, swipe() { var maxHeight, maxWidth; maxWidth = 0; maxHeight = 0; - return $('.swipe.view', this.file).each( - (function(_this) { - return function(index, view) { - var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding, ref; - (ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref); - $swipeFrame = $('.swipe-frame', view); - $swipeWrap = $('.swipe-wrap', view); - $swipeBar = $('.swipe-bar', view); - - $swipeFrame.css({ - width: maxWidth + 16, - height: maxHeight + 28, - }); - $swipeWrap.css({ - width: maxWidth + 1, - height: maxHeight + 2, - }); - // Set swipeBar left position to match image frame - $swipeBar.css({ - left: 1, - }); - - wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10); - - _this.initDraggable($swipeBar, wrapPadding, (e, left) => { - if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) { - $swipeWrap.width(maxWidth + 1 - left); - $swipeBar.css('left', left); - } - }); - }; - })(this), - ); + return $('.swipe.view', this.file).each((index, view) => { + var $swipeWrap, $swipeBar, $swipeFrame, wrapPadding; + const ref = ImageFile.prepareFrames(view); + [maxWidth, maxHeight] = ref; + $swipeFrame = $('.swipe-frame', view); + $swipeWrap = $('.swipe-wrap', view); + $swipeBar = $('.swipe-bar', view); + + $swipeFrame.css({ + width: maxWidth + 16, + height: maxHeight + 28, + }); + $swipeWrap.css({ + width: maxWidth + 1, + height: maxHeight + 2, + }); + // Set swipeBar left position to match image frame + $swipeBar.css({ + left: 1, + }); + + wrapPadding = parseInt($swipeWrap.css('right').replace('px', ''), 10); + + this.initDraggable($swipeBar, wrapPadding, (e, left) => { + if (left > 0 && left < $swipeFrame.width() - wrapPadding * 2) { + $swipeWrap.width(maxWidth + 1 - left); + $swipeBar.css('left', left); + } + }); + }); }, 'onion-skin': function() { var dragTrackWidth, maxHeight, maxWidth; maxWidth = 0; maxHeight = 0; dragTrackWidth = $('.drag-track', this.file).width() - $('.dragger', this.file).width(); - return $('.onion-skin.view', this.file).each( - (function(_this) { - return function(index, view) { - var $frame, $track, $dragger, $frameAdded, framePadding, ref; - (ref = _this.prepareFrames(view)), ([maxWidth, maxHeight] = ref); - $frame = $('.onion-skin-frame', view); - $frameAdded = $('.frame.added', view); - $track = $('.drag-track', view); - $dragger = $('.dragger', $track); - - $frame.css({ - width: maxWidth + 16, - height: maxHeight + 28, - }); - $('.swipe-wrap', view).css({ - width: maxWidth + 1, - height: maxHeight + 2, - }); - $dragger.css({ - left: dragTrackWidth, - }); - - $frameAdded.css('opacity', 1); - framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10); - - _this.initDraggable($dragger, framePadding, (e, left) => { - var opacity = left / dragTrackWidth; - - if (opacity >= 0 && opacity <= 1) { - $dragger.css('left', left); - $frameAdded.css('opacity', opacity); - } - }); - }; - })(this), - ); + return $('.onion-skin.view', this.file).each((index, view) => { + var $frame, $track, $dragger, $frameAdded, framePadding; + + const ref = ImageFile.prepareFrames(view); + [maxWidth, maxHeight] = ref; + $frame = $('.onion-skin-frame', view); + $frameAdded = $('.frame.added', view); + $track = $('.drag-track', view); + $dragger = $('.dragger', $track); + + $frame.css({ + width: maxWidth + 16, + height: maxHeight + 28, + }); + $('.swipe-wrap', view).css({ + width: maxWidth + 1, + height: maxHeight + 2, + }); + $dragger.css({ + left: dragTrackWidth, + }); + + $frameAdded.css('opacity', 1); + framePadding = parseInt($frameAdded.css('right').replace('px', ''), 10); + + this.initDraggable($dragger, framePadding, (e, left) => { + var opacity = left / dragTrackWidth; + + if (opacity >= 0 && opacity <= 1) { + $dragger.css('left', left); + $frameAdded.css('opacity', opacity); + } + }); + }); }, }; @@ -235,14 +206,7 @@ export default class ImageFile { if (domImg.complete) { return callback.call(this, domImg.naturalWidth, domImg.naturalHeight); } else { - return img.on( - 'load', - (function(_this) { - return function() { - return callback.call(_this, domImg.naturalWidth, domImg.naturalHeight); - }; - })(this), - ); + return img.on('load', () => callback.call(this, domImg.naturalWidth, domImg.naturalHeight)); } } } diff --git a/app/assets/javascripts/pages/admin/abuse_reports/index.js b/app/assets/javascripts/pages/admin/abuse_reports/index.js index d76b1f174fc..d97e24d9e0b 100644 --- a/app/assets/javascripts/pages/admin/abuse_reports/index.js +++ b/app/assets/javascripts/pages/admin/abuse_reports/index.js @@ -1,3 +1,8 @@ +/* eslint-disable no-new */ import AbuseReports from './abuse_reports'; +import UsersSelect from '~/users_select'; -document.addEventListener('DOMContentLoaded', () => new AbuseReports()); +document.addEventListener('DOMContentLoaded', () => { + new AbuseReports(); + new UsersSelect(); +}); diff --git a/app/assets/stylesheets/framework/modal.scss b/app/assets/stylesheets/framework/modal.scss index 9c924559135..757264add93 100644 --- a/app/assets/stylesheets/framework/modal.scss +++ b/app/assets/stylesheets/framework/modal.scss @@ -49,7 +49,7 @@ line-height: $line-height-base; position: relative; min-height: $modal-body-height; - padding: #{2 * $grid-size} #{6 * $grid-size} #{2 * $grid-size} #{2 * $grid-size}; + padding: #{2 * $grid-size}; text-align: left; white-space: normal; @@ -70,9 +70,9 @@ margin: 0; } - .btn + .btn:not(.dropdown-toggle-split), .btn + .btn-group, - .btn-group + .btn { + .btn-group + .btn, + .btn-group + .btn-group { margin-left: $grid-size; } @@ -83,13 +83,6 @@ @include media-breakpoint-down(xs) { flex-direction: column; - .btn + .btn:not(.dropdown-toggle-split), - .btn + .btn-group, - .btn-group + .btn { - margin-left: 0; - margin-top: $grid-size; - } - .btn-group .btn + .btn { margin-left: -1px; margin-top: 0; diff --git a/app/assets/stylesheets/pages/projects.scss b/app/assets/stylesheets/pages/projects.scss index 3f8bdc82eff..a50d31f15df 100644 --- a/app/assets/stylesheets/pages/projects.scss +++ b/app/assets/stylesheets/pages/projects.scss @@ -274,6 +274,12 @@ height: 24px; } + .git-clone-holder { + .btn { + height: auto; + } + } + .dropdown-toggle, .clone-dropdown-btn { .fa { diff --git a/app/assets/stylesheets/pages/reports.scss b/app/assets/stylesheets/pages/reports.scss index 0fbf7033aa5..390ebd48685 100644 --- a/app/assets/stylesheets/pages/reports.scss +++ b/app/assets/stylesheets/pages/reports.scss @@ -131,7 +131,6 @@ .modal-security-report-dast { .modal-dialog { - width: $modal-lg; max-width: $modal-lg; } diff --git a/app/controllers/admin/abuse_reports_controller.rb b/app/controllers/admin/abuse_reports_controller.rb index c26c994cf5f..31d825c235b 100644 --- a/app/controllers/admin/abuse_reports_controller.rb +++ b/app/controllers/admin/abuse_reports_controller.rb @@ -1,12 +1,9 @@ # frozen_string_literal: true class Admin::AbuseReportsController < Admin::ApplicationController - # rubocop: disable CodeReuse/ActiveRecord def index - @abuse_reports = AbuseReport.order(id: :desc).page(params[:page]) - @abuse_reports = @abuse_reports.includes(:user, :reporter) + @abuse_reports = AbuseReportsFinder.new(params).execute end - # rubocop: enable CodeReuse/ActiveRecord def destroy abuse_report = AbuseReport.find(params[:id]) diff --git a/app/finders/abuse_reports_finder.rb b/app/finders/abuse_reports_finder.rb new file mode 100644 index 00000000000..04043f36426 --- /dev/null +++ b/app/finders/abuse_reports_finder.rb @@ -0,0 +1,18 @@ +# frozen_string_literal: true + +class AbuseReportsFinder + attr_reader :params + + def initialize(params = {}) + @params = params + end + + def execute + reports = AbuseReport.all + reports = reports.by_user(params[:user_id]) if params[:user_id].present? + + reports.with_order_id_desc + .with_users + .page(params[:page]) + end +end diff --git a/app/models/abuse_report.rb b/app/models/abuse_report.rb index a3a1748142f..7cfebf0473f 100644 --- a/app/models/abuse_report.rb +++ b/app/models/abuse_report.rb @@ -2,6 +2,7 @@ class AbuseReport < ApplicationRecord include CacheMarkdownField + include Sortable cache_markdown_field :message, pipeline: :single_line @@ -13,6 +14,9 @@ class AbuseReport < ApplicationRecord validates :message, presence: true validates :user_id, uniqueness: { message: 'has already been reported' } + scope :by_user, -> (user) { where(user_id: user) } + scope :with_users, -> { includes(:reporter, :user) } + # For CacheMarkdownField alias_method :author, :reporter diff --git a/app/models/award_emoji.rb b/app/models/award_emoji.rb index 24fcb97db6e..5a33a8f89df 100644 --- a/app/models/award_emoji.rb +++ b/app/models/award_emoji.rb @@ -6,11 +6,14 @@ class AwardEmoji < ApplicationRecord include Participable include GhostUser + include Importable belongs_to :awardable, polymorphic: true # rubocop:disable Cop/PolymorphicAssociations belongs_to :user - validates :awardable, :user, presence: true + validates :user, presence: true + validates :awardable, presence: true, unless: :importing? + validates :name, presence: true, inclusion: { in: Gitlab::Emoji.emojis_names } validates :name, uniqueness: { scope: [:user, :awardable_type, :awardable_id] }, unless: :ghost_user? diff --git a/app/views/admin/abuse_reports/index.html.haml b/app/views/admin/abuse_reports/index.html.haml index cc29657a439..e3d78b3058f 100644 --- a/app/views/admin/abuse_reports/index.html.haml +++ b/app/views/admin/abuse_reports/index.html.haml @@ -1,6 +1,18 @@ -- page_title 'Abuse Reports' -%h3.page-title Abuse Reports -%hr +- page_title _('Abuse Reports') + +%h3.page-title= _('Abuse Reports') + +.row-content-block.second-block + = form_tag admin_abuse_reports_path, method: :get, class: 'filter-form' do + .filter-categories.flex-fill + .filter-item.inline + = dropdown_tag(user_dropdown_label(params[:user_id], 'User'), + options: { toggle_class: 'js-filter-submit js-user-search', + title: _('Filter by user'), filter: true, filterInput: 'input#user-search', + dropdown_class: 'dropdown-menu-selectable dropdown-menu-user js-filter-submit', + placeholder: _('Search users'), + data: { current_user: true, field_name: 'user_id' }}) + .abuse-reports - if @abuse_reports.present? .table-holder diff --git a/app/views/projects/pages/_access.html.haml b/app/views/projects/pages/_access.html.haml index 178f0acc5b9..08dcba2afd7 100644 --- a/app/views/projects/pages/_access.html.haml +++ b/app/views/projects/pages/_access.html.haml @@ -13,5 +13,11 @@ - @project.pages_domains.each do |domain| %p = external_link(domain.url, domain.url) + - unless @project.public_pages? + .card-footer.alert-warning + - help_page = help_page_path('/user/project/pages/pages_access_control') + - link_start = '<a href="%{url}" target="_blank" class="alert-link" rel="noopener noreferrer">'.html_safe % { url: help_page } + - link_end = '</a>'.html_safe + = s_('GitLabPages|Access Control is enabled for this Pages website; only authorized users will be able to access it. To make your website publicly available, navigate to your project\'s %{strong_start}Settings > General > Visibility%{strong_end} and select %{strong_start}Everyone%{strong_end} in pages section. Read the %{link_start}documentation%{link_end} for more information.').html_safe % { link_start: link_start, link_end: link_end, strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } .card-footer.alert-primary = s_('GitLabPages|It may take up to 30 minutes before the site is available after the first deployment.') diff --git a/app/views/projects/pages/show.html.haml b/app/views/projects/pages/show.html.haml index 0e1f281410a..3ec87597849 100644 --- a/app/views/projects/pages/show.html.haml +++ b/app/views/projects/pages/show.html.haml @@ -1,23 +1,27 @@ - page_title 'Pages' -%h3.page-title.with-button - = s_('GitLabPages|Pages') +- if @project.pages_enabled? + %h3.page-title.with-button + = s_('GitLabPages|Pages') - - if can?(current_user, :update_pages, @project) && (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https) - = link_to new_project_pages_domain_path(@project), class: 'btn btn-success float-right', title: s_('GitLabPages|New Domain') do - = s_('GitLabPages|New Domain') + - if can?(current_user, :update_pages, @project) && (Gitlab.config.pages.external_http || Gitlab.config.pages.external_https) + = link_to new_project_pages_domain_path(@project), class: 'btn btn-success float-right', title: s_('GitLabPages|New Domain') do + = s_('GitLabPages|New Domain') -%p.light - = s_('GitLabPages|With GitLab Pages you can host your static websites on GitLab. Combined with the power of GitLab CI and the help of GitLab Runner you can deploy static pages for your individual projects, your user or your group.') -- if Gitlab.config.pages.external_https - = render 'https_only' + %p.light + = s_('GitLabPages|With GitLab Pages you can host your static websites on GitLab. Combined with the power of GitLab CI and the help of GitLab Runner you can deploy static pages for your individual projects, your user or your group.') + - if Gitlab.config.pages.external_https + = render 'https_only' -%hr.clearfix + %hr.clearfix -= render 'access' -= render 'use' -- if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https - = render 'list' + = render 'access' + = render 'use' + - if Gitlab.config.pages.external_http || Gitlab.config.pages.external_https + = render 'list' + - else + = render 'no_domains' + = render 'destroy' - else - = render 'no_domains' -= render 'destroy' + .bs-callout.bs-callout-warning + = s_('GitLabPages|GitLab Pages are disabled for this project. You can enable them on your project\'s %{strong_start}Settings > General > Visibility%{strong_end} page.').html_safe % { strong_start: '<strong>'.html_safe, strong_end: '</strong>'.html_safe } diff --git a/changelogs/unreleased/31309-add-ability-for-users-to-organize-projects-on-the-operations-dashbo.yml b/changelogs/unreleased/31309-add-ability-for-users-to-organize-projects-on-the-operations-dashbo.yml new file mode 100644 index 00000000000..cd70b5f7864 --- /dev/null +++ b/changelogs/unreleased/31309-add-ability-for-users-to-organize-projects-on-the-operations-dashbo.yml @@ -0,0 +1,5 @@ +--- +title: Add ability to reorder projects on operations dashboard +merge_request: 18855 +author: +type: added diff --git a/changelogs/unreleased/32951-secure-modal-mobile-issue.yml b/changelogs/unreleased/32951-secure-modal-mobile-issue.yml new file mode 100644 index 00000000000..d3c4399cb35 --- /dev/null +++ b/changelogs/unreleased/32951-secure-modal-mobile-issue.yml @@ -0,0 +1,5 @@ +--- +title: Fixes mobile styling issues on security modals +merge_request: 19391 +author: +type: fixed diff --git a/changelogs/unreleased/33557-make-pages-settings-e-g-enablement-access-control-more-visible.yml b/changelogs/unreleased/33557-make-pages-settings-e-g-enablement-access-control-more-visible.yml new file mode 100644 index 00000000000..d04771f47a8 --- /dev/null +++ b/changelogs/unreleased/33557-make-pages-settings-e-g-enablement-access-control-more-visible.yml @@ -0,0 +1,5 @@ +--- +title: Add warnings about pages access control settings +merge_request: 19067 +author: +type: added diff --git a/changelogs/unreleased/35440-Hide-start-trial-buttons-for-expired-namespaces.yml b/changelogs/unreleased/35440-Hide-start-trial-buttons-for-expired-namespaces.yml new file mode 100644 index 00000000000..d2d60e8c498 --- /dev/null +++ b/changelogs/unreleased/35440-Hide-start-trial-buttons-for-expired-namespaces.yml @@ -0,0 +1,5 @@ +--- +title: Hide repeated trial offers on self-hosted instances +merge_request: 19511 +author: +type: changed diff --git a/changelogs/unreleased/35731-fix-snippets-with-emoji-import.yml b/changelogs/unreleased/35731-fix-snippets-with-emoji-import.yml new file mode 100644 index 00000000000..bf977958ca3 --- /dev/null +++ b/changelogs/unreleased/35731-fix-snippets-with-emoji-import.yml @@ -0,0 +1,5 @@ +--- +title: Fix import of snippets having `award_emoji` (Project Export/Import) +merge_request: 19690 +author: +type: fixed diff --git a/changelogs/unreleased/Remove-IIFEs-from-image_file-js.yml b/changelogs/unreleased/Remove-IIFEs-from-image_file-js.yml new file mode 100644 index 00000000000..4a5f8a892a7 --- /dev/null +++ b/changelogs/unreleased/Remove-IIFEs-from-image_file-js.yml @@ -0,0 +1,5 @@ +--- +title: Removed IIFEs from image_file.js +merge_request: 19548 +author: nuwe1 +type: other diff --git a/changelogs/unreleased/add-rails-parser-for-new-cs-report-format.yml b/changelogs/unreleased/add-rails-parser-for-new-cs-report-format.yml new file mode 100644 index 00000000000..ae76ad3a4e6 --- /dev/null +++ b/changelogs/unreleased/add-rails-parser-for-new-cs-report-format.yml @@ -0,0 +1,5 @@ +--- +title: Handle new Container Scanning report format +merge_request: 19123 +author: +type: changed diff --git a/changelogs/unreleased/dz-abuse-reports-filter.yml b/changelogs/unreleased/dz-abuse-reports-filter.yml new file mode 100644 index 00000000000..99211d84b58 --- /dev/null +++ b/changelogs/unreleased/dz-abuse-reports-filter.yml @@ -0,0 +1,5 @@ +--- +title: Add user filtering to abuse reports page +merge_request: 19365 +author: +type: changed diff --git a/db/migrate/20191104205020_add_license_details_to_application_settings.rb b/db/migrate/20191104205020_add_license_details_to_application_settings.rb new file mode 100644 index 00000000000..f951ae6492d --- /dev/null +++ b/db/migrate/20191104205020_add_license_details_to_application_settings.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +class AddLicenseDetailsToApplicationSettings < ActiveRecord::Migration[5.2] + include Gitlab::Database::MigrationHelpers + + DOWNTIME = false + + def change + add_column :application_settings, :license_trial_ends_on, :date, null: true + end +end diff --git a/db/schema.rb b/db/schema.rb index a43e1c70ad5..c11c1059fd2 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -345,6 +345,7 @@ ActiveRecord::Schema.define(version: 2019_11_05_140942) do t.boolean "pendo_enabled", default: false, null: false t.string "pendo_url", limit: 255 t.integer "deletion_adjourned_period", default: 7, null: false + t.date "license_trial_ends_on" t.boolean "eks_integration_enabled", default: false, null: false t.string "eks_account_id", limit: 128 t.string "eks_access_key_id", limit: 128 diff --git a/doc/administration/geo/replication/troubleshooting.md b/doc/administration/geo/replication/troubleshooting.md index 0c74e2d6ac7..5c40a4441b6 100644 --- a/doc/administration/geo/replication/troubleshooting.md +++ b/doc/administration/geo/replication/troubleshooting.md @@ -142,6 +142,106 @@ and expect something like: By running the command above, `primary` should be `true` when executed in the **primary** node, and `false` on any **secondary** node. +## Fixing errors found when running the Geo check rake task + +When running this rake task, you may see errors if the nodes are not properly configured: + +```sh +sudo gitlab-rake gitlab:geo:check +``` + +1. Rails did not provide a password when connecting to the database + + ```text + Checking Geo ... + + GitLab Geo is available ... Exception: fe_sendauth: no password supplied + GitLab Geo is enabled ... Exception: fe_sendauth: no password supplied + ... + Checking Geo ... Finished + ``` + + - Ensure that you have the `gitlab_rails['db_password']` set to the plain text-password used when creating the hash for `postgresql['sql_user_password']`. + +1. Rails is unable to connect to the database + + ```text + Checking Geo ... + + GitLab Geo is available ... Exception: FATAL: no pg_hba.conf entry for host "1.1.1.1", user "gitlab", database "gitlabhq_production", SSL on + FATAL: no pg_hba.conf entry for host "1.1.1.1", user "gitlab", database "gitlabhq_production", SSL off + GitLab Geo is enabled ... Exception: FATAL: no pg_hba.conf entry for host "1.1.1.1", user "gitlab", database "gitlabhq_production", SSL on + FATAL: no pg_hba.conf entry for host "1.1.1.1", user "gitlab", database "gitlabhq_production", SSL off + ... + Checking Geo ... Finished + ``` + + - Ensure that you have the IP address of the rails node included in `postgresql['md5_auth_cidr_addresses']`. + - Ensure that you have included the subnet mask on the IP address: `postgresql['md5_auth_cidr_addresses'] = ['1.1.1.1/32']`. + +1. Rails has supplied the incorrect password + + ```text + Checking Geo ... + GitLab Geo is available ... Exception: FATAL: password authentication failed for user "gitlab" + FATAL: password authentication failed for user "gitlab" + GitLab Geo is enabled ... Exception: FATAL: password authentication failed for user "gitlab" + FATAL: password authentication failed for user "gitlab" + ... + Checking Geo ... Finished + ``` + + - Verify the correct password is set for `gitlab_rails['db_password']` that was used when creating the hash in `postgresql['sql_user_password']` by running `gitlab-ctl pg-password-md5 gitlab` and entering the password. + +1. Check returns not a secondary node + + ```text + Checking Geo ... + + GitLab Geo is available ... yes + GitLab Geo is enabled ... yes + GitLab Geo secondary database is correctly configured ... not a secondary node + Database replication enabled? ... not a secondary node + ... + Checking Geo ... Finished + ``` + + - Ensure that you have added the secondary node in the admin area of the primary node. + - Ensure that you entered the `external_url` or `gitlab_rails['geo_node_name']` when adding the secondary node in the admin are of the primary node. + - Prior to GitLab 12.4, edit the secondary node in the admin area of the primary node and ensure that there is a trailing `/` in the `Name` field. + +1. Check returns Exception: PG::UndefinedTable: ERROR: relation "geo_nodes" does not exist + + ```text + Checking Geo ... + + GitLab Geo is available ... no + Try fixing it: + Upload a new license that includes the GitLab Geo feature + For more information see: + https://about.gitlab.com/features/gitlab-geo/ + GitLab Geo is enabled ... Exception: PG::UndefinedTable: ERROR: relation "geo_nodes" does not exist + LINE 8: WHERE a.attrelid = '"geo_nodes"'::regclass + ^ + : SELECT a.attname, format_type(a.atttypid, a.atttypmod), + pg_get_expr(d.adbin, d.adrelid), a.attnotnull, a.atttypid, a.atttypmod, + c.collname, col_description(a.attrelid, a.attnum) AS comment + FROM pg_attribute a + LEFT JOIN pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum + LEFT JOIN pg_type t ON a.atttypid = t.oid + LEFT JOIN pg_collation c ON a.attcollation = c.oid AND a.attcollation <> t.typcollation + WHERE a.attrelid = '"geo_nodes"'::regclass + AND a.attnum > 0 AND NOT a.attisdropped + ORDER BY a.attnum + ... + Checking Geo ... Finished + ``` + + When performing a Postgres major version (9 > 10) update this is expected. Follow: + + - [initiate-the-replication-process](https://docs.gitlab.com/ee/administration/geo/replication/database.html#step-3-initiate-the-replication-process) + - [Geo database has an outdated FDW remote schema](https://docs.gitlab.com/ee/administration/geo/replication/troubleshooting.html#geo-database-has-an-outdated-fdw-remote-schema-error) + ## Fixing replication errors The following sections outline troubleshooting steps for fixing replication diff --git a/doc/api/issues.md b/doc/api/issues.md index ded412a7af0..54b27370741 100644 --- a/doc/api/issues.md +++ b/doc/api/issues.md @@ -577,14 +577,22 @@ the `weight` parameter: ``` Users on GitLab [Ultimate](https://about.gitlab.com/pricing/) will additionally see -the `epic_iid` property: +the `epic` property: -```json +```javascript { "project_id" : 4, "description" : "Omnis vero earum sunt corporis dolor et placeat.", - "epic_iid" : 42, - ... + "epic": { + "epic_iid" : 5, //deprecated, use `iid` of the `epic` attribute + "epic": { + "id" : 42, + "iid" : 5, + "title": "My epic epic", + "url" : "/groups/h5bp/-/epics/5", + "group_id": 8 + }, + // ... } ``` @@ -592,6 +600,9 @@ the `epic_iid` property: **Note**: The `closed_by` attribute was [introduced in GitLab 10.6][ce-17042]. This value will only be present for issues which were closed after GitLab 10.6 and when the user account that closed the issue still exists. +**Note**: The `epic_iid` attribute is deprecated and [will be removed in 13.0](https://gitlab.com/gitlab-org/gitlab/issues/35157). +Please use `iid` of the `epic` attribute instead. + ## New issue Creates a new project issue. diff --git a/doc/development/contributing/merge_request_workflow.md b/doc/development/contributing/merge_request_workflow.md index 39557cf4174..510e90524ed 100644 --- a/doc/development/contributing/merge_request_workflow.md +++ b/doc/development/contributing/merge_request_workflow.md @@ -69,7 +69,7 @@ request is as follows: the issue(s) once the merge request is merged. 1. If you're allowed to (Core team members, for example), set a relevant milestone and [labels](issue_workflow.md). -1. If the MR changes the UI, it should include *Before* and *After* screenshots. +1. If the MR changes the UI, you'll need approval from a Product Designer (UX), based on the appropriate [product category](https://about.gitlab.com/handbook/product/categories/). UI changes should use available components from the GitLab Design System, [Pajamas](https://design.gitlab.com/). The MR must include *Before* and *After* screenshots. 1. If the MR changes CSS classes, please include the list of affected pages, which can be found by running `grep css-class ./app -R`. 1. Be prepared to answer questions and incorporate feedback into your MR with new diff --git a/doc/user/operations_dashboard/index.md b/doc/user/operations_dashboard/index.md index 8faf6cda1a8..cdb80cca6f7 100644 --- a/doc/user/operations_dashboard/index.md +++ b/doc/user/operations_dashboard/index.md @@ -25,6 +25,10 @@ last commit, pipeline status, and when it was last deployed. ![Operations Dashboard with projects](img/index_operations_dashboard_with_projects.png) +## Arranging projects on a dashboard + +You can drag project cards to change their order. The card order is currently only saved to your browser, so will not change the dashboard for other people. + ## Making it the default dashboard when you sign in The Operations Dashboard can also be made the default GitLab dashboard shown when diff --git a/locale/gitlab.pot b/locale/gitlab.pot index 4601b7dffd0..06344a6880e 100644 --- a/locale/gitlab.pot +++ b/locale/gitlab.pot @@ -7407,6 +7407,9 @@ msgstr "" msgid "Filter by two-factor authentication" msgstr "" +msgid "Filter by user" +msgstr "" + msgid "Filter projects" msgstr "" @@ -8160,6 +8163,9 @@ msgstr "" msgid "GitLabPages|%{domain} is not verified. To learn how to verify ownership, visit your %{link_start}domain details%{link_end}." msgstr "" +msgid "GitLabPages|Access Control is enabled for this Pages website; only authorized users will be able to access it. To make your website publicly available, navigate to your project's %{strong_start}Settings > General > Visibility%{strong_end} and select %{strong_start}Everyone%{strong_end} in pages section. Read the %{link_start}documentation%{link_end} for more information." +msgstr "" + msgid "GitLabPages|Access pages" msgstr "" @@ -8184,6 +8190,9 @@ msgstr "" msgid "GitLabPages|Force HTTPS (requires valid certificates)" msgstr "" +msgid "GitLabPages|GitLab Pages are disabled for this project. You can enable them on your project's %{strong_start}Settings > General > Visibility%{strong_end} page." +msgstr "" + msgid "GitLabPages|It may take up to 30 minutes before the site is available after the first deployment." msgstr "" @@ -10100,6 +10109,27 @@ msgstr "" msgid "Licenses" msgstr "" +msgid "License|Buy license" +msgstr "" + +msgid "License|License" +msgstr "" + +msgid "License|You can restore access to the Gold features at any time by upgrading." +msgstr "" + +msgid "License|You can start a free trial of GitLab Ultimate without any obligation or payment details." +msgstr "" + +msgid "License|You do not have a license." +msgstr "" + +msgid "License|Your License" +msgstr "" + +msgid "License|Your free trial of GitLab Ultimate expired on %{trial_ends_on}." +msgstr "" + msgid "Limit display of time tracking units to hours." msgstr "" @@ -12997,6 +13027,9 @@ msgstr "" msgid "Project name" msgstr "" +msgid "Project order will not be saved as local storage is not available." +msgstr "" + msgid "Project overview" msgstr "" diff --git a/spec/features/admin/admin_abuse_reports_spec.rb b/spec/features/admin/admin_abuse_reports_spec.rb index 48fff9e57d3..93051a8a355 100644 --- a/spec/features/admin/admin_abuse_reports_spec.rb +++ b/spec/features/admin/admin_abuse_reports_spec.rb @@ -51,5 +51,29 @@ describe "Admin::AbuseReports", :js do end end end + + describe 'filtering by user' do + let!(:user2) { create(:user) } + let!(:abuse_report) { create(:abuse_report, user: user) } + let!(:abuse_report_2) { create(:abuse_report, user: user2) } + + it 'shows only single user report' do + visit admin_abuse_reports_path + + page.within '.filter-form' do + click_button 'User' + wait_for_requests + + page.within '.dropdown-menu-user' do + click_link user2.name + end + + wait_for_requests + end + + expect(page).to have_content(user2.name) + expect(page).not_to have_content(user.name) + end + end end end diff --git a/spec/features/projects/pages_spec.rb b/spec/features/projects/pages_spec.rb index d55e9d12801..17a2d30e784 100644 --- a/spec/features/projects/pages_spec.rb +++ b/spec/features/projects/pages_spec.rb @@ -30,12 +30,52 @@ shared_examples 'pages settings editing' do expect(page).to have_content('Access pages') end + context 'when pages are disabled in the project settings' do + it 'renders disabled warning' do + project.project_feature.update!(pages_access_level: ProjectFeature::DISABLED) + + visit project_pages_path(project) + + expect(page).to have_content('GitLab Pages are disabled for this project') + end + end + it 'renders first deployment warning' do visit project_pages_path(project) expect(page).to have_content('It may take up to 30 minutes before the site is available after the first deployment.') end + shared_examples 'does not render access control warning' do + it 'does not render access control warning' do + visit project_pages_path(project) + + expect(page).not_to have_content('Access Control is enabled for this Pages website') + end + end + + include_examples 'does not render access control warning' + + context 'when access control is enabled in gitlab settings' do + before do + stub_pages_setting(access_control: true) + end + + it 'renders access control warning' do + visit project_pages_path(project) + + expect(page).to have_content('Access Control is enabled for this Pages website') + end + + context 'when pages are public' do + before do + project.project_feature.update!(pages_access_level: ProjectFeature::PUBLIC) + end + + include_examples 'does not render access control warning' + end + end + context 'when support for external domains is disabled' do it 'renders message that support is disabled' do visit project_pages_path(project) diff --git a/spec/finders/abuse_reports_finder_spec.rb b/spec/finders/abuse_reports_finder_spec.rb new file mode 100644 index 00000000000..c84a645ca08 --- /dev/null +++ b/spec/finders/abuse_reports_finder_spec.rb @@ -0,0 +1,27 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe AbuseReportsFinder, '#execute' do + let(:params) { {} } + let!(:user1) { create(:user) } + let!(:user2) { create(:user) } + let!(:abuse_report_1) { create(:abuse_report, user: user1) } + let!(:abuse_report_2) { create(:abuse_report, user: user2) } + + subject { described_class.new(params).execute } + + context 'empty params' do + it 'returns all abuse reports' do + expect(subject).to match_array([abuse_report_1, abuse_report_2]) + end + end + + context 'params[:user_id] is present' do + let(:params) { { user_id: user2 } } + + it 'returns abuse reports for the specified user' do + expect(subject).to match_array([abuse_report_2]) + end + end +end diff --git a/spec/fixtures/lib/gitlab/import_export/complex/project.json b/spec/fixtures/lib/gitlab/import_export/complex/project.json index 86931d66472..31805a54f2f 100644 --- a/spec/fixtures/lib/gitlab/import_export/complex/project.json +++ b/spec/fixtures/lib/gitlab/import_export/complex/project.json @@ -2260,7 +2260,41 @@ ] } ], - "snippets": [], + "snippets": [ + { + "id": 1, + "title": "Test snippet title", + "content": "x = 1", + "author_id": 1, + "project_id": 1, + "created_at": "2019-11-05T15:06:06.579Z", + "updated_at": "2019-11-05T15:06:06.579Z", + "file_name": "", + "visibility_level": 20, + "description": "Test snippet description", + "award_emoji": [ + { + "id": 1, + "name": "thumbsup", + "user_id": 1, + "awardable_type": "Snippet", + "awardable_id": 1, + "created_at": "2019-11-05T15:37:21.287Z", + "updated_at": "2019-11-05T15:37:21.287Z" + }, + { + "id": 2, + "name": "coffee", + "user_id": 1, + "awardable_type": "Snippet", + "awardable_id": 1, + "created_at": "2019-11-05T15:37:24.645Z", + "updated_at": "2019-11-05T15:37:24.645Z" + } + ], + "notes": [] + } + ], "releases": [], "project_members": [ { diff --git a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb index b60c5cc59be..459b1eed1a7 100644 --- a/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb +++ b/spec/lib/gitlab/import_export/project_tree_restorer_spec.rb @@ -210,6 +210,16 @@ describe Gitlab::ImportExport::ProjectTreeRestorer do expect(@project.project_badges.count).to eq(2) end + it 'has snippets' do + expect(@project.snippets.count).to eq(1) + end + + it 'has award emoji for a snippet' do + award_emoji = @project.snippets.first.award_emoji + + expect(award_emoji.map(&:name)).to contain_exactly('thumbsup', 'coffee') + end + it 'restores the correct service' do expect(CustomIssueTrackerService.first).not_to be_nil end |