diff options
137 files changed, 605 insertions, 93 deletions
@@ -233,7 +233,7 @@ gem 'ruby-fogbugz', '~> 0.2.1' gem 'kubeclient', '~> 3.1.0' # Sanitize user input -gem 'sanitize', '~> 4.6.5' +gem 'sanitize', '~> 4.6' gem 'babosa', '~> 1.0.2' # Sanitizes SVG input diff --git a/Gemfile.lock b/Gemfile.lock index 0976169bb11..22626c0071b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -514,7 +514,7 @@ GEM net-ldap (0.16.0) net-ssh (5.0.1) netrc (0.11.0) - nokogiri (1.8.3) + nokogiri (1.8.4) mini_portile2 (~> 2.3.0) nokogumbo (1.5.0) nokogiri @@ -808,7 +808,7 @@ GEM et-orbi (~> 1.0) rugged (0.27.2) safe_yaml (1.0.4) - sanitize (4.6.5) + sanitize (4.6.6) crass (~> 1.0.2) nokogiri (>= 1.4.4) nokogumbo (~> 1.4) @@ -1157,7 +1157,7 @@ DEPENDENCIES ruby_parser (~> 3.8) rufus-scheduler (~> 3.4) rugged (~> 0.27) - sanitize (~> 4.6.5) + sanitize (~> 4.6) sass-rails (~> 5.0.6) scss_lint (~> 0.56.0) seed-fu (~> 2.3.7) diff --git a/Gemfile.rails5.lock b/Gemfile.rails5.lock index 1cf612fd4a6..5a0aaf05608 100644 --- a/Gemfile.rails5.lock +++ b/Gemfile.rails5.lock @@ -1168,7 +1168,7 @@ DEPENDENCIES ruby_parser (~> 3.8) rufus-scheduler (~> 3.4) rugged (~> 0.27) - sanitize (~> 4.6.5) + sanitize (~> 4.6) sass-rails (~> 5.0.6) scss_lint (~> 0.56.0) seed-fu (~> 2.3.7) diff --git a/app/assets/javascripts/lib/utils/common_utils.js b/app/assets/javascripts/lib/utils/common_utils.js index 6b7550efff8..2f3dd6f6cbc 100644 --- a/app/assets/javascripts/lib/utils/common_utils.js +++ b/app/assets/javascripts/lib/utils/common_utils.js @@ -541,6 +541,26 @@ export const addSelectOnFocusBehaviour = (selector = '.js-select-on-focus') => { }); }; +/** + * Method to round of values with decimal places + * with provided precision. + * + * Taken from https://stackoverflow.com/a/7343013/414749 + * + * Eg; roundOffFloat(3.141592, 3) = 3.142 + * + * Refer to spec/javascripts/lib/utils/common_utils_spec.js for + * more supported examples. + * + * @param {Float} number + * @param {Number} precision + */ +export const roundOffFloat = (number, precision = 0) => { + // eslint-disable-next-line no-restricted-properties + const multiplier = Math.pow(10, precision); + return Math.round(number * multiplier) / multiplier; +}; + window.gl = window.gl || {}; window.gl.utils = { ...(window.gl.utils || {}), diff --git a/app/assets/javascripts/notes/index.js b/app/assets/javascripts/notes/index.js index 6dd4c9d66ac..3aef30c608c 100644 --- a/app/assets/javascripts/notes/index.js +++ b/app/assets/javascripts/notes/index.js @@ -15,7 +15,7 @@ document.addEventListener('DOMContentLoaded', () => { const notesDataset = document.getElementById('js-vue-notes').dataset; const parsedUserData = JSON.parse(notesDataset.currentUserData); const noteableData = JSON.parse(notesDataset.noteableData); - const { markdownVersion } = notesDataset; + const markdownVersion = parseInt(notesDataset.markdownVersion, 10); let currentUserData = {}; noteableData.noteableType = notesDataset.noteableType; diff --git a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue index b1c2df54ef6..f44d361c47e 100644 --- a/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue +++ b/app/assets/javascripts/vue_shared/components/stacked_progress_bar.vue @@ -1,4 +1,5 @@ <script> +import { roundOffFloat } from '~/lib/utils/common_utils'; import tooltip from '~/vue_shared/directives/tooltip'; export default { @@ -70,7 +71,7 @@ export default { }, methods: { getPercent(count) { - return Math.ceil((count / this.totalCount) * 100); + return roundOffFloat((count / this.totalCount) * 100, 1); }, barStyle(percent) { return `width: ${percent}%;`; diff --git a/app/assets/stylesheets/framework/stacked_progress_bar.scss b/app/assets/stylesheets/framework/stacked_progress_bar.scss index 528ba53a48b..29a2d5881f7 100644 --- a/app/assets/stylesheets/framework/stacked_progress_bar.scss +++ b/app/assets/stylesheets/framework/stacked_progress_bar.scss @@ -10,7 +10,7 @@ .status-neutral, .status-red, { height: 100%; - min-width: 30px; + min-width: 40px; padding: 0 5px; font-size: $tooltip-font-size; font-weight: normal; diff --git a/app/controllers/profiles_controller.rb b/app/controllers/profiles_controller.rb index 074db361949..56a7b766b77 100644 --- a/app/controllers/profiles_controller.rb +++ b/app/controllers/profiles_controller.rb @@ -99,7 +99,8 @@ class ProfilesController < Profiles::ApplicationController :username, :website_url, :organization, - :preferred_language + :preferred_language, + :private_profile ) end end diff --git a/app/controllers/users_controller.rb b/app/controllers/users_controller.rb index 31f47a7aa7c..2f65f4a7403 100644 --- a/app/controllers/users_controller.rb +++ b/app/controllers/users_controller.rb @@ -13,6 +13,8 @@ class UsersController < ApplicationController skip_before_action :authenticate_user! before_action :user, except: [:exists] + before_action :authorize_read_user_profile!, + only: [:calendar, :calendar_activities, :groups, :projects, :contributed_projects, :snippets] def show respond_to do |format| @@ -148,4 +150,8 @@ class UsersController < ApplicationController def build_canonical_path(user) url_for(safe_params.merge(username: user.to_param)) end + + def authorize_read_user_profile! + access_denied! unless can?(current_user, :read_user_profile, user) + end end diff --git a/app/finders/personal_projects_finder.rb b/app/finders/personal_projects_finder.rb index 18adfea747f..a56a3a1e1a9 100644 --- a/app/finders/personal_projects_finder.rb +++ b/app/finders/personal_projects_finder.rb @@ -1,4 +1,6 @@ class PersonalProjectsFinder < UnionFinder + include Gitlab::Allowable + def initialize(user, params = {}) @user = user @params = params @@ -14,6 +16,8 @@ class PersonalProjectsFinder < UnionFinder # # Returns an ActiveRecord::Relation. def execute(current_user = nil) + return Project.none unless can?(current_user, :read_user_profile, @user) + segments = all_projects(current_user) find_union(segments, Project).includes(:namespace).order_updated_desc diff --git a/app/finders/user_recent_events_finder.rb b/app/finders/user_recent_events_finder.rb index 74776b2ed1f..876f086a3ef 100644 --- a/app/finders/user_recent_events_finder.rb +++ b/app/finders/user_recent_events_finder.rb @@ -7,6 +7,7 @@ class UserRecentEventsFinder prepend FinderWithCrossProjectAccess include FinderMethods + include Gitlab::Allowable requires_cross_project_access @@ -21,6 +22,8 @@ class UserRecentEventsFinder end def execute + return Event.none unless can?(current_user, :read_user_profile, target_user) + recent_events(params[:offset] || 0) .joins(:project) .with_associations diff --git a/app/helpers/users_helper.rb b/app/helpers/users_helper.rb index 4d17b22a4a1..8ee4203b6f5 100644 --- a/app/helpers/users_helper.rb +++ b/app/helpers/users_helper.rb @@ -42,7 +42,13 @@ module UsersHelper private def get_profile_tabs - [:activity, :groups, :contributed, :projects, :snippets] + tabs = [] + + if can?(current_user, :read_user_profile, @user) + tabs += [:activity, :groups, :contributed, :projects, :snippets] + end + + tabs end def get_current_user_menu_items diff --git a/app/policies/user_policy.rb b/app/policies/user_policy.rb index ee219f0a0d0..8499e45e846 100644 --- a/app/policies/user_policy.rb +++ b/app/policies/user_policy.rb @@ -5,6 +5,9 @@ class UserPolicy < BasePolicy desc "This is the ghost user" condition(:subject_ghost, scope: :subject, score: 0) { @subject.ghost? } + desc "The profile is private" + condition(:private_profile, scope: :subject, score: 0) { @subject.private_profile? } + rule { ~restricted_public_level }.enable :read_user rule { ~anonymous }.enable :read_user @@ -12,4 +15,7 @@ class UserPolicy < BasePolicy enable :destroy_user enable :update_user end + + rule { default }.enable :read_user_profile + rule { private_profile & ~(user_is_self | admin) }.prevent :read_user_profile end diff --git a/app/serializers/analytics_build_entity.rb b/app/serializers/analytics_build_entity.rb index bdc22d71202..99663c8d5eb 100644 --- a/app/serializers/analytics_build_entity.rb +++ b/app/serializers/analytics_build_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsBuildEntity < Grape::Entity include RequestAwareEntity include EntityDateHelper diff --git a/app/serializers/analytics_build_serializer.rb b/app/serializers/analytics_build_serializer.rb index f172d67d356..9c9f76a1c28 100644 --- a/app/serializers/analytics_build_serializer.rb +++ b/app/serializers/analytics_build_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsBuildSerializer < BaseSerializer entity AnalyticsBuildEntity end diff --git a/app/serializers/analytics_commit_entity.rb b/app/serializers/analytics_commit_entity.rb index 402cecbfd08..b25c603c9f0 100644 --- a/app/serializers/analytics_commit_entity.rb +++ b/app/serializers/analytics_commit_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsCommitEntity < CommitEntity include EntityDateHelper diff --git a/app/serializers/analytics_commit_serializer.rb b/app/serializers/analytics_commit_serializer.rb index cdbfecf2b70..0f65687a3c0 100644 --- a/app/serializers/analytics_commit_serializer.rb +++ b/app/serializers/analytics_commit_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsCommitSerializer < BaseSerializer entity AnalyticsCommitEntity end diff --git a/app/serializers/analytics_generic_serializer.rb b/app/serializers/analytics_generic_serializer.rb index 9f4859e8410..10391c13034 100644 --- a/app/serializers/analytics_generic_serializer.rb +++ b/app/serializers/analytics_generic_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsGenericSerializer < BaseSerializer def represent(resource, opts = {}) resource.symbolize_keys! diff --git a/app/serializers/analytics_issue_entity.rb b/app/serializers/analytics_issue_entity.rb index b7d95ea020f..ab15bd0ac7a 100644 --- a/app/serializers/analytics_issue_entity.rb +++ b/app/serializers/analytics_issue_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsIssueEntity < Grape::Entity include RequestAwareEntity include EntityDateHelper diff --git a/app/serializers/analytics_issue_serializer.rb b/app/serializers/analytics_issue_serializer.rb index 4fb3e8f1bb4..4a1777276a4 100644 --- a/app/serializers/analytics_issue_serializer.rb +++ b/app/serializers/analytics_issue_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsIssueSerializer < AnalyticsGenericSerializer entity AnalyticsIssueEntity end diff --git a/app/serializers/analytics_merge_request_entity.rb b/app/serializers/analytics_merge_request_entity.rb index 888265eaa38..b7134da9461 100644 --- a/app/serializers/analytics_merge_request_entity.rb +++ b/app/serializers/analytics_merge_request_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsMergeRequestEntity < AnalyticsIssueEntity expose :state diff --git a/app/serializers/analytics_merge_request_serializer.rb b/app/serializers/analytics_merge_request_serializer.rb index 4622a1dd855..f0b9115d02c 100644 --- a/app/serializers/analytics_merge_request_serializer.rb +++ b/app/serializers/analytics_merge_request_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsMergeRequestSerializer < AnalyticsGenericSerializer entity AnalyticsMergeRequestEntity end diff --git a/app/serializers/analytics_stage_entity.rb b/app/serializers/analytics_stage_entity.rb index 3e355a13e06..ae7c20c3bba 100644 --- a/app/serializers/analytics_stage_entity.rb +++ b/app/serializers/analytics_stage_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsStageEntity < Grape::Entity include EntityDateHelper diff --git a/app/serializers/analytics_stage_serializer.rb b/app/serializers/analytics_stage_serializer.rb index 613cf6874d8..86786273240 100644 --- a/app/serializers/analytics_stage_serializer.rb +++ b/app/serializers/analytics_stage_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsStageSerializer < BaseSerializer entity AnalyticsStageEntity end diff --git a/app/serializers/analytics_summary_entity.rb b/app/serializers/analytics_summary_entity.rb index 9c37afd53e1..39c6b4b06b2 100644 --- a/app/serializers/analytics_summary_entity.rb +++ b/app/serializers/analytics_summary_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsSummaryEntity < Grape::Entity expose :value, safe: true expose :title diff --git a/app/serializers/analytics_summary_serializer.rb b/app/serializers/analytics_summary_serializer.rb index c87a24aa47c..b22bd737f03 100644 --- a/app/serializers/analytics_summary_serializer.rb +++ b/app/serializers/analytics_summary_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AnalyticsSummarySerializer < BaseSerializer entity AnalyticsSummaryEntity end diff --git a/app/serializers/award_emoji_entity.rb b/app/serializers/award_emoji_entity.rb index 6e03cd02392..212931a2fa9 100644 --- a/app/serializers/award_emoji_entity.rb +++ b/app/serializers/award_emoji_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class AwardEmojiEntity < Grape::Entity expose :name expose :user, using: API::Entities::UserSafe diff --git a/app/serializers/base_serializer.rb b/app/serializers/base_serializer.rb index 8cade280b0c..7b65bd22f54 100644 --- a/app/serializers/base_serializer.rb +++ b/app/serializers/base_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BaseSerializer attr_reader :params diff --git a/app/serializers/blob_entity.rb b/app/serializers/blob_entity.rb index b501fd5e964..3ac61481dea 100644 --- a/app/serializers/blob_entity.rb +++ b/app/serializers/blob_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BlobEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/build_action_entity.rb b/app/serializers/build_action_entity.rb index f2d76a8ad81..f9da3f63911 100644 --- a/app/serializers/build_action_entity.rb +++ b/app/serializers/build_action_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BuildActionEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/build_artifact_entity.rb b/app/serializers/build_artifact_entity.rb index 6e0e33bc09b..414f436e76e 100644 --- a/app/serializers/build_artifact_entity.rb +++ b/app/serializers/build_artifact_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BuildArtifactEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/build_details_entity.rb b/app/serializers/build_details_entity.rb index 2de9624aed4..b887b99d31c 100644 --- a/app/serializers/build_details_entity.rb +++ b/app/serializers/build_details_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BuildDetailsEntity < JobEntity expose :coverage, :erased_at, :duration expose :tag_list, as: :tags diff --git a/app/serializers/build_metadata_entity.rb b/app/serializers/build_metadata_entity.rb index f16f3badffa..6242ee8957d 100644 --- a/app/serializers/build_metadata_entity.rb +++ b/app/serializers/build_metadata_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BuildMetadataEntity < Grape::Entity expose :timeout_human_readable expose :timeout_source do |metadata| diff --git a/app/serializers/build_serializer.rb b/app/serializers/build_serializer.rb index bae9932847f..0649fdad6a8 100644 --- a/app/serializers/build_serializer.rb +++ b/app/serializers/build_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class BuildSerializer < BaseSerializer entity JobEntity diff --git a/app/serializers/cluster_application_entity.rb b/app/serializers/cluster_application_entity.rb index 77fc3336521..2bd17e58086 100644 --- a/app/serializers/cluster_application_entity.rb +++ b/app/serializers/cluster_application_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ClusterApplicationEntity < Grape::Entity expose :name expose :status_name, as: :status diff --git a/app/serializers/cluster_entity.rb b/app/serializers/cluster_entity.rb index 7e5b0997878..c59f68bbc49 100644 --- a/app/serializers/cluster_entity.rb +++ b/app/serializers/cluster_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ClusterEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/cluster_serializer.rb b/app/serializers/cluster_serializer.rb index 2e13c1501e7..4bb4d4880d4 100644 --- a/app/serializers/cluster_serializer.rb +++ b/app/serializers/cluster_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ClusterSerializer < BaseSerializer entity ClusterEntity diff --git a/app/serializers/cohort_activity_month_entity.rb b/app/serializers/cohort_activity_month_entity.rb index e6788a8b596..cdbc89a2dcd 100644 --- a/app/serializers/cohort_activity_month_entity.rb +++ b/app/serializers/cohort_activity_month_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CohortActivityMonthEntity < Grape::Entity include ActionView::Helpers::NumberHelper diff --git a/app/serializers/cohort_entity.rb b/app/serializers/cohort_entity.rb index 7cdba5b0484..3d0213e1038 100644 --- a/app/serializers/cohort_entity.rb +++ b/app/serializers/cohort_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CohortEntity < Grape::Entity include ActionView::Helpers::NumberHelper diff --git a/app/serializers/cohorts_entity.rb b/app/serializers/cohorts_entity.rb index 98f5995ba6f..a84d568bf30 100644 --- a/app/serializers/cohorts_entity.rb +++ b/app/serializers/cohorts_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CohortsEntity < Grape::Entity expose :months_included expose :cohorts, using: CohortEntity diff --git a/app/serializers/cohorts_serializer.rb b/app/serializers/cohorts_serializer.rb index fe9367b13d8..ceca5e1e5a8 100644 --- a/app/serializers/cohorts_serializer.rb +++ b/app/serializers/cohorts_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CohortsSerializer < AnalyticsGenericSerializer entity CohortsEntity end diff --git a/app/serializers/commit_entity.rb b/app/serializers/commit_entity.rb index c8dd98cc04d..b3287c66554 100644 --- a/app/serializers/commit_entity.rb +++ b/app/serializers/commit_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class CommitEntity < API::Entities::Commit include RequestAwareEntity diff --git a/app/serializers/concerns/with_pagination.rb b/app/serializers/concerns/with_pagination.rb index 89631b73fcf..c8ffae355e8 100644 --- a/app/serializers/concerns/with_pagination.rb +++ b/app/serializers/concerns/with_pagination.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module WithPagination attr_accessor :paginator diff --git a/app/serializers/container_repositories_serializer.rb b/app/serializers/container_repositories_serializer.rb index 56dc70b5687..e1ce3c7b3ae 100644 --- a/app/serializers/container_repositories_serializer.rb +++ b/app/serializers/container_repositories_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ContainerRepositoriesSerializer < BaseSerializer entity ContainerRepositoryEntity end diff --git a/app/serializers/container_repository_entity.rb b/app/serializers/container_repository_entity.rb index 1103cf30a07..59bf35f5aff 100644 --- a/app/serializers/container_repository_entity.rb +++ b/app/serializers/container_repository_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ContainerRepositoryEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/container_tag_entity.rb b/app/serializers/container_tag_entity.rb index 8f1488e6cbb..637294877f8 100644 --- a/app/serializers/container_tag_entity.rb +++ b/app/serializers/container_tag_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ContainerTagEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/container_tags_serializer.rb b/app/serializers/container_tags_serializer.rb index 6ff3adff135..982ce33f6e3 100644 --- a/app/serializers/container_tags_serializer.rb +++ b/app/serializers/container_tags_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ContainerTagsSerializer < BaseSerializer entity ContainerTagEntity diff --git a/app/serializers/deploy_key_entity.rb b/app/serializers/deploy_key_entity.rb index 2678f99510c..54bf030aba1 100644 --- a/app/serializers/deploy_key_entity.rb +++ b/app/serializers/deploy_key_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeployKeyEntity < Grape::Entity expose :id expose :user_id diff --git a/app/serializers/deploy_key_serializer.rb b/app/serializers/deploy_key_serializer.rb index 8f849eb88b7..a1cd98b631b 100644 --- a/app/serializers/deploy_key_serializer.rb +++ b/app/serializers/deploy_key_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeployKeySerializer < BaseSerializer entity DeployKeyEntity end diff --git a/app/serializers/deploy_keys_project_entity.rb b/app/serializers/deploy_keys_project_entity.rb index 568ef5ab75e..5775ad72d0d 100644 --- a/app/serializers/deploy_keys_project_entity.rb +++ b/app/serializers/deploy_keys_project_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeployKeysProjectEntity < Grape::Entity expose :can_push expose :project, using: ProjectEntity diff --git a/app/serializers/deployment_entity.rb b/app/serializers/deployment_entity.rb index 241c689bccd..344148a1fb7 100644 --- a/app/serializers/deployment_entity.rb +++ b/app/serializers/deployment_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeploymentEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/deployment_serializer.rb b/app/serializers/deployment_serializer.rb index cba5c3f311f..04db6b88489 100644 --- a/app/serializers/deployment_serializer.rb +++ b/app/serializers/deployment_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DeploymentSerializer < BaseSerializer entity DeploymentEntity diff --git a/app/serializers/diff_file_entity.rb b/app/serializers/diff_file_entity.rb index 61135fba97b..79844c9210a 100644 --- a/app/serializers/diff_file_entity.rb +++ b/app/serializers/diff_file_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DiffFileEntity < Grape::Entity include RequestAwareEntity include BlobHelper diff --git a/app/serializers/diffs_entity.rb b/app/serializers/diffs_entity.rb index bb804e5347a..f75ace14d9c 100644 --- a/app/serializers/diffs_entity.rb +++ b/app/serializers/diffs_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DiffsEntity < Grape::Entity include DiffHelper include RequestAwareEntity diff --git a/app/serializers/diffs_serializer.rb b/app/serializers/diffs_serializer.rb index 6771e10c5ac..9e5eb1699d4 100644 --- a/app/serializers/diffs_serializer.rb +++ b/app/serializers/diffs_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DiffsSerializer < BaseSerializer entity DiffsEntity end diff --git a/app/serializers/discussion_entity.rb b/app/serializers/discussion_entity.rb index 7505bbdeb3d..6f95e6f9ca1 100644 --- a/app/serializers/discussion_entity.rb +++ b/app/serializers/discussion_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DiscussionEntity < Grape::Entity include RequestAwareEntity include NotesHelper diff --git a/app/serializers/discussion_serializer.rb b/app/serializers/discussion_serializer.rb index ed5e1224bb2..5be40e74175 100644 --- a/app/serializers/discussion_serializer.rb +++ b/app/serializers/discussion_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class DiscussionSerializer < BaseSerializer entity DiscussionEntity end diff --git a/app/serializers/entity_date_helper.rb b/app/serializers/entity_date_helper.rb index 464217123b4..cc0c2abf863 100644 --- a/app/serializers/entity_date_helper.rb +++ b/app/serializers/entity_date_helper.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module EntityDateHelper include ActionView::Helpers::DateHelper include ActionView::Helpers::TagHelper @@ -50,15 +52,20 @@ module EntityDateHelper elsif entity.due_date is_upcoming = (entity.due_date - Date.today).to_i > 0 time_ago = time_ago_in_words(entity.due_date) - content = time_ago.gsub(/\d+/) { |match| "<strong>#{match}</strong>" } - content.slice!("about ") - content << " " + (is_upcoming ? _("remaining") : _("ago")) - content.html_safe + + # https://gitlab.com/gitlab-org/gitlab-ce/issues/49440 + # + # Need to improve the i18n here and do a full translation + # of the string instead of piecewise translations. + content = time_ago + .gsub(/\d+/) { |match| "<strong>#{match}</strong>" } + .remove("about ") + remaining_or_ago = is_upcoming ? _("remaining") : _("ago") + + "#{content} #{remaining_or_ago}".html_safe elsif entity.start_date && entity.start_date.past? - days = entity.elapsed_days - content = content_tag(:strong, days) - content << " #{'day'.pluralize(days)} elapsed" - content.html_safe + days = entity.elapsed_days + "#{content_tag(:strong, days)} #{'day'.pluralize(days)} elapsed".html_safe end end end diff --git a/app/serializers/entity_request.rb b/app/serializers/entity_request.rb index 456ba1174c0..49e026e8c2a 100644 --- a/app/serializers/entity_request.rb +++ b/app/serializers/entity_request.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class EntityRequest # We use EntityRequest object to collect parameters and variables # from the controller. Because options that are being passed to the entity diff --git a/app/serializers/environment_entity.rb b/app/serializers/environment_entity.rb index 83558fc6659..b18e9706db6 100644 --- a/app/serializers/environment_entity.rb +++ b/app/serializers/environment_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class EnvironmentEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/environment_serializer.rb b/app/serializers/environment_serializer.rb index 84722f33f59..dc1686c30c4 100644 --- a/app/serializers/environment_serializer.rb +++ b/app/serializers/environment_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class EnvironmentSerializer < BaseSerializer include WithPagination diff --git a/app/serializers/group_child_entity.rb b/app/serializers/group_child_entity.rb index ee150eefd9e..f6804fe7f6a 100644 --- a/app/serializers/group_child_entity.rb +++ b/app/serializers/group_child_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupChildEntity < Grape::Entity include ActionView::Helpers::NumberHelper include RequestAwareEntity diff --git a/app/serializers/group_child_serializer.rb b/app/serializers/group_child_serializer.rb index 2baef0a5703..789707c2c9b 100644 --- a/app/serializers/group_child_serializer.rb +++ b/app/serializers/group_child_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupChildSerializer < BaseSerializer include WithPagination diff --git a/app/serializers/group_entity.rb b/app/serializers/group_entity.rb index 6d8466da902..c46c342ee5d 100644 --- a/app/serializers/group_entity.rb +++ b/app/serializers/group_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupEntity < Grape::Entity include ActionView::Helpers::NumberHelper include RequestAwareEntity diff --git a/app/serializers/group_serializer.rb b/app/serializers/group_serializer.rb index 8cf7eb63bcf..38c5757a6c1 100644 --- a/app/serializers/group_serializer.rb +++ b/app/serializers/group_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupSerializer < BaseSerializer include WithPagination diff --git a/app/serializers/group_variable_entity.rb b/app/serializers/group_variable_entity.rb index 62cf0b21e1e..0edab4a3092 100644 --- a/app/serializers/group_variable_entity.rb +++ b/app/serializers/group_variable_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupVariableEntity < Grape::Entity expose :id expose :key diff --git a/app/serializers/group_variable_serializer.rb b/app/serializers/group_variable_serializer.rb index 8f8205924aa..ed20b240cce 100644 --- a/app/serializers/group_variable_serializer.rb +++ b/app/serializers/group_variable_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class GroupVariableSerializer < BaseSerializer entity GroupVariableEntity end diff --git a/app/serializers/issuable_entity.rb b/app/serializers/issuable_entity.rb index 6f31fbd6b7c..e71bd3313fb 100644 --- a/app/serializers/issuable_entity.rb +++ b/app/serializers/issuable_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssuableEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/issuable_sidebar_entity.rb b/app/serializers/issuable_sidebar_entity.rb index 29138c803df..773d78d324c 100644 --- a/app/serializers/issuable_sidebar_entity.rb +++ b/app/serializers/issuable_sidebar_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssuableSidebarEntity < Grape::Entity include TimeTrackableEntity include RequestAwareEntity diff --git a/app/serializers/issue_entity.rb b/app/serializers/issue_entity.rb index 840fdbcbf14..16a477c92fa 100644 --- a/app/serializers/issue_entity.rb +++ b/app/serializers/issue_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssueEntity < IssuableEntity include TimeTrackableEntity diff --git a/app/serializers/issue_serializer.rb b/app/serializers/issue_serializer.rb index 2555595379b..37cf5e28396 100644 --- a/app/serializers/issue_serializer.rb +++ b/app/serializers/issue_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssueSerializer < BaseSerializer # This overrided method takes care of which entity should be used # to serialize the `issue` based on `basic` key in `opts` param. diff --git a/app/serializers/issue_sidebar_entity.rb b/app/serializers/issue_sidebar_entity.rb index 6c823dbfe95..349ad9d1fef 100644 --- a/app/serializers/issue_sidebar_entity.rb +++ b/app/serializers/issue_sidebar_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class IssueSidebarEntity < IssuableSidebarEntity expose :assignees, using: API::Entities::UserBasic end diff --git a/app/serializers/job_entity.rb b/app/serializers/job_entity.rb index 960e7291ae6..7bc1d87dea5 100644 --- a/app/serializers/job_entity.rb +++ b/app/serializers/job_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class JobEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/job_group_entity.rb b/app/serializers/job_group_entity.rb index 8554de55517..0941a9d36be 100644 --- a/app/serializers/job_group_entity.rb +++ b/app/serializers/job_group_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class JobGroupEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/label_entity.rb b/app/serializers/label_entity.rb index 4452161051e..98743d62b50 100644 --- a/app/serializers/label_entity.rb +++ b/app/serializers/label_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LabelEntity < Grape::Entity expose :id, if: ->(label, _) { !label.is_a?(GlobalLabel) } diff --git a/app/serializers/label_serializer.rb b/app/serializers/label_serializer.rb index ad6ba8c46c9..25b9f7de243 100644 --- a/app/serializers/label_serializer.rb +++ b/app/serializers/label_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LabelSerializer < BaseSerializer entity LabelEntity diff --git a/app/serializers/lfs_file_lock_entity.rb b/app/serializers/lfs_file_lock_entity.rb index 264a77adc3f..7961c4e666b 100644 --- a/app/serializers/lfs_file_lock_entity.rb +++ b/app/serializers/lfs_file_lock_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LfsFileLockEntity < Grape::Entity root 'locks', 'lock' diff --git a/app/serializers/lfs_file_lock_serializer.rb b/app/serializers/lfs_file_lock_serializer.rb index ba8fb1a461d..0367097e376 100644 --- a/app/serializers/lfs_file_lock_serializer.rb +++ b/app/serializers/lfs_file_lock_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class LfsFileLockSerializer < BaseSerializer entity LfsFileLockEntity end diff --git a/app/serializers/merge_request_basic_entity.rb b/app/serializers/merge_request_basic_entity.rb index 1c06691026d..f7eb74cf392 100644 --- a/app/serializers/merge_request_basic_entity.rb +++ b/app/serializers/merge_request_basic_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestBasicEntity < IssuableSidebarEntity expose :assignee_id expose :merge_status diff --git a/app/serializers/merge_request_basic_serializer.rb b/app/serializers/merge_request_basic_serializer.rb index cc5c664c8fa..a68b48b00db 100644 --- a/app/serializers/merge_request_basic_serializer.rb +++ b/app/serializers/merge_request_basic_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestBasicSerializer < BaseSerializer entity MergeRequestBasicEntity end diff --git a/app/serializers/merge_request_create_entity.rb b/app/serializers/merge_request_create_entity.rb index 11234313293..e7a93004dda 100644 --- a/app/serializers/merge_request_create_entity.rb +++ b/app/serializers/merge_request_create_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestCreateEntity < Grape::Entity expose :iid diff --git a/app/serializers/merge_request_create_serializer.rb b/app/serializers/merge_request_create_serializer.rb index 08daf473319..b6df9ee13fc 100644 --- a/app/serializers/merge_request_create_serializer.rb +++ b/app/serializers/merge_request_create_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestCreateSerializer < BaseSerializer entity MergeRequestCreateEntity end diff --git a/app/serializers/merge_request_diff_entity.rb b/app/serializers/merge_request_diff_entity.rb index 32c761b45ac..433bfe60474 100644 --- a/app/serializers/merge_request_diff_entity.rb +++ b/app/serializers/merge_request_diff_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestDiffEntity < Grape::Entity include Gitlab::Routing include GitHelper diff --git a/app/serializers/merge_request_metrics_entity.rb b/app/serializers/merge_request_metrics_entity.rb index 3548107ac16..1c9db08d103 100644 --- a/app/serializers/merge_request_metrics_entity.rb +++ b/app/serializers/merge_request_metrics_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestMetricsEntity < Grape::Entity expose :latest_closed_at, as: :closed_at expose :merged_at diff --git a/app/serializers/merge_request_serializer.rb b/app/serializers/merge_request_serializer.rb index caf193bdae3..1f8c830e1aa 100644 --- a/app/serializers/merge_request_serializer.rb +++ b/app/serializers/merge_request_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestSerializer < BaseSerializer # This overrided method takes care of which entity should be used # to serialize the `merge_request` based on `serializer` key in `opts` param. diff --git a/app/serializers/merge_request_user_entity.rb b/app/serializers/merge_request_user_entity.rb index 33fc7b724d5..fd2d2897113 100644 --- a/app/serializers/merge_request_user_entity.rb +++ b/app/serializers/merge_request_user_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestUserEntity < UserEntity include RequestAwareEntity include BlobHelper diff --git a/app/serializers/merge_request_widget_entity.rb b/app/serializers/merge_request_widget_entity.rb index a78bd77cf7c..4fe04e4b206 100644 --- a/app/serializers/merge_request_widget_entity.rb +++ b/app/serializers/merge_request_widget_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class MergeRequestWidgetEntity < IssuableEntity expose :state expose :in_progress_merge_commit_sha diff --git a/app/serializers/note_attachment_entity.rb b/app/serializers/note_attachment_entity.rb index 1ad50568ab9..dc801e2bf4e 100644 --- a/app/serializers/note_attachment_entity.rb +++ b/app/serializers/note_attachment_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NoteAttachmentEntity < Grape::Entity expose :url expose :filename diff --git a/app/serializers/note_entity.rb b/app/serializers/note_entity.rb index 0e1f94a9f61..daa5c24d0f5 100644 --- a/app/serializers/note_entity.rb +++ b/app/serializers/note_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NoteEntity < API::Entities::Note include RequestAwareEntity include NotesHelper diff --git a/app/serializers/note_user_entity.rb b/app/serializers/note_user_entity.rb index 7289f3a0222..b00dfa7d353 100644 --- a/app/serializers/note_user_entity.rb +++ b/app/serializers/note_user_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class NoteUserEntity < UserEntity unexpose :web_url end diff --git a/app/serializers/pipeline_details_entity.rb b/app/serializers/pipeline_details_entity.rb index 8ba9cac53c4..3b56767f774 100644 --- a/app/serializers/pipeline_details_entity.rb +++ b/app/serializers/pipeline_details_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PipelineDetailsEntity < PipelineEntity expose :details do expose :ordered_stages, as: :stages, using: StageEntity diff --git a/app/serializers/pipeline_entity.rb b/app/serializers/pipeline_entity.rb index f782b411b84..6cf1925adda 100644 --- a/app/serializers/pipeline_entity.rb +++ b/app/serializers/pipeline_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PipelineEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/pipeline_serializer.rb b/app/serializers/pipeline_serializer.rb index 17a022539bb..4a33160afa1 100644 --- a/app/serializers/pipeline_serializer.rb +++ b/app/serializers/pipeline_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class PipelineSerializer < BaseSerializer include WithPagination entity PipelineDetailsEntity diff --git a/app/serializers/project_entity.rb b/app/serializers/project_entity.rb index b3e5fd21e97..60c4ba135d6 100644 --- a/app/serializers/project_entity.rb +++ b/app/serializers/project_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/project_mirror_entity.rb b/app/serializers/project_mirror_entity.rb index a9c08ac021a..8aba244cd11 100644 --- a/app/serializers/project_mirror_entity.rb +++ b/app/serializers/project_mirror_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectMirrorEntity < Grape::Entity expose :id diff --git a/app/serializers/project_note_entity.rb b/app/serializers/project_note_entity.rb index e541bfbee8d..d7c4d0aacc6 100644 --- a/app/serializers/project_note_entity.rb +++ b/app/serializers/project_note_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectNoteEntity < NoteEntity expose :human_access do |note| note.project.team.human_max_access(note.author_id) diff --git a/app/serializers/project_note_serializer.rb b/app/serializers/project_note_serializer.rb index 763ad0bdb3f..2182904e815 100644 --- a/app/serializers/project_note_serializer.rb +++ b/app/serializers/project_note_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectNoteSerializer < BaseSerializer entity ProjectNoteEntity end diff --git a/app/serializers/project_serializer.rb b/app/serializers/project_serializer.rb index 74de1e79a8f..23b96c2fc9e 100644 --- a/app/serializers/project_serializer.rb +++ b/app/serializers/project_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class ProjectSerializer < BaseSerializer entity ProjectEntity end diff --git a/app/serializers/request_aware_entity.rb b/app/serializers/request_aware_entity.rb index d53fcfb8c1b..1524c1291d8 100644 --- a/app/serializers/request_aware_entity.rb +++ b/app/serializers/request_aware_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module RequestAwareEntity extend ActiveSupport::Concern diff --git a/app/serializers/runner_entity.rb b/app/serializers/runner_entity.rb index db26eadab2d..04ec80e0efa 100644 --- a/app/serializers/runner_entity.rb +++ b/app/serializers/runner_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class RunnerEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/stage_entity.rb b/app/serializers/stage_entity.rb index 2516df70ad9..00e6d32ee3a 100644 --- a/app/serializers/stage_entity.rb +++ b/app/serializers/stage_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class StageEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/stage_serializer.rb b/app/serializers/stage_serializer.rb index 091d8e91e43..11fb0d3f852 100644 --- a/app/serializers/stage_serializer.rb +++ b/app/serializers/stage_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class StageSerializer < BaseSerializer include WithPagination diff --git a/app/serializers/status_entity.rb b/app/serializers/status_entity.rb index 47df7f9dcf9..306c30f0323 100644 --- a/app/serializers/status_entity.rb +++ b/app/serializers/status_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class StatusEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/submodule_entity.rb b/app/serializers/submodule_entity.rb index ed1f1ae0ef0..e475a4f301f 100644 --- a/app/serializers/submodule_entity.rb +++ b/app/serializers/submodule_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class SubmoduleEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/time_trackable_entity.rb b/app/serializers/time_trackable_entity.rb index e81cd7bec72..613d19703a4 100644 --- a/app/serializers/time_trackable_entity.rb +++ b/app/serializers/time_trackable_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + module TimeTrackableEntity extend ActiveSupport::Concern extend Grape diff --git a/app/serializers/tree_entity.rb b/app/serializers/tree_entity.rb index 9f1b485347f..9b7dc80e1d9 100644 --- a/app/serializers/tree_entity.rb +++ b/app/serializers/tree_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class TreeEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/tree_root_entity.rb b/app/serializers/tree_root_entity.rb index 496f070ddbd..f1cfcd943d8 100644 --- a/app/serializers/tree_root_entity.rb +++ b/app/serializers/tree_root_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + # TODO: Inherit from TreeEntity, when `Tree` implements `id` and `name` like `Gitlab::Git::Tree`. class TreeRootEntity < Grape::Entity include RequestAwareEntity diff --git a/app/serializers/tree_serializer.rb b/app/serializers/tree_serializer.rb index 713ade23bc9..536b8ab1ae2 100644 --- a/app/serializers/tree_serializer.rb +++ b/app/serializers/tree_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class TreeSerializer < BaseSerializer entity TreeRootEntity end diff --git a/app/serializers/user_entity.rb b/app/serializers/user_entity.rb index 876512b12dc..6236d66ff4a 100644 --- a/app/serializers/user_entity.rb +++ b/app/serializers/user_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UserEntity < API::Entities::UserBasic include RequestAwareEntity diff --git a/app/serializers/user_serializer.rb b/app/serializers/user_serializer.rb index 49a71ebac61..2111e1b5667 100644 --- a/app/serializers/user_serializer.rb +++ b/app/serializers/user_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class UserSerializer < BaseSerializer entity UserEntity end diff --git a/app/serializers/variable_entity.rb b/app/serializers/variable_entity.rb index d576745c073..85cf367fe51 100644 --- a/app/serializers/variable_entity.rb +++ b/app/serializers/variable_entity.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class VariableEntity < Grape::Entity expose :id expose :key diff --git a/app/serializers/variable_serializer.rb b/app/serializers/variable_serializer.rb index 32ae82ab51c..586666cad8e 100644 --- a/app/serializers/variable_serializer.rb +++ b/app/serializers/variable_serializer.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + class VariableSerializer < BaseSerializer entity VariableEntity end diff --git a/app/services/users/build_service.rb b/app/services/users/build_service.rb index c69b46cab5a..acc2fa153ae 100644 --- a/app/services/users/build_service.rb +++ b/app/services/users/build_service.rb @@ -64,7 +64,8 @@ module Users :theme_id, :twitter, :username, - :website_url + :website_url, + :private_profile ] end diff --git a/app/views/profiles/show.html.haml b/app/views/profiles/show.html.haml index 507cd5dcc12..a4835584b50 100644 --- a/app/views/profiles/show.html.haml +++ b/app/views/profiles/show.html.haml @@ -69,6 +69,12 @@ = f.text_field :location = f.text_field :organization = f.text_area :bio, rows: 4, maxlength: 250, help: 'Tell us about yourself in fewer than 250 characters.' + %hr + %h5 Private profile + - private_profile_label = capture do + Don't display activity-related personal information on your profile + = link_to icon('question-circle'), help_page_path('user/profile/index.md', anchor: 'private-profile') + = f.check_box :private_profile, label: private_profile_label .prepend-top-default.append-bottom-default = f.submit 'Update profile settings', class: 'btn btn-success' = link_to 'Cancel', user_path(current_user), class: 'btn btn-cancel' diff --git a/app/views/users/show.html.haml b/app/views/users/show.html.haml index b2ec7166832..8d9e86d02c4 100644 --- a/app/views/users/show.html.haml +++ b/app/views/users/show.html.haml @@ -23,8 +23,9 @@ = link_to new_abuse_report_path(user_id: @user.id, ref_url: request.referrer), class: 'btn', title: 'Report abuse', data: { toggle: 'tooltip', placement: 'bottom', container: 'body' } do = icon('exclamation-circle') - = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do - = icon('rss') + - if can?(current_user, :read_user_profile, @user) + = link_to user_path(@user, rss_url_options), class: 'btn btn-default has-tooltip', title: 'Subscribe', 'aria-label': 'Subscribe' do + = icon('rss') - if current_user && current_user.admin? = link_to [:admin, @user], class: 'btn btn-default', title: 'View user in admin area', data: {toggle: 'tooltip', placement: 'bottom', container: 'body'} do @@ -40,10 +41,12 @@ = @user.name .cover-desc.member-date - %span.middle-dot-divider - @#{@user.username} - %span.middle-dot-divider - Member since #{@user.created_at.to_date.to_s(:long)} + %p + %span.middle-dot-divider + @#{@user.username} + - if can?(current_user, :read_user_profile, @user) + %span.middle-dot-divider + Member since #{@user.created_at.to_date.to_s(:long)} .cover-desc - unless @user.public_email.blank? @@ -78,30 +81,31 @@ %p.profile-user-bio = @user.bio - .scrolling-tabs-container - .fade-left= icon('angle-left') - .fade-right= icon('angle-right') - %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs - - if profile_tab?(:activity) - %li.js-activity-tab - = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do - Activity - - if profile_tab?(:groups) - %li.js-groups-tab - = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do - Groups - - if profile_tab?(:contributed) - %li.js-contributed-tab - = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do - Contributed projects - - if profile_tab?(:projects) - %li.js-projects-tab - = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do - Personal projects - - if profile_tab?(:snippets) - %li.js-snippets-tab - = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do - Snippets + - unless profile_tabs.empty? + .scrolling-tabs-container + .fade-left= icon('angle-left') + .fade-right= icon('angle-right') + %ul.nav-links.user-profile-nav.scrolling-tabs.nav.nav-tabs + - if profile_tab?(:activity) + %li.js-activity-tab + = link_to user_path, data: { target: 'div#activity', action: 'activity', toggle: 'tab' } do + Activity + - if profile_tab?(:groups) + %li.js-groups-tab + = link_to user_groups_path, data: { target: 'div#groups', action: 'groups', toggle: 'tab', endpoint: user_groups_path(format: :json) } do + Groups + - if profile_tab?(:contributed) + %li.js-contributed-tab + = link_to user_contributed_projects_path, data: { target: 'div#contributed', action: 'contributed', toggle: 'tab', endpoint: user_contributed_projects_path(format: :json) } do + Contributed projects + - if profile_tab?(:projects) + %li.js-projects-tab + = link_to user_projects_path, data: { target: 'div#projects', action: 'projects', toggle: 'tab', endpoint: user_projects_path(format: :json) } do + Personal projects + - if profile_tab?(:snippets) + %li.js-snippets-tab + = link_to user_snippets_path, data: { target: 'div#snippets', action: 'snippets', toggle: 'tab', endpoint: user_snippets_path(format: :json) } do + Snippets %div{ class: container_class } .tab-content @@ -137,3 +141,13 @@ .loading-status = spinner + + - if profile_tabs.empty? + .row + .col-12 + .svg-content + = image_tag 'illustrations/profile_private_mode.svg' + .col-12.text-center + .text-content + %h4 + This user has a private profile diff --git a/changelogs/unreleased/38604-add-private-profile.yml b/changelogs/unreleased/38604-add-private-profile.yml new file mode 100644 index 00000000000..e40e7d9321e --- /dev/null +++ b/changelogs/unreleased/38604-add-private-profile.yml @@ -0,0 +1,5 @@ +--- +title: Add an option to have a private profile on GitLab. +merge_request: 20387 +author: jxterry +type: added diff --git a/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml b/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml new file mode 100644 index 00000000000..b60aeba860a --- /dev/null +++ b/changelogs/unreleased/43011-typecast-markdownversion-prop-notesapp.yml @@ -0,0 +1,5 @@ +--- +title: Fix Vue datatype errors for markdownVersion parsing +merge_request: 20800 +author: +type: fixed diff --git a/changelogs/unreleased/frozen-string-enable-app-serializers.yml b/changelogs/unreleased/frozen-string-enable-app-serializers.yml new file mode 100644 index 00000000000..40c7b695d39 --- /dev/null +++ b/changelogs/unreleased/frozen-string-enable-app-serializers.yml @@ -0,0 +1,5 @@ +--- +title: Enable frozen string in app/serializers/**/*.rb +merge_request: 20726 +author: gfyoung +type: performance diff --git a/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml b/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml new file mode 100644 index 00000000000..a2fca4c5b91 --- /dev/null +++ b/changelogs/unreleased/kp-stacked-progress-bar-decimal-places.yml @@ -0,0 +1,5 @@ +--- +title: Show decimal place up to single digit in Stacked Progress Bar +merge_request: 20776 +author: +type: changed diff --git a/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml b/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml new file mode 100644 index 00000000000..b9444440cb9 --- /dev/null +++ b/changelogs/unreleased/sh-bump-sanitize-4-6-6.yml @@ -0,0 +1,5 @@ +--- +title: Bump nokogiri to 1.8.4 and sanitize to 4.6.6 for performance +merge_request: 20795 +author: +type: performance diff --git a/db/migrate/20180722103201_add_private_profile_to_users.rb b/db/migrate/20180722103201_add_private_profile_to_users.rb new file mode 100644 index 00000000000..4f7ef1322d8 --- /dev/null +++ b/db/migrate/20180722103201_add_private_profile_to_users.rb @@ -0,0 +1,10 @@ +# See http://doc.gitlab.com/ce/development/migration_style_guide.html +# for more information on how to write migrations for GitLab. + +class AddPrivateProfileToUsers < ActiveRecord::Migration + DOWNTIME = false + + def change + add_column :users, :private_profile, :boolean + end +end diff --git a/db/schema.rb b/db/schema.rb index 1a5555fb3a4..3db11d8447e 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,7 +11,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20180704204006) do +ActiveRecord::Schema.define(version: 20180722103201) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -2124,6 +2124,7 @@ ActiveRecord::Schema.define(version: 20180704204006) do t.integer "theme_id", limit: 2 t.integer "accepted_term_id" t.string "feed_token" + t.boolean "private_profile" end add_index "users", ["admin"], name: "index_users_on_admin", using: :btree diff --git a/doc/api/users.md b/doc/api/users.md index 72fdaaa2c74..1bcc7b7f346 100644 --- a/doc/api/users.md +++ b/doc/api/users.md @@ -105,7 +105,8 @@ GET /users "can_create_group": true, "can_create_project": true, "two_factor_enabled": true, - "external": false + "external": false, + "private_profile": false }, { "id": 2, @@ -135,7 +136,8 @@ GET /users "can_create_group": true, "can_create_project": true, "two_factor_enabled": true, - "external": false + "external": false, + "private_profile": false } ] ``` @@ -248,7 +250,8 @@ Parameters: "can_create_group": true, "can_create_project": true, "two_factor_enabled": true, - "external": false + "external": false, + "private_profile": false } ``` @@ -288,6 +291,7 @@ Parameters: - `skip_confirmation` (optional) - Skip confirmation - true or false (default) - `external` (optional) - Flags the user as external - true or false(default) - `avatar` (optional) - Image file for user's avatar +- `private_profile (optional) - User's profile is private - true or false ## User modification @@ -318,6 +322,7 @@ Parameters: - `skip_reconfirmation` (optional) - Skip reconfirmation - true or false (default) - `external` (optional) - Flags the user as external - true or false(default) - `avatar` (optional) - Image file for user's avatar +- `private_profile (optional) - User's profile is private - true or false On password update, user will be forced to change it upon next login. Note, at the moment this method does only return a `404` error, @@ -382,7 +387,8 @@ GET /user "can_create_group": true, "can_create_project": true, "two_factor_enabled": true, - "external": false + "external": false, + "private_profile": false } ``` @@ -429,7 +435,8 @@ GET /user "can_create_group": true, "can_create_project": true, "two_factor_enabled": true, - "external": false + "external": false, + "private_profile": false } ``` diff --git a/doc/user/profile/index.md b/doc/user/profile/index.md index 91cdef8d1dd..96a08c04905 100644 --- a/doc/user/profile/index.md +++ b/doc/user/profile/index.md @@ -68,6 +68,28 @@ Alternatively, you can follow [this detailed procedure from the GitLab Team Hand which also covers the case where you have projects hosted with [GitLab Pages](../project/pages/index.md). +## Private profile + +The following information will be hidden from the user profile page (https://gitlab.example.com/username) if this feature is enabled: + +- Atom feed +- Date when account is created +- Activity tab +- Groups tab +- Contributed projects tab +- Personal projects tab +- Snippets tab + +To enable private profile: + +1. Navigate to your personal [profile settings](#profile-settings). +1. Check the "Private profile" option. +1. Hit **Update profile settings**. + + +NOTE: **Note:** +You and GitLab admins can see your the abovementioned information on your profile even if it is private. + ## Troubleshooting ### Why do I keep getting signed out? diff --git a/doc/user/profile/personal_access_tokens.md b/doc/user/profile/personal_access_tokens.md index 9b4fdd65e2f..601db5f424d 100644 --- a/doc/user/profile/personal_access_tokens.md +++ b/doc/user/profile/personal_access_tokens.md @@ -48,6 +48,7 @@ the following table. | `api` | Grants complete access to the API (read/write) ([introduced][ce-5951] in GitLab 8.15). Required for accessing Git repositories over HTTP when 2FA is enabled. | | `read_registry` | Allows to read [container registry] images if a project is private and authorization is required ([introduced][ce-11845] in GitLab 9.3). | | `sudo` | Allows performing API actions as any user in the system (if the authenticated user is an admin) ([introduced][ce-14838] in GitLab 10.2). | +| `read_repository` | Allows read-access to the repository through git clone). | [2fa]: ../account/two_factor_authentication.md [api]: ../../api/README.md diff --git a/doc/user/project/issue_board.md b/doc/user/project/issue_board.md index 860edb8e6f7..6dfdbe6c0d5 100644 --- a/doc/user/project/issue_board.md +++ b/doc/user/project/issue_board.md @@ -27,7 +27,7 @@ You create issues, host code, perform reviews, build, test, and deploy from one single platform. Issue Boards help you to visualize and manage the entire process _in_ GitLab. -With [Multiple Issue Boards](#multiple-issue-boards), available +With [Multiple Issue Boards](#use-cases-for-multiple-issue-boards), available only in [GitLab Enterprise Edition](#features-per-tier), you go even further, as you can not only keep yourself and your project organized from a broader perspective with one Issue Board per project, @@ -75,7 +75,7 @@ each team can have their own board to organize their workflow individually. #### Scrum team -With multiple Issue Boards, each team has one board. Now you can move issues through each +With Multiple Issue Boards, each team has one board. Now you can move issues through each part of the process. For instance: **To Do**, **Doing**, and **Done**. #### Organization of topics diff --git a/lib/api/entities.rb b/lib/api/entities.rb index 464a31ee819..e883687f2db 100644 --- a/lib/api/entities.rb +++ b/lib/api/entities.rb @@ -30,7 +30,7 @@ module API end class User < UserBasic - expose :created_at + expose :created_at, if: ->(user, opts) { Ability.allowed?(opts[:current_user], :read_user_profile, user) } expose :bio, :location, :skype, :linkedin, :twitter, :website_url, :organization end @@ -55,6 +55,7 @@ module API expose :can_create_project?, as: :can_create_project expose :two_factor_enabled?, as: :two_factor_enabled expose :external + expose :private_profile end class UserWithAdmin < UserPublic diff --git a/lib/api/keys.rb b/lib/api/keys.rb index 767f27ef334..fd93f797f72 100644 --- a/lib/api/keys.rb +++ b/lib/api/keys.rb @@ -12,7 +12,7 @@ module API key = Key.find(params[:id]) - present key, with: Entities::SSHKeyWithUser + present key, with: Entities::SSHKeyWithUser, current_user: current_user end end end diff --git a/lib/api/users.rb b/lib/api/users.rb index 6da6c2b43de..e83887b3e9e 100644 --- a/lib/api/users.rb +++ b/lib/api/users.rb @@ -42,6 +42,7 @@ module API optional :can_create_group, type: Boolean, desc: 'Flag indicating the user can create groups' optional :external, type: Boolean, desc: 'Flag indicating the user is an external user' optional :avatar, type: File, desc: 'Avatar image for user' + optional :private_profile, type: Boolean, desc: 'Flag indicating the user has a private profile' optional :min_access_level, type: Integer, values: Gitlab::Access.all_values, desc: 'Limit by minimum access level of authenticated user' all_or_none_of :extern_uid, :provider end @@ -97,7 +98,7 @@ module API entity = current_user&.admin? ? Entities::UserWithAdmin : Entities::UserBasic users = users.preload(:identities, :u2f_registrations) if entity == Entities::UserWithAdmin - users, options = with_custom_attributes(users, with: entity) + users, options = with_custom_attributes(users, { with: entity, current_user: current_user }) present paginate(users), options end @@ -114,7 +115,7 @@ module API user = User.find_by(id: params[:id]) not_found!('User') unless user && can?(current_user, :read_user, user) - opts = current_user&.admin? ? { with: Entities::UserWithAdmin } : { with: Entities::User } + opts = { with: current_user&.admin? ? Entities::UserWithAdmin : Entities::User, current_user: current_user } user, opts = with_custom_attributes(user, opts) present user, opts @@ -140,7 +141,7 @@ module API user = ::Users::CreateService.new(current_user, params).execute(skip_authorization: true) if user.persisted? - present user, with: Entities::UserPublic + present user, with: Entities::UserPublic, current_user: current_user else conflict!('Email has already been taken') if User .where(email: user.email) @@ -199,7 +200,7 @@ module API result = ::Users::UpdateService.new(current_user, user_params.except(:extern_uid, :provider).merge(user: user)).execute if result[:status] == :success - present user, with: Entities::UserPublic + present user, with: Entities::UserPublic, current_user: current_user else render_validation_error!(user) end @@ -546,7 +547,7 @@ module API Entities::UserPublic end - present current_user, with: entity + present current_user, with: entity, current_user: current_user end end diff --git a/lib/gitlab/git/repository.rb b/lib/gitlab/git/repository.rb index 7bd5927d15e..0356e8efc5c 100644 --- a/lib/gitlab/git/repository.rb +++ b/lib/gitlab/git/repository.rb @@ -353,8 +353,6 @@ module Gitlab # offset: 5, # after: Time.new(2016, 4, 21, 14, 32, 10) # ) - # - # Gitaly migration: https://gitlab.com/gitlab-org/gitaly/issues/446 def log(options) default_options = { limit: 10, @@ -1029,8 +1027,8 @@ module Gitlab end def clean_stale_repository_files - gitaly_migrate(:repository_cleanup, status: Gitlab::GitalyClient::MigrationStatus::OPT_OUT) do |is_enabled| - gitaly_repository_client.cleanup if is_enabled && exists? + wrapped_gitaly_errors do + gitaly_repository_client.cleanup if exists? end rescue Gitlab::Git::CommandError => e # Don't fail if we can't cleanup Rails.logger.error("Unable to clean repository on storage #{storage} with relative path #{relative_path}: #{e.message}") diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index b0acf4a49ac..071f96a729e 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -2,6 +2,8 @@ require 'spec_helper' describe UsersController do let(:user) { create(:user) } + let(:private_user) { create(:user, private_profile: true) } + let(:public_user) { create(:user) } describe 'GET #show' do context 'with rendered views' do @@ -98,16 +100,47 @@ describe UsersController do expect(assigns(:events)).to be_empty end + + it 'hides events if the user has a private profile' do + Gitlab::DataBuilder::Push.build_sample(project, private_user) + + get :show, username: private_user.username, format: :json + + expect(assigns(:events)).to be_empty + end end end describe 'GET #calendar' do - it 'renders calendar' do - sign_in(user) + context 'for user' do + let(:project) { create(:project) } + + before do + sign_in(user) + project.add_developer(user) + end + + context 'with public profile' do + it 'renders calendar' do + push_data = Gitlab::DataBuilder::Push.build_sample(project, public_user) + EventCreateService.new.push(project, public_user, push_data) + + get :calendar, username: public_user.username, format: :json - get :calendar, username: user.username, format: :json + expect(response).to have_gitlab_http_status(200) + end + end + + context 'with private profile' do + it 'does not render calendar' do + push_data = Gitlab::DataBuilder::Push.build_sample(project, private_user) + EventCreateService.new.push(project, private_user, push_data) - expect(response).to have_gitlab_http_status(200) + get :calendar, username: private_user.username, format: :json + + expect(response).to have_gitlab_http_status(:not_found) + end + end end context 'forked project' do @@ -150,9 +183,26 @@ describe UsersController do expect(assigns(:calendar_date)).to eq(Date.parse('2014-07-31')) end - it 'renders calendar_activities' do - get :calendar_activities, username: user.username - expect(response).to render_template('calendar_activities') + context 'for user' do + context 'with public profile' do + it 'renders calendar_activities' do + push_data = Gitlab::DataBuilder::Push.build_sample(project, public_user) + EventCreateService.new.push(project, public_user, push_data) + + get :calendar_activities, username: public_user.username + expect(assigns[:events]).not_to be_empty + end + end + + context 'with private profile' do + it 'does not render calendar_activities' do + push_data = Gitlab::DataBuilder::Push.build_sample(project, private_user) + EventCreateService.new.push(project, private_user, push_data) + + get :calendar_activities, username: private_user.username + expect(response).to have_gitlab_http_status(:not_found) + end + end end end diff --git a/spec/features/users/show_spec.rb b/spec/features/users/show_spec.rb index 3e2fb704bc6..207c333c636 100644 --- a/spec/features/users/show_spec.rb +++ b/spec/features/users/show_spec.rb @@ -3,15 +3,53 @@ require 'spec_helper' describe 'User page' do let(:user) { create(:user) } - it 'shows all the tabs' do - visit(user_path(user)) - - page.within '.nav-links' do - expect(page).to have_link('Activity') - expect(page).to have_link('Groups') - expect(page).to have_link('Contributed projects') - expect(page).to have_link('Personal projects') - expect(page).to have_link('Snippets') + context 'with public profile' do + it 'shows all the tabs' do + visit(user_path(user)) + + page.within '.nav-links' do + expect(page).to have_link('Activity') + expect(page).to have_link('Groups') + expect(page).to have_link('Contributed projects') + expect(page).to have_link('Personal projects') + expect(page).to have_link('Snippets') + end + end + + it 'does not show private profile message' do + visit(user_path(user)) + + expect(page).not_to have_content("This user has a private profile") + end + end + + context 'with private profile' do + let(:user) { create(:user, private_profile: true) } + + it 'shows no tab' do + visit(user_path(user)) + + expect(page).to have_css("div.profile-header") + expect(page).not_to have_css("ul.nav-links") + end + + it 'shows private profile message' do + visit(user_path(user)) + + expect(page).to have_content("This user has a private profile") + end + + it 'shows own tabs' do + sign_in(user) + visit(user_path(user)) + + page.within '.nav-links' do + expect(page).to have_link('Activity') + expect(page).to have_link('Groups') + expect(page).to have_link('Contributed projects') + expect(page).to have_link('Personal projects') + expect(page).to have_link('Snippets') + end end end diff --git a/spec/finders/user_recent_events_finder_spec.rb b/spec/finders/user_recent_events_finder_spec.rb index da043f94021..58470f4c84d 100644 --- a/spec/finders/user_recent_events_finder_spec.rb +++ b/spec/finders/user_recent_events_finder_spec.rb @@ -29,11 +29,22 @@ describe UserRecentEventsFinder do public_project.add_developer(current_user) end - it 'returns all the events' do - expect(finder.execute).to include(private_event, internal_event, public_event) + context 'when profile is public' do + it 'returns all the events' do + expect(finder.execute).to include(private_event, internal_event, public_event) + end + end + + context 'when profile is private' do + it 'returns no event' do + allow(Ability).to receive(:allowed?).and_call_original + allow(Ability).to receive(:allowed?).with(current_user, :read_user_profile, project_owner).and_return(false) + expect(finder.execute).to be_empty + end end it 'does not include the events if the user cannot read cross project' do + expect(Ability).to receive(:allowed?).and_call_original expect(Ability).to receive(:allowed?).with(current_user, :read_cross_project) { false } expect(finder.execute).to be_empty end diff --git a/spec/helpers/users_helper_spec.rb b/spec/helpers/users_helper_spec.rb index b18c045848f..b079802cb81 100644 --- a/spec/helpers/users_helper_spec.rb +++ b/spec/helpers/users_helper_spec.rb @@ -25,8 +25,20 @@ describe UsersHelper do allow(helper).to receive(:can?).and_return(true) end - it 'includes all the expected tabs' do - expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets) + context 'with public profile' do + it 'includes all the expected tabs' do + expect(tabs).to include(:activity, :groups, :contributed, :projects, :snippets) + end + end + + context 'with private profile' do + before do + allow(helper).to receive(:can?).with(user, :read_user_profile, nil).and_return(false) + end + + it 'is empty' do + expect(tabs).to be_empty + end end end diff --git a/spec/javascripts/lib/utils/common_utils_spec.js b/spec/javascripts/lib/utils/common_utils_spec.js index 41ff59949e5..71b26a315af 100644 --- a/spec/javascripts/lib/utils/common_utils_spec.js +++ b/spec/javascripts/lib/utils/common_utils_spec.js @@ -627,4 +627,23 @@ describe('common_utils', () => { }); }); }); + + describe('roundOffFloat', () => { + it('Rounds off decimal places of a float number with provided precision', () => { + expect(commonUtils.roundOffFloat(3.141592, 3)).toBe(3.142); + }); + + it('Rounds off a float number to a whole number when provided precision is zero', () => { + expect(commonUtils.roundOffFloat(3.141592, 0)).toBe(3); + expect(commonUtils.roundOffFloat(3.5, 0)).toBe(4); + }); + + it('Rounds off float number to nearest 0, 10, 100, 1000 and so on when provided precision is below 0', () => { + expect(commonUtils.roundOffFloat(34567.14159, -1)).toBe(34570); + expect(commonUtils.roundOffFloat(34567.14159, -2)).toBe(34600); + expect(commonUtils.roundOffFloat(34567.14159, -3)).toBe(35000); + expect(commonUtils.roundOffFloat(34567.14159, -4)).toBe(30000); + expect(commonUtils.roundOffFloat(34567.14159, -5)).toBe(0); + }); + }); }); diff --git a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js index de3bf667fb3..076d940961d 100644 --- a/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js +++ b/spec/javascripts/vue_shared/components/stacked_progress_bar_spec.js @@ -10,9 +10,9 @@ const createComponent = (config) => { successLabel: 'Synced', failureLabel: 'Failed', neutralLabel: 'Out of sync', - successCount: 10, - failureCount: 5, - totalCount: 20, + successCount: 25, + failureCount: 10, + totalCount: 5000, }, config); return mountComponent(Component, defaultConfig); @@ -32,7 +32,7 @@ describe('StackedProgressBarComponent', () => { describe('computed', () => { describe('neutralCount', () => { it('returns neutralCount based on totalCount, successCount and failureCount', () => { - expect(vm.neutralCount).toBe(5); // 20 - 10 - 5 + expect(vm.neutralCount).toBe(4965); // 5000 - 25 - 10 }); }); }); @@ -40,7 +40,11 @@ describe('StackedProgressBarComponent', () => { describe('methods', () => { describe('getPercent', () => { it('returns percentage from provided count based on `totalCount`', () => { - expect(vm.getPercent(10)).toBe(50); + expect(vm.getPercent(500)).toBe(10); + }); + + it('returns percentage with decimal place from provided count based on `totalCount`', () => { + expect(vm.getPercent(10)).toBe(0.2); }); }); diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index b3079c0a77b..6a051f865aa 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -11,6 +11,7 @@ describe API::Users do let(:ldap_blocked_user) { create(:omniauth_user, provider: 'ldapmain', state: 'ldap_blocked') } let(:not_existing_user_id) { (User.maximum('id') || 0 ) + 10 } let(:not_existing_pat_id) { (PersonalAccessToken.maximum('id') || 0 ) + 10 } + let(:private_user) { create(:user, private_profile: true) } describe 'GET /users' do context "when unauthenticated" do @@ -254,6 +255,13 @@ describe API::Users do expect(response).to match_response_schema('public_api/v4/user/admin') expect(json_response['is_admin']).to be(false) end + + it "includes the `created_at` field for private users" do + get api("/users/#{private_user.id}", admin) + + expect(response).to match_response_schema('public_api/v4/user/admin') + expect(json_response.keys).to include 'created_at' + end end context 'for an anonymous user' do @@ -272,6 +280,20 @@ describe API::Users do expect(response).to have_gitlab_http_status(404) end + + it "returns the `created_at` field for public users" do + get api("/users/#{user.id}") + + expect(response).to match_response_schema('public_api/v4/user/basic') + expect(json_response.keys).to include 'created_at' + end + + it "does not return the `created_at` field for private users" do + get api("/users/#{private_user.id}") + + expect(response).to match_response_schema('public_api/v4/user/basic') + expect(json_response.keys).not_to include 'created_at' + end end it "returns a 404 error if user id not found" do @@ -374,6 +396,18 @@ describe API::Users do expect(new_user.recently_sent_password_reset?).to eq(true) end + it "creates user with private profile" do + post api('/users', admin), attributes_for(:user, private_profile: true) + + expect(response).to have_gitlab_http_status(201) + + user_id = json_response['id'] + new_user = User.find(user_id) + + expect(new_user).not_to eq(nil) + expect(new_user.private_profile?).to eq(true) + end + it "does not create user with invalid email" do post api('/users', admin), email: 'invalid email', @@ -583,6 +617,13 @@ describe API::Users do expect(user.reload.external?).to be_truthy end + it "updates private profile" do + put api("/users/#{user.id}", admin), { private_profile: true } + + expect(response).to have_gitlab_http_status(200) + expect(user.reload.private_profile).to eq(true) + end + it "does not update admin status" do put api("/users/#{admin_user.id}", admin), { can_create_group: false } |