Commit 703ca040 authored by Vasilii Iakliushin's avatar Vasilii Iakliushin Committed by Mark Florian

Add toggle to remove Analytics item from the sidebar

Contributes to https://gitlab.com/gitlab-org/gitlab/-/issues/224700

* Create a database migration to store project display preferences for
Analytics item on the sidebar.
parent 3b69456b
......@@ -138,6 +138,7 @@ export default {
snippetsAccessLevel: featureAccessLevel.EVERYONE,
pagesAccessLevel: featureAccessLevel.EVERYONE,
metricsDashboardAccessLevel: featureAccessLevel.PROJECT_MEMBERS,
analyticsAccessLevel: featureAccessLevel.EVERYONE,
requirementsAccessLevel: featureAccessLevel.EVERYONE,
containerRegistryEnabled: true,
lfsEnabled: true,
......@@ -241,6 +242,10 @@ export default {
featureAccessLevel.PROJECT_MEMBERS,
this.metricsDashboardAccessLevel,
);
this.analyticsAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
this.analyticsAccessLevel,
);
this.requirementsAccessLevel = Math.min(
featureAccessLevel.PROJECT_MEMBERS,
this.requirementsAccessLevel,
......@@ -266,6 +271,8 @@ export default {
this.snippetsAccessLevel = featureAccessLevel.EVERYONE;
if (this.pagesAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.pagesAccessLevel = featureAccessLevel.EVERYONE;
if (this.analyticsAccessLevel > featureAccessLevel.NOT_ENABLED)
this.analyticsAccessLevel = featureAccessLevel.EVERYONE;
if (this.metricsDashboardAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
this.metricsDashboardAccessLevel = featureAccessLevel.EVERYONE;
if (this.requirementsAccessLevel === featureAccessLevel.PROJECT_MEMBERS)
......@@ -494,6 +501,17 @@ export default {
/>
</project-setting-row>
</div>
<project-setting-row
ref="analytics-settings"
:label="s__('ProjectSettings|Analytics')"
:help-text="s__('ProjectSettings|View project analytics')"
>
<project-feature-setting
v-model="analyticsAccessLevel"
:options="featureAccessLevelOptions"
name="project[project_feature_attributes][analytics_access_level]"
/>
</project-setting-row>
<project-setting-row
v-if="requirementsAvailable"
ref="requirements-settings"
......
......@@ -387,6 +387,7 @@ class ProjectsController < Projects::ApplicationController
wiki_access_level
pages_access_level
metrics_dashboard_access_level
analytics_access_level
operations_access_level
]
end
......
......@@ -463,7 +463,8 @@ module ProjectsHelper
issues: :read_issue,
project_members: :read_project_member,
wiki: :read_wiki,
feature_flags: :read_feature_flag
feature_flags: :read_feature_flag,
analytics: :read_analytics
}
end
......@@ -625,6 +626,7 @@ module ProjectsHelper
wikiAccessLevel: feature.wiki_access_level,
snippetsAccessLevel: feature.snippets_access_level,
pagesAccessLevel: feature.pages_access_level,
analyticsAccessLevel: feature.analytics_access_level,
containerRegistryEnabled: !!project.container_registry_enabled,
lfsEnabled: !!project.lfs_enabled,
emailsDisabled: project.emails_disabled?,
......
......@@ -70,6 +70,10 @@ module ProjectFeaturesCompatibility
write_feature_attribute_string(:metrics_dashboard_access_level, value)
end
def analytics_access_level=(value)
write_feature_attribute_string(:analytics_access_level, value)
end
def operations_access_level=(value)
write_feature_attribute_string(:operations_access_level, value)
end
......
......@@ -385,10 +385,10 @@ class Project < ApplicationRecord
delegate :feature_available?, :builds_enabled?, :wiki_enabled?,
:merge_requests_enabled?, :forking_enabled?, :issues_enabled?,
:pages_enabled?, :snippets_enabled?, :public_pages?, :private_pages?,
:pages_enabled?, :analytics_enabled?, :snippets_enabled?, :public_pages?, :private_pages?,
:merge_requests_access_level, :forking_access_level, :issues_access_level,
:wiki_access_level, :snippets_access_level, :builds_access_level,
:repository_access_level, :pages_access_level, :metrics_dashboard_access_level,
:repository_access_level, :pages_access_level, :metrics_dashboard_access_level, :analytics_access_level,
:operations_enabled?, :operations_access_level, to: :project_feature, allow_nil: true
delegate :show_default_award_emojis, :show_default_award_emojis=,
:show_default_award_emojis?,
......
......@@ -3,7 +3,7 @@
class ProjectFeature < ApplicationRecord
include Featurable
FEATURES = %i(issues forking merge_requests wiki snippets builds repository pages metrics_dashboard operations).freeze
FEATURES = %i(issues forking merge_requests wiki snippets builds repository pages metrics_dashboard analytics operations).freeze
set_available_features(FEATURES)
......@@ -44,6 +44,7 @@ class ProjectFeature < ApplicationRecord
default_value_for :snippets_access_level, value: ENABLED, allows_nil: false
default_value_for :wiki_access_level, value: ENABLED, allows_nil: false
default_value_for :repository_access_level, value: ENABLED, allows_nil: false
default_value_for :analytics_access_level, value: ENABLED, allows_nil: false
default_value_for :metrics_dashboard_access_level, value: PRIVATE, allows_nil: false
default_value_for :operations_access_level, value: ENABLED, allows_nil: false
......
......@@ -151,6 +151,7 @@ class ProjectPolicy < BasePolicy
builds
pages
metrics_dashboard
analytics
operations
]
......@@ -216,6 +217,7 @@ class ProjectPolicy < BasePolicy
enable :award_emoji
enable :read_pages_content
enable :read_release
enable :read_analytics
end
# These abilities are not allowed to admins that are not members of the project,
......@@ -442,6 +444,10 @@ class ProjectPolicy < BasePolicy
prevent(*create_read_update_admin_destroy(:snippet))
end
rule { analytics_disabled }.policy do
prevent(:read_analytics)
end
rule { wiki_disabled }.policy do
prevent(*create_read_update_admin_destroy(:wiki))
prevent(:download_wiki_code)
......@@ -512,6 +518,7 @@ class ProjectPolicy < BasePolicy
enable :download_wiki_code
enable :read_cycle_analytics
enable :read_pages_content
enable :read_analytics
# NOTE: may be overridden by IssuePolicy
enable :read_issue
......
......@@ -4,7 +4,7 @@
- if navbar_links.any?
= nav_link(path: all_paths) do
= link_to analytics_link.link, { data: { qa_selector: 'analytics_anchor' } } do
= link_to analytics_link.link, {class: 'shortcuts-analytics', data: { qa_selector: 'analytics_anchor' } } do
.nav-icon-container
= sprite_icon('chart')
%span.nav-item-name{ data: { qa_selector: 'analytics_link' } }
......
......@@ -324,6 +324,7 @@
= render_if_exists 'layouts/nav/sidebar/project_packages_link'
- if project_nav_tab? :analytics
= render 'layouts/nav/sidebar/analytics_links', links: project_analytics_navbar_links(@project, current_user)
- if project_nav_tab?(:confluence)
......
---
title: Add toggle to remove Analytics left nav item
merge_request: 46011
author:
type: added
# frozen_string_literal: true
# See https://docs.gitlab.com/ee/development/migration_style_guide.html
# for more information on how to write migrations for GitLab.
class AddAnalyticsAccessLevelToProjectFeatures < ActiveRecord::Migration[6.0]
include Gitlab::Database::MigrationHelpers
# Set this constant to true if this migration requires downtime.
DOWNTIME = false
def up
with_lock_retries do
add_column :project_features, :analytics_access_level, :integer, default: 20, null: false
end
end
def down
with_lock_retries do
remove_column :project_features, :analytics_access_level, :integer
end
end
end
d9151c8cafe7a62be9904cb05cc2a6f6e28c2910e69744df1ddd4ad587c83335
\ No newline at end of file
......@@ -15439,7 +15439,8 @@ CREATE TABLE project_features (
forking_access_level integer,
metrics_dashboard_access_level integer,
requirements_access_level integer DEFAULT 20 NOT NULL,
operations_access_level integer DEFAULT 20 NOT NULL
operations_access_level integer DEFAULT 20 NOT NULL,
analytics_access_level integer DEFAULT 20 NOT NULL
);
CREATE SEQUENCE project_features_id_seq
......
......@@ -1045,6 +1045,7 @@ POST /projects
| Attribute | Type | Required | Description |
|-------------------------------------------------------------|---------|------------------------|-------------|
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `approvals_before_merge` **(STARTER)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. |
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`). |
......@@ -1118,6 +1119,7 @@ POST /projects/user/:user_id
| Attribute | Type | Required | Description |
|-------------------------------------------------------------|---------|------------------------|-------------|
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `approvals_before_merge` **(STARTER)** | integer | **{dotted-circle}** No | How many approvers should approve merge requests by default. |
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual` or `timed_incremental`). |
......@@ -1190,6 +1192,7 @@ PUT /projects/:id
| Attribute | Type | Required | Description |
|-------------------------------------------------------------|----------------|------------------------|-------------|
| `allow_merge_on_skipped_pipeline` | boolean | **{dotted-circle}** No | Set whether or not merge requests can be merged with skipped jobs. |
| `analytics_access_level` | string | no | One of `disabled`, `private` or `enabled` |
| `approvals_before_merge` **(STARTER)** | integer | **{dotted-circle}** No | How many approvers should approve merge request by default. |
| `auto_cancel_pending_pipelines` | string | **{dotted-circle}** No | Auto-cancel pending pipelines. This isn't a boolean, but enabled/disabled. |
| `auto_devops_deploy_strategy` | string | **{dotted-circle}** No | Auto Deploy strategy (`continuous`, `manual`, or `timed_incremental`). |
......@@ -2374,6 +2377,7 @@ Example response:
"repository_access_level": "enabled",
"merge_requests_access_level": "enabled",
"forking_access_level": "enabled",
"analytics_access_level": "enabled",
"wiki_access_level": "enabled",
"builds_access_level": "enabled",
"snippets_access_level": "enabled",
......
......@@ -68,6 +68,7 @@ Use the switches to enable or disable the following features:
| **Container Registry** | | Activates a [registry](../../packages/container_registry/) for your Docker images |
| **Git Large File Storage** | | Enables the use of [large files](../../../topics/git/lfs/index.md#git-large-file-storage-lfs) |
| **Packages** | | Supports configuration of a [package registry](../../../administration/packages/index.md#gitlab-package-registry-administration) functionality |
| **Analytics** | ✓ | Enables [analytics](../../analytics/) |
| **Wiki** | ✓ | Enables a separate system for [documentation](../wiki/) |
| **Snippets** | ✓ | Enables [sharing of code and text](../../snippets.md) |
| **Pages** | ✓ | Allows you to [publish static websites](../pages/) |
......
......@@ -14,6 +14,7 @@ module EE
:repository_access_level,
:pages_access_level,
:metrics_dashboard_access_level,
:analytics_access_level,
:operations_access_level,
:requirements_access_level].freeze
......
......@@ -68,6 +68,7 @@ module API
expose(:snippets_access_level) { |project, options| project.project_feature.string_access_level(:snippets) }
expose(:pages_access_level) { |project, options| project.project_feature.string_access_level(:pages) }
expose(:operations_access_level) { |project, options| project.project_feature.string_access_level(:operations) }
expose(:analytics_access_level) { |project, options| project.project_feature.string_access_level(:analytics) }
expose :emails_disabled
expose :shared_runners_enabled
......
......@@ -33,6 +33,7 @@ module API
optional :snippets_access_level, type: String, values: %w(disabled private enabled), desc: 'Snippets access level. One of `disabled`, `private` or `enabled`'
optional :pages_access_level, type: String, values: %w(disabled private enabled public), desc: 'Pages access level. One of `disabled`, `private`, `enabled` or `public`'
optional :operations_access_level, type: String, values: %w(disabled private enabled), desc: 'Operations access level. One of `disabled`, `private` or `enabled`'
optional :analytics_access_level, type: String, values: %w(disabled private enabled), desc: 'Analytics access level. One of `disabled`, `private` or `enabled`'
optional :emails_disabled, type: Boolean, desc: 'Disable email notifications'
optional :show_default_award_emojis, type: Boolean, desc: 'Show default award emojis'
......
......@@ -21757,6 +21757,9 @@ msgstr ""
msgid "ProjectSettings|Allow users to request access"
msgstr ""
msgid "ProjectSettings|Analytics"
msgstr ""
msgid "ProjectSettings|Automatically resolve merge request diff discussions when they become outdated"
msgstr ""
......@@ -22000,6 +22003,9 @@ msgstr ""
msgid "ProjectSettings|View and edit files in this project. Non-project members will only have read access"
msgstr ""
msgid "ProjectSettings|View project analytics"
msgstr ""
msgid "ProjectSettings|When approved for merge, merge requests are queued and pipelines validate the combined results of the source and target branches before merge."
msgstr ""
......
......@@ -14,7 +14,7 @@ RSpec.describe 'Edit Project Settings' do
sign_in(member)
end
tools = { builds: "pipelines", issues: "issues", wiki: "wiki", snippets: "snippets", merge_requests: "merge_requests" }
tools = { builds: "pipelines", issues: "issues", wiki: "wiki", snippets: "snippets", merge_requests: "merge_requests", analytics: "analytics" }
tools.each do |tool_name, shortcut_name|
describe "feature #{tool_name}" do
......
......@@ -21,6 +21,7 @@ const defaultProps = {
wikiAccessLevel: 20,
snippetsAccessLevel: 20,
pagesAccessLevel: 10,
analyticsAccessLevel: 20,
containerRegistryEnabled: true,
lfsEnabled: true,
emailsDisabled: false,
......@@ -79,6 +80,8 @@ describe('Settings Panel', () => {
const findRepositoryFeatureSetting = () =>
findRepositoryFeatureProjectRow().find(projectFeatureSetting);
const findAnalyticsRow = () => wrapper.find({ ref: 'analytics-settings' });
beforeEach(() => {
wrapper = mountComponent();
});
......@@ -557,4 +560,12 @@ describe('Settings Panel', () => {
});
});
});
describe('Analytics', () => {
it('should show the analytics toggle', async () => {
await wrapper.vm.$nextTick();
expect(findAnalyticsRow().exists()).toBe(true);
});
});
});
......@@ -578,6 +578,7 @@ ProjectFeature:
- pages_access_level
- metrics_dashboard_access_level
- requirements_access_level
- analytics_access_level
- operations_access_level
- created_at
- updated_at
......
......@@ -977,6 +977,34 @@ RSpec.describe ProjectPolicy do
end
end
describe 'read_analytics' do
context 'anonymous user' do
let(:current_user) { anonymous }
it { is_expected.to be_allowed(:read_analytics) }
end
context 'project member' do
let(:project) { private_project }
%w(guest reporter developer maintainer).each do |role|
context role do
let(:current_user) { send(role) }
it { is_expected.to be_allowed(:read_analytics) }
context "without access to Analytics" do
before do
project.project_feature.update!(analytics_access_level: ProjectFeature::DISABLED)
end
it { is_expected.to be_disallowed(:read_analytics) }
end
end
end
end
end
it_behaves_like 'Self-managed Core resource access tokens'
describe 'operations feature' do
......
......@@ -873,6 +873,7 @@ RSpec.describe API::Projects do
jobs_enabled: false,
merge_requests_enabled: false,
forking_access_level: 'disabled',
analytics_access_level: 'disabled',
wiki_enabled: false,
resolve_outdated_diff_discussions: false,
remove_source_branch_after_merge: true,
......@@ -903,6 +904,7 @@ RSpec.describe API::Projects do
expect(project.project_feature.merge_requests_access_level).to eq(ProjectFeature::DISABLED)
expect(project.project_feature.wiki_access_level).to eq(ProjectFeature::DISABLED)
expect(project.operations_access_level).to eq(ProjectFeature::DISABLED)
expect(project.project_feature.analytics_access_level).to eq(ProjectFeature::DISABLED)
end
it 'creates a project using a template' do
......@@ -1623,6 +1625,7 @@ RSpec.describe API::Projects do
expect(json_response['issues_access_level']).to be_present
expect(json_response['merge_requests_access_level']).to be_present
expect(json_response['forking_access_level']).to be_present
expect(json_response['analytics_access_level']).to be_present
expect(json_response['wiki_access_level']).to be_present
expect(json_response['builds_access_level']).to be_present
expect(json_response['operations_access_level']).to be_present
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment