Commit 470cdaa3 authored by Kamil Trzciński's avatar Kamil Trzciński

Merge branch '7048_set_security_dashboard_as_default_view_for_groups_be' into 'master'

Set Security Dashboard as default view for groups

See merge request gitlab-org/gitlab-ee!7889
parents d4b23522 103586d9
import initGroupDetails from '../shared/group_details';
document.addEventListener('DOMContentLoaded', () => {
initGroupDetails('details');
});
/* eslint-disable no-new */
import { getPagePath } from '~/lib/utils/common_utils';
import { ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED } from '~/groups/constants';
import NewGroupChild from '~/groups/new_group_child';
import notificationsDropdown from '~/notifications_dropdown';
import NotificationsForm from '~/notifications_form';
import ProjectsList from '~/projects_list';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import GroupTabs from './group_tabs';
export default function initGroupDetails(actionName = 'show') {
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup');
const loadableActions = [ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED];
const paths = window.location.pathname.split('/');
const subpath = paths[paths.length - 1];
let action = loadableActions.includes(subpath) ? subpath : getPagePath(1);
if (actionName && action === actionName) {
action = 'show'; // 'show' resets GroupTabs to default action through base class
}
new GroupTabs({ parentEl: '.groups-listing', action });
new ShortcutsNavigation();
new NotificationsForm();
notificationsDropdown();
new ProjectsList();
if (newGroupChildWrapper) {
new NewGroupChild(newGroupChildWrapper);
}
}
/* eslint-disable no-new */ import initGroupDetails from '../shared/group_details';
import { getPagePath } from '~/lib/utils/common_utils';
import { ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED } from '~/groups/constants';
import NewGroupChild from '~/groups/new_group_child';
import notificationsDropdown from '~/notifications_dropdown';
import NotificationsForm from '~/notifications_form';
import ProjectsList from '~/projects_list';
import ShortcutsNavigation from '~/behaviors/shortcuts/shortcuts_navigation';
import GroupTabs from './group_tabs';
document.addEventListener('DOMContentLoaded', () => { document.addEventListener('DOMContentLoaded', () => {
const newGroupChildWrapper = document.querySelector('.js-new-project-subgroup'); initGroupDetails();
const loadableActions = [ACTIVE_TAB_SHARED, ACTIVE_TAB_ARCHIVED];
const paths = window.location.pathname.split('/');
const subpath = paths[paths.length - 1];
const action = loadableActions.includes(subpath) ? subpath : getPagePath(1);
new GroupTabs({ parentEl: '.groups-listing', action });
new ShortcutsNavigation();
new NotificationsForm();
notificationsDropdown();
new ProjectsList();
if (newGroupChildWrapper) {
new NewGroupChild(newGroupChildWrapper);
}
}); });
...@@ -91,6 +91,7 @@ export default class UserTabs { ...@@ -91,6 +91,7 @@ export default class UserTabs {
this.actions = Object.keys(this.loaded); this.actions = Object.keys(this.loaded);
this.bindEvents(); this.bindEvents();
// TODO: refactor to make this configurable via constructor params with a default value of 'show'
if (this.action === 'show') { if (this.action === 'show') {
this.action = this.defaultAction; this.action = this.defaultAction;
} }
......
...@@ -58,11 +58,24 @@ class GroupsController < Groups::ApplicationController ...@@ -58,11 +58,24 @@ class GroupsController < Groups::ApplicationController
def show def show
respond_to do |format| respond_to do |format|
format.html format.html do
render_show_html
end
format.atom do format.atom do
load_events render_details_view_atom
render layout: 'xml.atom' end
end
end
def details
respond_to do |format|
format.html do
render_details_html
end
format.atom do
render_details_view_atom
end end
end end
end end
...@@ -119,6 +132,19 @@ class GroupsController < Groups::ApplicationController ...@@ -119,6 +132,19 @@ class GroupsController < Groups::ApplicationController
protected protected
def render_show_html
render 'groups/show'
end
def render_details_html
render 'groups/show'
end
def render_details_view_atom
load_events
render layout: 'xml.atom', template: 'groups/show'
end
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def authorize_create_group! def authorize_create_group!
allowed = if params[:parent_id].present? allowed = if params[:parent_id].present?
...@@ -178,8 +204,8 @@ class GroupsController < Groups::ApplicationController ...@@ -178,8 +204,8 @@ class GroupsController < Groups::ApplicationController
.includes(:namespace) .includes(:namespace)
@events = EventCollection @events = EventCollection
.new(@projects, offset: params[:offset].to_i, filter: event_filter) .new(@projects, offset: params[:offset].to_i, filter: event_filter)
.to_a .to_a
Events::RenderService Events::RenderService
.new(current_user) .new(current_user)
......
...@@ -4,6 +4,7 @@ module GroupsHelper ...@@ -4,6 +4,7 @@ module GroupsHelper
def group_overview_nav_link_paths def group_overview_nav_link_paths
%w[ %w[
groups#show groups#show
groups#details
groups#activity groups#activity
groups#subgroups groups#subgroups
analytics#show analytics#show
......
...@@ -20,13 +20,14 @@ ...@@ -20,13 +20,14 @@
= _('Overview') = _('Overview')
%ul.sidebar-sub-level-items %ul.sidebar-sub-level-items
= nav_link(path: ['groups#show', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do = nav_link(path: ['groups#show', 'groups#details', 'groups#activity', 'groups#subgroups'], html_options: { class: "fly-out-top-item" } ) do
= link_to group_path(@group) do = link_to group_path(@group) do
%strong.fly-out-top-item-name %strong.fly-out-top-item-name
= _('Overview') = _('Overview')
%li.divider.fly-out-top-item %li.divider.fly-out-top-item
= nav_link(path: ['groups#show', 'groups#subgroups'], html_options: { class: 'home' }) do
= link_to group_path(@group), title: _('Group details') do = nav_link(path: ['groups#show', 'groups#details', 'groups#subgroups'], html_options: { class: 'home' }) do
= link_to details_group_path(@group), title: _('Group details') do
%span %span
= _('Details') = _('Details')
...@@ -40,9 +41,9 @@ ...@@ -40,9 +41,9 @@
- if group_sidebar_link?(:contribution_analytics) - if group_sidebar_link?(:contribution_analytics)
= nav_link(path: 'analytics#show') do = nav_link(path: 'analytics#show') do
= link_to group_analytics_path(@group), title: 'Contribution Analytics', data: {placement: 'right'} do = link_to group_analytics_path(@group), title: _('Contribution Analytics'), data: { placement: 'right' } do
%span %span
Contribution Analytics = _('Contribution Analytics')
= render_if_exists "layouts/nav/ee/epic_link", group: @group = render_if_exists "layouts/nav/ee/epic_link", group: @group
......
...@@ -14,6 +14,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do ...@@ -14,6 +14,7 @@ constraints(::Constraints::GroupUrlConstrainer.new) do
get :issues, as: :issues_group get :issues, as: :issues_group
get :merge_requests, as: :merge_requests_group get :merge_requests, as: :merge_requests_group
get :projects, as: :projects_group get :projects, as: :projects_group
get :details, as: :details_group # needed for EE but left here to provide helpers for CE partials
get :activity, as: :activity_group get :activity, as: :activity_group
put :transfer, as: :transfer_group put :transfer, as: :transfer_group
# TODO: Remove as part of refactor in https://gitlab.com/gitlab-org/gitlab-ce/issues/49693 # TODO: Remove as part of refactor in https://gitlab.com/gitlab-org/gitlab-ce/issues/49693
......
import initGroupDetails from '~/pages/groups/shared/group_details';
import initSecurityDashboard from 'ee/security_dashboard/index';
document.addEventListener('DOMContentLoaded', () => {
if (document.querySelector('#js-group-security-dashboard')) {
initSecurityDashboard();
} else {
initGroupDetails();
}
});
...@@ -3,6 +3,16 @@ ...@@ -3,6 +3,16 @@
module EE module EE
module GroupsController module GroupsController
extend ActiveSupport::Concern extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
override :render_show_html
def render_show_html
if redirect_show_path
redirect_to redirect_show_path, status: :temporary_redirect
else
super
end
end
def group_params_attributes def group_params_attributes
super + group_params_ee super + group_params_ee
...@@ -25,5 +35,24 @@ module EE ...@@ -25,5 +35,24 @@ module EE
def current_group def current_group
@group @group
end end
def redirect_show_path
strong_memoize(:redirect_show_path) do
case group_view
when 'security_dashboard'
helpers.group_security_dashboard_path(group) if ::Feature.enabled?(:group_overview_security_dashboard)
else
nil
end
end
end
def group_view
current_user&.group_view || default_group_view
end
def default_group_view
EE::User::DEFAULT_GROUP_VIEW
end
end end
end end
# frozen_string_literal: true # frozen_string_literal: true
class Groups::Security::ApplicationController < Groups::ApplicationController class Groups::Security::ApplicationController < Groups::ApplicationController
before_action :ensure_security_dashboard_feature_enabled before_action :ensure_security_dashboard_feature_enabled!
before_action :authorize_read_group_security_dashboard! before_action :authorize_read_group_security_dashboard!
private protected
def ensure_security_dashboard_feature_enabled def ensure_security_dashboard_feature_enabled!
render_404 unless @group.feature_available?(:security_dashboard) render_404 unless group.feature_available?(:security_dashboard)
end end
def authorize_read_group_security_dashboard! def authorize_read_group_security_dashboard!
render_403 unless can?(current_user, :read_group_security_dashboard, group) render_403 unless helpers.can_read_group_security_dashboard?(group)
end end
end end
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
module EE module EE
module PreferencesHelper module PreferencesHelper
extend ::Gitlab::Utils::Override extend ::Gitlab::Utils::Override
include ::Groups::Security::DashboardHelper
override :excluded_dashboard_choices override :excluded_dashboard_choices
def excluded_dashboard_choices def excluded_dashboard_choices
...@@ -13,9 +14,10 @@ module EE ...@@ -13,9 +14,10 @@ module EE
def group_view_choices def group_view_choices
strong_memoize(:group_view_choices) do strong_memoize(:group_view_choices) do
[[_('Details (default)'), :details]].tap do |choices| choices = []
choices << [_('Security dashboard'), :security_dashboard] if group_view_security_dashboard_enabled? choices << [_('Details (default)'), :details]
end choices << [_('Security dashboard'), :security_dashboard] if group_view_security_dashboard_enabled?
choices
end end
end end
......
# frozen_string_literal: true
module Groups
module Security
module DashboardHelper
def can_read_group_security_dashboard?(group)
can?(current_user, :read_group_security_dashboard, group)
end
end
end
end
- if can?(current_user, :read_group_security_dashboard, @group) - if @group.feature_available?(:security_dashboard)
= nav_link(path: 'groups/security/dashboard#show') do = nav_link(path: 'groups/security/dashboard#show') do
= link_to group_security_dashboard_path(@group), title: _('Security Dashboard') do = link_to group_security_dashboard_path(@group), title: _('Security Dashboard') do
%span= _('Security Dashboard') %span= _('Security Dashboard')
---
title: Enabled setting the Security Dashboard as a default view for groups
merge_request: 7889
author:
type: added
# frozen_string_literal: true
require 'spec_helper'
describe Groups::Security::DashboardController do
let(:user) { create(:user) }
let(:group) { create(:group) }
before do
sign_in(user)
end
describe 'GET show' do
subject { get :show, params: { group_id: group.to_param } }
context 'when security dashboard feature is enabled' do
before do
stub_licensed_features(security_dashboard: true)
end
context 'and user is allowed to access group security dashboard' do
before do
group.add_developer(user)
end
it { is_expected.to have_gitlab_http_status(200) }
end
context 'when user is not allowed to access group security dashboard' do
it { is_expected.to have_gitlab_http_status(403) }
end
end
context 'when security dashboard feature is disabled' do
it { is_expected.to have_gitlab_http_status(404) }
end
end
end
require 'spec_helper' require 'spec_helper'
describe GroupsHelper do describe GroupsHelper do
describe '#group_sidebar_links' do before do
let(:user) { create(:user) } allow(helper).to receive(:current_user) { user }
let(:group) { create(:group, :private) } end
let(:user) { create(:user, group_view: :security_dashboard) }
let(:group) { create(:group, :private) }
describe '#group_sidebar_links' do
before do before do
allow(helper).to receive(:current_user) { user }
group.add_owner(user) group.add_owner(user)
helper.instance_variable_set(:@group, group) helper.instance_variable_set(:@group, group)
allow(helper).to receive(:can?) { |*args| Ability.allowed?(*args) } allow(helper).to receive(:can?) { |*args| Ability.allowed?(*args) }
......
# frozen_string_literal: true
shared_examples 'ensures security dashboard permissions' do
context 'when security dashboard feature is enabled' do
before do
stub_licensed_features(security_dashboard: true)
end
context 'and user is allowed to access group security dashboard' do
before do
group.add_developer(user)
end
it { is_expected.to have_gitlab_http_status(200) }
end
context 'when user is not allowed to access group security dashboard' do
it { is_expected.to have_gitlab_http_status(403) }
end
end
context 'when security dashboard feature is disabled' do
it { is_expected.to have_gitlab_http_status(404) }
end
end
...@@ -2,9 +2,11 @@ require 'spec_helper' ...@@ -2,9 +2,11 @@ require 'spec_helper'
describe 'layouts/nav/sidebar/_group' do describe 'layouts/nav/sidebar/_group' do
before do before do
assign(:group, create(:group)) assign(:group, group)
end end
let(:group) { create(:group) }
describe 'contribution analytics tab' do describe 'contribution analytics tab' do
it 'is not visible when there is no valid license and we dont show promotions' do it 'is not visible when there is no valid license and we dont show promotions' do
stub_licensed_features(contribution_analytics: false) stub_licensed_features(contribution_analytics: false)
...@@ -66,24 +68,31 @@ describe 'layouts/nav/sidebar/_group' do ...@@ -66,24 +68,31 @@ describe 'layouts/nav/sidebar/_group' do
end end
describe 'security dashboard tab' do describe 'security dashboard tab' do
it 'is visible when user has enough permission' do before do
allow(view).to receive(:can?) stub_licensed_features(security_dashboard: true)
.with(anything, :read_group_security_dashboard, anything) enable_namespace_license_check!
.and_return(true)
render create(:gitlab_subscription, hosted_plan: group.plan, namespace: group)
end
context 'when security dashboard feature is enabled' do
let(:group) { create(:group, plan: :gold_plan) }
it 'is visible' do
render
expect(rendered).to have_text 'Security Dashboard' expect(rendered).to have_link 'Security Dashboard'
end
end end
it 'is not visible when user does not have enough permission' do context 'when security dashboard feature is disabled' do
allow(view).to receive(:can?) let(:group) { create(:group, plan: :bronze_plan) }
.with(anything, :read_group_security_dashboard, anything)
.and_return(false)
render it 'is not visible' do
render
expect(rendered).not_to have_text 'Security Dashboard' expect(rendered).not_to have_link 'Security Dashboard'
end
end end
end end
end end
# frozen_string_literal: true
require 'spec_helper'
describe 'profiles/preferences/show' do
before do
assign(:user, user)
allow(controller).to receive(:current_user).and_return(user)
view.extend ::Groups::Security::DashboardHelper
end
let(:user) { build(:user) }
context 'security dashboard feature is available' do
before do
stub_licensed_features(security_dashboard: true)
end
it 'renders the group view choice preference' do
render
expect(rendered).to have_select('Group overview content')
end
end
context 'security dashboard feature is unavailable' do
it 'does not render the group view choice preference' do
render
expect(rendered).not_to have_select('Group overview content')
end
end
end
...@@ -3,6 +3,7 @@ ...@@ -3,6 +3,7 @@
module Gitlab module Gitlab
module View module View
module Presenter module Presenter
# TODO: find a way to delegate calls to `class` methods to the subject's class; see gitlab-ce/#57299
class Delegated < SimpleDelegator class Delegated < SimpleDelegator
include Gitlab::View::Presenter::Base include Gitlab::View::Presenter::Base
......
...@@ -2858,6 +2858,9 @@ msgstr "" ...@@ -2858,6 +2858,9 @@ msgstr ""
msgid "Contribution" msgid "Contribution"
msgstr "" msgstr ""
msgid "Contribution Analytics"
msgstr ""
msgid "Contribution Charts" msgid "Contribution Charts"
msgstr "" msgstr ""
......
...@@ -32,21 +32,46 @@ describe GroupsController do ...@@ -32,21 +32,46 @@ describe GroupsController do
end end
end end
shared_examples 'details view' do
it { is_expected.to render_template('groups/show') }
context 'as atom' do
let!(:event) { create(:event, project: project) }
let(:format) { :atom }
it { is_expected.to render_template('groups/show') }
it 'assigns events for all the projects in the group' do
subject
expect(assigns(:events)).to contain_exactly(event)
end
end
end
describe 'GET #show' do describe 'GET #show' do
before do before do
sign_in(user) sign_in(user)
project project
end end
context 'as atom' do let(:format) { :html }
it 'assigns events for all the projects in the group' do
create(:event, project: project)
get :show, params: { id: group.to_param }, format: :atom subject { get :show, params: { id: group.to_param }, format: format }
expect(assigns(:events)).not_to be_empty it_behaves_like 'details view'
end end
describe 'GET #details' do
before do
sign_in(user)
project
end end
let(:format) { :html }
subject { get :details, params: { id: group.to_param }, format: format }
it_behaves_like 'details view'
end end
describe 'GET edit' do describe 'GET edit' do
......
...@@ -17,6 +17,10 @@ describe "Groups", "routing" do ...@@ -17,6 +17,10 @@ describe "Groups", "routing" do
expect(get("/#{group_path}")).to route_to('groups#show', id: group_path) expect(get("/#{group_path}")).to route_to('groups#show', id: group_path)
end end
it "to #details" do
expect(get("/groups/#{group_path}/-/details")).to route_to('groups#details', id: group_path)
end
it "to #activity" do it "to #activity" do
expect(get("/groups/#{group_path}/-/activity")).to route_to('groups#activity', id: group_path) expect(get("/groups/#{group_path}/-/activity")).to route_to('groups#activity', id: group_path)
end end
......
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