diff options
Diffstat (limited to 'lib/sidebars')
74 files changed, 1606 insertions, 140 deletions
diff --git a/lib/sidebars/concerns/render_if_logged_in.rb b/lib/sidebars/concerns/render_if_logged_in.rb new file mode 100644 index 00000000000..221af7df23b --- /dev/null +++ b/lib/sidebars/concerns/render_if_logged_in.rb @@ -0,0 +1,11 @@ +# frozen_string_literal: true + +module Sidebars + module Concerns + module RenderIfLoggedIn + def render? + !!context.current_user + end + end + end +end diff --git a/lib/sidebars/concerns/super_sidebar_panel.rb b/lib/sidebars/concerns/super_sidebar_panel.rb new file mode 100644 index 00000000000..5f3607debbc --- /dev/null +++ b/lib/sidebars/concerns/super_sidebar_panel.rb @@ -0,0 +1,58 @@ +# frozen_string_literal: true + +module Sidebars + module Concerns + # Contains helper methods aid conversion of a "normal" panel + # into a Super Sidebar Panel + module SuperSidebarPanel + # Picks menus from a list and adds them to the current menu list + # if they should be picked into the super sidebar + def pick_from_old_menus(old_menus) + old_menus.select! do |menu| + next true unless menu.pick_into_super_sidebar? + + add_menu(menu) + false + end + end + + def transform_old_menus(current_menus, *old_menus) + old_menus.each do |menu| + next unless menu.render? + + menu.renderable_items.each { |item| add_menu_item_to_super_sidebar_parent(current_menus, item) } + + menu_item_args = menu.serialize_as_menu_item_args + + next if menu_item_args.nil? + + add_menu_item_to_super_sidebar_parent( + current_menus, ::Sidebars::MenuItem.new(**menu_item_args) + ) + end + end + + private + + # Finds a menu_items super sidebar parent and adds the item to that menu + # Handles: + # - menu_item.super_sidebar_before, adding before a certain item + # - parent == nil, or parent not being part of the panel: + # we assume that the menu item hasn't been categorized yet + # - parent == ::Sidebars::NilMenuItem, the item explicitly is supposed to be removed + def add_menu_item_to_super_sidebar_parent(menus, menu_item) + parent = menu_item.super_sidebar_parent || ::Sidebars::UncategorizedMenu + return if parent == ::Sidebars::NilMenuItem + + idx = index_of(menus, parent) || index_of(menus, ::Sidebars::UncategorizedMenu) + return unless idx + + if menu_item.super_sidebar_before + menus[idx].insert_item_before(menu_item.super_sidebar_before, menu_item) + else + menus[idx].add_item(menu_item) + end + end + end + end +end diff --git a/lib/sidebars/context.rb b/lib/sidebars/context.rb index d9ac2705aaf..b49776bed10 100644 --- a/lib/sidebars/context.rb +++ b/lib/sidebars/context.rb @@ -6,16 +6,19 @@ # values where the logic is in helpers. module Sidebars class Context - attr_reader :current_user, :container + attr_reader :current_user, :container, :route_is_active, :is_super_sidebar def initialize(current_user:, container:, **args) @current_user = current_user @container = container + @is_super_sidebar = false args.each do |key, value| singleton_class.public_send(:attr_reader, key) # rubocop:disable GitlabSecurity/PublicSend instance_variable_set("@#{key}", value) end + + @route_is_active ||= ->(_) { false } end end end diff --git a/lib/sidebars/explore/menus/groups_menu.rb b/lib/sidebars/explore/menus/groups_menu.rb new file mode 100644 index 00000000000..542da0ad7fd --- /dev/null +++ b/lib/sidebars/explore/menus/groups_menu.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Sidebars + module Explore + module Menus + class GroupsMenu < ::Sidebars::Menu + override :link + def link + explore_groups_path + end + + override :title + def title + _('Groups') + end + + override :sprite_icon + def sprite_icon + 'group' + end + + override :render? + def render? + true + end + + override :active_routes + def active_routes + { controller: ['explore/groups'] } + end + end + end + end +end diff --git a/lib/sidebars/explore/menus/projects_menu.rb b/lib/sidebars/explore/menus/projects_menu.rb new file mode 100644 index 00000000000..29c35d23b7b --- /dev/null +++ b/lib/sidebars/explore/menus/projects_menu.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Sidebars + module Explore + module Menus + class ProjectsMenu < ::Sidebars::Menu + override :link + def link + explore_projects_path + end + + override :title + def title + _('Projects') + end + + override :sprite_icon + def sprite_icon + 'project' + end + + override :render? + def render? + true + end + + override :active_routes + def active_routes + { page: [link, explore_root_path] } + end + end + end + end +end diff --git a/lib/sidebars/explore/menus/snippets_menu.rb b/lib/sidebars/explore/menus/snippets_menu.rb new file mode 100644 index 00000000000..67b852258a7 --- /dev/null +++ b/lib/sidebars/explore/menus/snippets_menu.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Sidebars + module Explore + module Menus + class SnippetsMenu < ::Sidebars::Menu + override :link + def link + explore_snippets_path + end + + override :title + def title + _('Snippets') + end + + override :sprite_icon + def sprite_icon + 'snippet' + end + + override :render? + def render? + true + end + + override :active_routes + def active_routes + { controller: ['explore/snippets'] } + end + end + end + end +end diff --git a/lib/sidebars/explore/menus/topics_menu.rb b/lib/sidebars/explore/menus/topics_menu.rb new file mode 100644 index 00000000000..5e0a7897873 --- /dev/null +++ b/lib/sidebars/explore/menus/topics_menu.rb @@ -0,0 +1,34 @@ +# frozen_string_literal: true + +module Sidebars + module Explore + module Menus + class TopicsMenu < ::Sidebars::Menu + override :link + def link + topics_explore_projects_path + end + + override :title + def title + _('Topics') + end + + override :sprite_icon + def sprite_icon + 'labels' + end + + override :render? + def render? + true + end + + override :active_routes + def active_routes + { page: link, path: 'projects#topic' } + end + end + end + end +end diff --git a/lib/sidebars/explore/panel.rb b/lib/sidebars/explore/panel.rb new file mode 100644 index 00000000000..9a585a99705 --- /dev/null +++ b/lib/sidebars/explore/panel.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Sidebars + module Explore + class Panel < ::Sidebars::Panel + override :configure_menus + def configure_menus + add_menus + end + + override :aria_label + def aria_label + _('Explore') + end + + override :render_raw_scope_menu_partial + def render_raw_scope_menu_partial + "shared/nav/explore_scope_header" + end + + override :super_sidebar_context_header + def super_sidebar_context_header + { + title: aria_label, + icon: 'compass' + } + end + + private + + def add_menus + add_menu(Sidebars::Explore::Menus::ProjectsMenu.new(context)) + add_menu(Sidebars::Explore::Menus::GroupsMenu.new(context)) + add_menu(Sidebars::Explore::Menus::TopicsMenu.new(context)) + add_menu(Sidebars::Explore::Menus::SnippetsMenu.new(context)) + end + end + end +end diff --git a/lib/sidebars/groups/menus/ci_cd_menu.rb b/lib/sidebars/groups/menus/ci_cd_menu.rb index 0c2995f95e6..f32bc49673f 100644 --- a/lib/sidebars/groups/menus/ci_cd_menu.rb +++ b/lib/sidebars/groups/menus/ci_cd_menu.rb @@ -21,6 +21,11 @@ module Sidebars 'rocket' end + override :pick_into_super_sidebar? + def pick_into_super_sidebar? + true + end + private def runners_menu_item diff --git a/lib/sidebars/groups/menus/group_information_menu.rb b/lib/sidebars/groups/menus/group_information_menu.rb index 3ce99e14a04..2364ad85cb5 100644 --- a/lib/sidebars/groups/menus/group_information_menu.rb +++ b/lib/sidebars/groups/menus/group_information_menu.rb @@ -28,6 +28,11 @@ module Sidebars { path: 'groups#subgroups' } end + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + nil + end + private def activity_menu_item @@ -38,6 +43,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Activity'), link: activity_group_path(context.group), + super_sidebar_parent: ::Sidebars::Groups::SuperSidebarMenus::PlanMenu, active_routes: { path: 'groups#activity' }, item_id: :activity ) @@ -51,6 +57,8 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Labels'), link: group_labels_path(context.group), + super_sidebar_parent: ::Sidebars::Groups::SuperSidebarMenus::PlanMenu, + super_sidebar_before: :activity, active_routes: { controller: :labels }, item_id: :labels ) @@ -64,6 +72,8 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Members'), link: group_group_members_path(context.group), + sprite_icon: context.is_super_sidebar ? 'users' : nil, + super_sidebar_parent: ::Sidebars::StaticMenu, active_routes: { path: 'group_members#index' }, item_id: :members ) diff --git a/lib/sidebars/groups/menus/invite_team_members_menu.rb b/lib/sidebars/groups/menus/invite_team_members_menu.rb deleted file mode 100644 index 0779b696061..00000000000 --- a/lib/sidebars/groups/menus/invite_team_members_menu.rb +++ /dev/null @@ -1,46 +0,0 @@ -# frozen_string_literal: true - -module Sidebars - module Groups - module Menus - class InviteTeamMembersMenu < ::Sidebars::Menu - override :title - def title - s_('InviteMember|Invite members') - end - - override :render? - def render? - can?(context.current_user, :admin_group_member, context.group) && all_valid_members.size <= 1 - end - - override :menu_partial - def menu_partial - 'groups/invite_members_side_nav_link' - end - - override :menu_partial_options - def menu_partial_options - { - group: context.group, - title: title, - sidebar_menu: self - } - end - - override :extra_nav_link_html_options - def extra_nav_link_html_options - { - 'data-test-id': 'side-nav-invite-members' - } - end - - private - - def all_valid_members - GroupMembersFinder.new(context.group, context.current_user).execute - end - end - end - end -end diff --git a/lib/sidebars/groups/menus/issues_menu.rb b/lib/sidebars/groups/menus/issues_menu.rb index 4044cb1c716..75bdb617b1a 100644 --- a/lib/sidebars/groups/menus/issues_menu.rb +++ b/lib/sidebars/groups/menus/issues_menu.rb @@ -49,12 +49,25 @@ module Sidebars } end + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + super.merge({ + active_routes: list_menu_item.active_routes, + sprite_icon: sprite_icon, + pill_count: pill_count, + has_pill: has_pill?, + super_sidebar_parent: ::Sidebars::StaticMenu, + item_id: :group_issue_list + }) + end + private def list_menu_item ::Sidebars::MenuItem.new( title: _('List'), link: issues_group_path(context.group), + super_sidebar_parent: ::Sidebars::NilMenuItem, active_routes: { path: 'groups#issues' }, container_html_options: { aria: { label: _('Issues') } }, item_id: :issue_list @@ -66,11 +79,16 @@ module Sidebars return ::Sidebars::NilMenuItem.new(item_id: :boards) end - title = context.group.multiple_issue_boards_available? ? s_('IssueBoards|Boards') : s_('IssueBoards|Board') + title = if context.is_super_sidebar + context.group.multiple_issue_boards_available? ? s_('Issue boards') : s_('Issue board') + else + context.group.multiple_issue_boards_available? ? s_('IssueBoards|Boards') : s_('IssueBoards|Board') + end ::Sidebars::MenuItem.new( title: title, link: group_boards_path(context.group), + super_sidebar_parent: ::Sidebars::Groups::SuperSidebarMenus::PlanMenu, active_routes: { path: %w[boards#index boards#show] }, item_id: :boards ) @@ -84,6 +102,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Milestones'), link: group_milestones_path(context.group), + super_sidebar_parent: ::Sidebars::Groups::SuperSidebarMenus::PlanMenu, active_routes: { path: 'milestones#index' }, item_id: :milestones ) diff --git a/lib/sidebars/groups/menus/kubernetes_menu.rb b/lib/sidebars/groups/menus/kubernetes_menu.rb index 0d845978a93..a7c14148230 100644 --- a/lib/sidebars/groups/menus/kubernetes_menu.rb +++ b/lib/sidebars/groups/menus/kubernetes_menu.rb @@ -38,6 +38,14 @@ module Sidebars def active_routes { controller: :clusters } end + + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + super.merge({ + super_sidebar_parent: ::Sidebars::Groups::SuperSidebarMenus::OperationsMenu, + item_id: :group_kubernetes_clusters + }) + end end end end diff --git a/lib/sidebars/groups/menus/merge_requests_menu.rb b/lib/sidebars/groups/menus/merge_requests_menu.rb index 050cba07641..5e25c33167c 100644 --- a/lib/sidebars/groups/menus/merge_requests_menu.rb +++ b/lib/sidebars/groups/menus/merge_requests_menu.rb @@ -52,6 +52,17 @@ module Sidebars def active_routes { path: 'groups#merge_requests' } end + + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + super.merge({ + sprite_icon: sprite_icon, + pill_count: pill_count, + has_pill: has_pill?, + super_sidebar_parent: ::Sidebars::StaticMenu, + item_id: :group_merge_request_list + }) + end end end end diff --git a/lib/sidebars/groups/menus/observability_menu.rb b/lib/sidebars/groups/menus/observability_menu.rb index d85efb1a002..570a59f7e55 100644 --- a/lib/sidebars/groups/menus/observability_menu.rb +++ b/lib/sidebars/groups/menus/observability_menu.rb @@ -6,8 +6,11 @@ module Sidebars class ObservabilityMenu < ::Sidebars::Menu override :configure_menu_items def configure_menu_items - add_item(explore_menu_item) - add_item(datasources_menu_item) + add_item(explore_menu_item) if Gitlab::Observability.allowed_for_action?(context.current_user, context.group, + :explore) + + add_item(datasources_menu_item) if Gitlab::Observability.allowed_for_action?(context.current_user, + context.group, :datasources) end override :title @@ -22,7 +25,12 @@ module Sidebars override :render? def render? - Gitlab::Observability.observability_enabled?(context.current_user, context.group) + Gitlab::Observability.allowed_for_action?(context.current_user, context.group, :explore) + end + + override :pick_into_super_sidebar? + def pick_into_super_sidebar? + true end private diff --git a/lib/sidebars/groups/menus/packages_registries_menu.rb b/lib/sidebars/groups/menus/packages_registries_menu.rb index e115ca669d4..73a67bf1142 100644 --- a/lib/sidebars/groups/menus/packages_registries_menu.rb +++ b/lib/sidebars/groups/menus/packages_registries_menu.rb @@ -23,6 +23,11 @@ module Sidebars 'package' end + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + nil + end + private def packages_registry_menu_item @@ -31,6 +36,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Package Registry'), link: group_packages_path(context.group), + super_sidebar_parent: ::Sidebars::Groups::SuperSidebarMenus::OperationsMenu, active_routes: { controller: 'groups/packages' }, item_id: :packages_registry ) @@ -44,6 +50,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Container Registry'), link: group_container_registries_path(context.group), + super_sidebar_parent: ::Sidebars::Groups::SuperSidebarMenus::OperationsMenu, active_routes: { controller: 'groups/registry/repositories' }, item_id: :container_registry ) @@ -59,6 +66,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Harbor Registry'), link: group_harbor_repositories_path(context.group), + super_sidebar_parent: ::Sidebars::Groups::SuperSidebarMenus::OperationsMenu, active_routes: { controller: 'groups/harbor/repositories' }, item_id: :harbor_registry ) @@ -74,6 +82,8 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Dependency Proxy'), link: group_dependency_proxy_path(context.group), + super_sidebar_parent: ::Sidebars::Groups::SuperSidebarMenus::OperationsMenu, + super_sidebar_before: :packages_registry, active_routes: { controller: 'groups/dependency_proxies' }, item_id: :dependency_proxy ) diff --git a/lib/sidebars/groups/menus/scope_menu.rb b/lib/sidebars/groups/menus/scope_menu.rb index 6ce43491343..5505f56a6d3 100644 --- a/lib/sidebars/groups/menus/scope_menu.rb +++ b/lib/sidebars/groups/menus/scope_menu.rb @@ -16,7 +16,7 @@ module Sidebars override :active_routes def active_routes - { path: %w[groups#show groups#details] } + { path: %w[groups#show groups#details groups#new projects#new] } end override :extra_nav_link_html_options @@ -32,6 +32,16 @@ module Sidebars def render? true end + + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + super.merge({ + title: _('Group overview'), + sprite_icon: 'group', + super_sidebar_parent: ::Sidebars::StaticMenu, + item_id: :group_overview + }) + end end end end diff --git a/lib/sidebars/groups/menus/settings_menu.rb b/lib/sidebars/groups/menus/settings_menu.rb index 5b81f22c796..76c5f9c16a5 100644 --- a/lib/sidebars/groups/menus/settings_menu.rb +++ b/lib/sidebars/groups/menus/settings_menu.rb @@ -47,6 +47,11 @@ module Sidebars } end + override :pick_into_super_sidebar? + def pick_into_super_sidebar? + true + end + private def general_menu_item diff --git a/lib/sidebars/groups/panel.rb b/lib/sidebars/groups/panel.rb index e8b815bdce7..77ca51ddf92 100644 --- a/lib/sidebars/groups/panel.rb +++ b/lib/sidebars/groups/panel.rb @@ -16,22 +16,12 @@ module Sidebars add_menu(Sidebars::Groups::Menus::PackagesRegistriesMenu.new(context)) add_menu(Sidebars::Groups::Menus::CustomerRelationsMenu.new(context)) add_menu(Sidebars::Groups::Menus::SettingsMenu.new(context)) - add_invite_members_menu end override :aria_label def aria_label context.group.subgroup? ? _('Subgroup navigation') : _('Group navigation') end - - private - - def add_invite_members_menu - experiment(:invite_members_in_side_nav, group: context.group) do |e| - e.control {} - e.candidate { add_menu(Sidebars::Groups::Menus::InviteTeamMembersMenu.new(context)) } - end - end end end end diff --git a/lib/sidebars/groups/super_sidebar_menus/operations_menu.rb b/lib/sidebars/groups/super_sidebar_menus/operations_menu.rb new file mode 100644 index 00000000000..195718e0681 --- /dev/null +++ b/lib/sidebars/groups/super_sidebar_menus/operations_menu.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Sidebars + module Groups + module SuperSidebarMenus + class OperationsMenu < ::Sidebars::Menu + override :title + def title + _('Operations') + end + + override :sprite_icon + def sprite_icon + 'deployments' + end + end + end + end +end diff --git a/lib/sidebars/groups/super_sidebar_menus/plan_menu.rb b/lib/sidebars/groups/super_sidebar_menus/plan_menu.rb new file mode 100644 index 00000000000..8a90974c0d4 --- /dev/null +++ b/lib/sidebars/groups/super_sidebar_menus/plan_menu.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Sidebars + module Groups + module SuperSidebarMenus + class PlanMenu < ::Sidebars::Menu + override :title + def title + _('Plan') + end + + override :sprite_icon + def sprite_icon + 'planning' + end + end + end + end +end diff --git a/lib/sidebars/groups/super_sidebar_panel.rb b/lib/sidebars/groups/super_sidebar_panel.rb new file mode 100644 index 00000000000..620f6e78eda --- /dev/null +++ b/lib/sidebars/groups/super_sidebar_panel.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Sidebars + module Groups + class SuperSidebarPanel < ::Sidebars::Groups::Panel + include ::Sidebars::Concerns::SuperSidebarPanel + extend ::Gitlab::Utils::Override + + override :configure_menus + def configure_menus + super + old_menus = @menus + @menus = [] + + add_menu(Sidebars::StaticMenu.new(context)) + add_menu(Sidebars::Groups::SuperSidebarMenus::PlanMenu.new(context)) + + pick_from_old_menus(old_menus) + + insert_menu_before( + Sidebars::Groups::Menus::ObservabilityMenu, + Sidebars::Groups::SuperSidebarMenus::OperationsMenu.new(context) + ) + + insert_menu_before( + Sidebars::Groups::Menus::SettingsMenu, + Sidebars::UncategorizedMenu.new(context) + ) + + transform_old_menus(@menus, @scope_menu, *old_menus) + end + + override :super_sidebar_context_header + def super_sidebar_context_header + { + title: context.group.name, + avatar: context.group.avatar_url, + id: context.group.id + } + end + end + end +end diff --git a/lib/sidebars/menu.rb b/lib/sidebars/menu.rb index dfd88c99a0c..03995362ff0 100644 --- a/lib/sidebars/menu.rb +++ b/lib/sidebars/menu.rb @@ -66,6 +66,40 @@ module Sidebars @renderable_items ||= @items.select(&:render?) end + # Returns a tree-like representation of itself and all + # renderable menu entries, with additional information + # on whether the item(s) have an active route + def serialize_for_super_sidebar + items = serialize_items_for_super_sidebar + is_active = @context.route_is_active.call(active_routes) || items.any? { |item| item[:is_active] } + + { + title: title, + icon: sprite_icon, + link: link, + is_active: is_active, + pill_count: has_pill? ? pill_count : nil, + items: items + } + end + + # Returns an array of renderable menu entries, + # with additional information on whether the item + # has an active route + def serialize_items_for_super_sidebar + # All renderable menu entries + renderable_items.map do |entry| + entry.serialize_for_super_sidebar.tap do |item| + active_routes = item.delete(:active_routes) + item[:is_active] = active_routes ? @context.route_is_active.call(active_routes) : false + end + end + end + + def pick_into_super_sidebar? + false + end + # Returns whether the menu has any renderable menu item def has_renderable_items? renderable_items.any? @@ -93,6 +127,17 @@ module Sidebars end end + # Sometimes we want to convert a top-level Menu (e.g. Wiki/Snippets) + # to a MenuItem. This serializer is used in order to enable that conversion + def serialize_as_menu_item_args + { + title: title, + link: link, + active_routes: active_routes, + container_html_options: container_html_options + } + end + private override :index_of diff --git a/lib/sidebars/menu_item.rb b/lib/sidebars/menu_item.rb index efdedf6c3bd..becff240034 100644 --- a/lib/sidebars/menu_item.rb +++ b/lib/sidebars/menu_item.rb @@ -4,11 +4,11 @@ module Sidebars class MenuItem include ::Sidebars::Concerns::LinkWithHtmlOptions - attr_reader :title, :link, :active_routes, :item_id, :container_html_options, :sprite_icon, :sprite_icon_html_options, :hint_html_options, :has_pill, :pill_count + attr_reader :title, :link, :active_routes, :item_id, :container_html_options, :sprite_icon, :sprite_icon_html_options, :hint_html_options, :has_pill, :pill_count, :super_sidebar_parent, :super_sidebar_before alias_method :has_pill?, :has_pill # rubocop: disable Metrics/ParameterLists - def initialize(title:, link:, active_routes:, item_id: nil, container_html_options: {}, sprite_icon: nil, sprite_icon_html_options: {}, hint_html_options: {}, has_pill: false, pill_count: nil) + def initialize(title:, link:, active_routes:, item_id: nil, container_html_options: {}, sprite_icon: nil, sprite_icon_html_options: {}, hint_html_options: {}, has_pill: false, pill_count: nil, super_sidebar_parent: nil, super_sidebar_before: nil) @title = title @link = link @active_routes = active_routes @@ -19,6 +19,8 @@ module Sidebars @hint_html_options = hint_html_options @has_pill = has_pill @pill_count = pill_count + @super_sidebar_before = super_sidebar_before + @super_sidebar_parent = super_sidebar_parent end # rubocop: enable Metrics/ParameterLists @@ -30,6 +32,25 @@ module Sidebars true end + def serialize_for_super_sidebar + { + title: title, + icon: sprite_icon, + link: link, + active_routes: active_routes, + pill_count: has_pill ? pill_count : nil + # Check whether support is needed for the following properties, + # in order to get feature parity with the HAML renderer + # https://gitlab.com/gitlab-org/gitlab/-/issues/391864 + # + # container_html_options + # hint_html_options + # nav_link_html_options + # + # item_id + } + end + def nav_link_html_options { data: { diff --git a/lib/sidebars/panel.rb b/lib/sidebars/panel.rb index 2a172cfffe3..4c0d4caf81e 100644 --- a/lib/sidebars/panel.rb +++ b/lib/sidebars/panel.rb @@ -4,7 +4,6 @@ module Sidebars class Panel extend ::Gitlab::Utils::Override include ::Sidebars::Concerns::PositionableList - include Gitlab::Experiment::Dsl attr_reader :context, :scope_menu, :hidden_menu @@ -61,6 +60,16 @@ module Sidebars @renderable_menus ||= @menus.select(&:render?) end + # Serializes every renderable menu and returns a flattened result + def super_sidebar_menu_items + @super_sidebar_menu_items ||= renderable_menus + .flat_map(&:serialize_for_super_sidebar) + end + + def super_sidebar_context_header + raise NotImplementedError + end + def container context.container end diff --git a/lib/sidebars/projects/menus/analytics_menu.rb b/lib/sidebars/projects/menus/analytics_menu.rb index 643b7ebcd5a..fae2efd91de 100644 --- a/lib/sidebars/projects/menus/analytics_menu.rb +++ b/lib/sidebars/projects/menus/analytics_menu.rb @@ -41,6 +41,11 @@ module Sidebars 'chart' end + override :pick_into_super_sidebar? + def pick_into_super_sidebar? + true + end + private def ci_cd_analytics_menu_item diff --git a/lib/sidebars/projects/menus/ci_cd_menu.rb b/lib/sidebars/projects/menus/ci_cd_menu.rb index 5df99bb9d84..3d11dba1089 100644 --- a/lib/sidebars/projects/menus/ci_cd_menu.rb +++ b/lib/sidebars/projects/menus/ci_cd_menu.rb @@ -39,6 +39,11 @@ module Sidebars 'rocket' end + override :pick_into_super_sidebar? + def pick_into_super_sidebar? + true + end + private def pipelines_menu_item diff --git a/lib/sidebars/projects/menus/deployments_menu.rb b/lib/sidebars/projects/menus/deployments_menu.rb index 4d4e65e9795..fa6c70cfd3d 100644 --- a/lib/sidebars/projects/menus/deployments_menu.rb +++ b/lib/sidebars/projects/menus/deployments_menu.rb @@ -34,6 +34,11 @@ module Sidebars 'deployments' end + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + nil + end + private def feature_flags_menu_item @@ -44,6 +49,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Feature Flags'), link: project_feature_flags_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :feature_flags }, container_html_options: { class: 'shortcuts-feature-flags' }, item_id: :feature_flags @@ -58,6 +64,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Environments'), link: project_environments_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :environments }, container_html_options: { class: 'shortcuts-environments' }, item_id: :environments @@ -73,6 +80,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Releases'), link: project_releases_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, item_id: :releases, active_routes: { controller: :releases }, container_html_options: { class: 'shortcuts-deployments-releases' } @@ -87,6 +95,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Pages'), link: project_pages_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { path: 'pages#show' }, item_id: :pages ) diff --git a/lib/sidebars/projects/menus/infrastructure_menu.rb b/lib/sidebars/projects/menus/infrastructure_menu.rb index 04c9ab77729..a7cd920a74c 100644 --- a/lib/sidebars/projects/menus/infrastructure_menu.rb +++ b/lib/sidebars/projects/menus/infrastructure_menu.rb @@ -11,6 +11,7 @@ module Sidebars add_item(kubernetes_menu_item) add_item(terraform_menu_item) add_item(google_cloud_menu_item) + add_item(aws_menu_item) true end @@ -32,6 +33,11 @@ module Sidebars 'cloud-gear' end + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + nil + end + private def feature_enabled? @@ -46,6 +52,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Kubernetes clusters'), link: project_clusters_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: [:cluster_agents, :clusters] }, container_html_options: { class: 'shortcuts-kubernetes' }, hint_html_options: kubernetes_hint_html_options, @@ -74,11 +81,30 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Terraform'), link: project_terraform_index_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :terraform }, item_id: :terraform ) end + def aws_menu_item + enabled_for_user = Feature.enabled?(:cloudseed_aws, context.current_user) + enabled_for_group = Feature.enabled?(:cloudseed_aws, context.project.group) + enabled_for_project = Feature.enabled?(:cloudseed_aws, context.project) + feature_is_enabled = enabled_for_user || enabled_for_group || enabled_for_project + user_has_permissions = can?(context.current_user, :admin_project_aws, context.project) + + return ::Sidebars::NilMenuItem.new(item_id: :cloudseed_aws) unless feature_is_enabled && user_has_permissions + + ::Sidebars::MenuItem.new( + title: _('AWS'), + link: '#', + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, + item_id: :aws, + active_routes: { controller: '' } + ) + end + def google_cloud_menu_item enabled_for_user = Feature.enabled?(:incubation_5mp_google_cloud, context.current_user) enabled_for_group = Feature.enabled?(:incubation_5mp_google_cloud, context.project.group) @@ -86,13 +112,16 @@ module Sidebars feature_is_enabled = enabled_for_user || enabled_for_group || enabled_for_project user_has_permissions = can?(context.current_user, :admin_project_google_cloud, context.project) - unless feature_is_enabled && user_has_permissions + google_oauth2_configured = google_oauth2_configured? + + unless feature_is_enabled && user_has_permissions && google_oauth2_configured return ::Sidebars::NilMenuItem.new(item_id: :incubation_5mp_google_cloud) end ::Sidebars::MenuItem.new( title: _('Google Cloud'), link: project_google_cloud_configuration_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: %w[ projects/google_cloud/configuration projects/google_cloud/service_accounts @@ -103,6 +132,11 @@ module Sidebars item_id: :google_cloud ) end + + def google_oauth2_configured? + config = Gitlab::Auth::OAuth::Provider.config_for('google_oauth2') + config&.present? && config.app_id.present? && config.app_secret.present? + end end end end diff --git a/lib/sidebars/projects/menus/invite_team_members_menu.rb b/lib/sidebars/projects/menus/invite_team_members_menu.rb deleted file mode 100644 index 0db49f1e12a..00000000000 --- a/lib/sidebars/projects/menus/invite_team_members_menu.rb +++ /dev/null @@ -1,47 +0,0 @@ -# frozen_string_literal: true - -module Sidebars - module Projects - module Menus - class InviteTeamMembersMenu < ::Sidebars::Menu - override :title - def title - s_('InviteMember|Invite members') - end - - override :render? - def render? - can?(context.current_user, :admin_project_member, context.project) && all_valid_members.size <= 1 - end - - override :menu_partial - def menu_partial - 'projects/invite_members_side_nav_link' - end - - override :menu_partial_options - def menu_partial_options - { - project: context.project, - title: title, - sidebar_menu: self - } - end - - override :extra_nav_link_html_options - def extra_nav_link_html_options - { - 'data-test-id': 'side-nav-invite-members' - } - end - - private - - def all_valid_members - MembersFinder.new(context.project, context.current_user) - .execute(include_relations: [:inherited, :direct, :invited_groups]) - end - end - end - end -end diff --git a/lib/sidebars/projects/menus/issues_menu.rb b/lib/sidebars/projects/menus/issues_menu.rb index 51eea3d850d..6904dc129b7 100644 --- a/lib/sidebars/projects/menus/issues_menu.rb +++ b/lib/sidebars/projects/menus/issues_menu.rb @@ -68,6 +68,17 @@ module Sidebars } end + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + super.merge({ + sprite_icon: sprite_icon, + pill_count: pill_count, + has_pill: has_pill?, + super_sidebar_parent: ::Sidebars::StaticMenu, + item_id: :project_issue_list + }) + end + private def show_issues_menu_items? @@ -78,6 +89,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('List'), link: project_issues_path(context.project), + super_sidebar_parent: ::Sidebars::NilMenuItem, active_routes: { path: 'projects/issues#index' }, container_html_options: { aria: { label: _('Issues') } }, item_id: :issue_list @@ -90,6 +102,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: title, link: project_boards_path(context.project), + super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::PlanMenu, active_routes: { controller: :boards }, item_id: :boards ) @@ -99,6 +112,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Service Desk'), link: service_desk_project_issues_path(context.project), + super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::PlanMenu, active_routes: { path: 'issues#service_desk' }, item_id: :service_desk ) @@ -108,6 +122,8 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Milestones'), link: project_milestones_path(context.project), + super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::PlanMenu, + super_sidebar_before: :service_desk, active_routes: { controller: :milestones }, item_id: :milestones ) diff --git a/lib/sidebars/projects/menus/merge_requests_menu.rb b/lib/sidebars/projects/menus/merge_requests_menu.rb index 3e543872d36..cc7fda0c920 100644 --- a/lib/sidebars/projects/menus/merge_requests_menu.rb +++ b/lib/sidebars/projects/menus/merge_requests_menu.rb @@ -64,6 +64,17 @@ module Sidebars { controller: ['projects/merge_requests', :milestones] } end end + + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + super.merge({ + sprite_icon: sprite_icon, + pill_count: pill_count, + has_pill: has_pill?, + super_sidebar_parent: ::Sidebars::StaticMenu, + item_id: :project_merge_request_list + }) + end end end end diff --git a/lib/sidebars/projects/menus/monitor_menu.rb b/lib/sidebars/projects/menus/monitor_menu.rb index 7d1fa8b8fa7..333112e13b6 100644 --- a/lib/sidebars/projects/menus/monitor_menu.rb +++ b/lib/sidebars/projects/menus/monitor_menu.rb @@ -12,7 +12,6 @@ module Sidebars add_item(error_tracking_menu_item) add_item(alert_management_menu_item) add_item(incidents_menu_item) - add_item(airflow_dashboard_menu_item) true end @@ -39,6 +38,11 @@ module Sidebars { controller: [:user, :gcp] } end + override :pick_into_super_sidebar? + def pick_into_super_sidebar? + true + end + private def feature_enabled? @@ -97,20 +101,6 @@ module Sidebars item_id: :incidents ) end - - def airflow_dashboard_menu_item - unless can?(context.current_user, :read_airflow_dags, context.project) && - Feature.enabled?(:airflow_dags, context.project) - return ::Sidebars::NilMenuItem.new(item_id: :airflow) - end - - ::Sidebars::MenuItem.new( - title: _('Airflow'), - link: project_airflow_dags_path(context.project), - active_routes: { path: 'airflow/dags#show' }, - item_id: :airflow_dags - ) - end end end end diff --git a/lib/sidebars/projects/menus/packages_registries_menu.rb b/lib/sidebars/projects/menus/packages_registries_menu.rb index fc7c564574a..d5b590a03aa 100644 --- a/lib/sidebars/projects/menus/packages_registries_menu.rb +++ b/lib/sidebars/projects/menus/packages_registries_menu.rb @@ -23,6 +23,11 @@ module Sidebars 'package' end + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + nil + end + private def packages_registry_menu_item @@ -33,6 +38,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Package Registry'), link: project_packages_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :packages }, item_id: :packages_registry, container_html_options: { class: 'shortcuts-container-registry' } @@ -47,6 +53,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Container Registry'), link: project_container_registry_index_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: 'projects/registry/repositories' }, item_id: :container_registry ) @@ -60,6 +67,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Infrastructure Registry'), link: project_infrastructure_registry_index_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :infrastructure_registry }, item_id: :infrastructure_registry ) @@ -75,6 +83,7 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Harbor Registry'), link: project_harbor_repositories_path(context.project), + super_sidebar_parent: Sidebars::Projects::SuperSidebarMenus::OperationsMenu, active_routes: { controller: :harbor_registry }, item_id: :harbor_registry ) diff --git a/lib/sidebars/projects/menus/project_information_menu.rb b/lib/sidebars/projects/menus/project_information_menu.rb index 44b94ee3522..020de2ff65f 100644 --- a/lib/sidebars/projects/menus/project_information_menu.rb +++ b/lib/sidebars/projects/menus/project_information_menu.rb @@ -33,12 +33,18 @@ module Sidebars 'project' end + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + nil + end + private def activity_menu_item ::Sidebars::MenuItem.new( title: _('Activity'), link: activity_project_path(context.project), + super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::PlanMenu, active_routes: { path: 'projects#activity' }, item_id: :activity, container_html_options: { class: 'shortcuts-project-activity' } @@ -53,6 +59,8 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Labels'), link: project_labels_path(context.project), + super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::PlanMenu, + super_sidebar_before: :activity, active_routes: { controller: :labels }, item_id: :labels ) @@ -66,6 +74,8 @@ module Sidebars ::Sidebars::MenuItem.new( title: _('Members'), link: project_project_members_path(context.project), + super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::PlanMenu, + super_sidebar_before: :labels, active_routes: { controller: :project_members }, item_id: :members, container_html_options: { diff --git a/lib/sidebars/projects/menus/repository_menu.rb b/lib/sidebars/projects/menus/repository_menu.rb index ec91ae741fe..158a29f0b31 100644 --- a/lib/sidebars/projects/menus/repository_menu.rb +++ b/lib/sidebars/projects/menus/repository_menu.rb @@ -44,6 +44,11 @@ module Sidebars 'doc-text' end + override :pick_into_super_sidebar? + def pick_into_super_sidebar? + true + end + private def files_menu_item @@ -92,7 +97,7 @@ module Sidebars link = project_graph_path(context.project, context.current_ref, ref_type: ref_type_from_context(context)) ::Sidebars::MenuItem.new( - title: _('Contributors'), + title: _('Contributor statistics'), link: link, active_routes: { path: 'graphs#show' }, item_id: :contributors @@ -112,7 +117,7 @@ module Sidebars def compare_menu_item ::Sidebars::MenuItem.new( - title: _('Compare'), + title: _('Compare revisions'), link: project_compare_index_path(context.project, from: context.project.repository.root_ref, to: context.current_ref), active_routes: { controller: :compare }, item_id: :compare diff --git a/lib/sidebars/projects/menus/scope_menu.rb b/lib/sidebars/projects/menus/scope_menu.rb index 35502c7ea09..7e2286633e5 100644 --- a/lib/sidebars/projects/menus/scope_menu.rb +++ b/lib/sidebars/projects/menus/scope_menu.rb @@ -39,6 +39,16 @@ module Sidebars def render? true end + + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + super.merge({ + title: _('Project overview'), + sprite_icon: 'project', + super_sidebar_parent: ::Sidebars::StaticMenu, + item_id: :project_overview + }) + end end end end diff --git a/lib/sidebars/projects/menus/security_compliance_menu.rb b/lib/sidebars/projects/menus/security_compliance_menu.rb index 9367514cdca..eb713244a7c 100644 --- a/lib/sidebars/projects/menus/security_compliance_menu.rb +++ b/lib/sidebars/projects/menus/security_compliance_menu.rb @@ -17,7 +17,7 @@ module Sidebars override :title def title - _('Security & Compliance') + _('Security and Compliance') end override :sprite_icon @@ -25,6 +25,11 @@ module Sidebars 'shield' end + override :pick_into_super_sidebar? + def pick_into_super_sidebar? + true + end + private def configuration_menu_item @@ -33,7 +38,7 @@ module Sidebars end ::Sidebars::MenuItem.new( - title: _('Configuration'), + title: _('Security configuration'), link: project_security_configuration_path(context.project), active_routes: { path: configuration_menu_item_paths }, item_id: :configuration diff --git a/lib/sidebars/projects/menus/settings_menu.rb b/lib/sidebars/projects/menus/settings_menu.rb index eea32d8b626..8b6d85e718d 100644 --- a/lib/sidebars/projects/menus/settings_menu.rb +++ b/lib/sidebars/projects/menus/settings_menu.rb @@ -44,6 +44,11 @@ module Sidebars 'settings' end + override :pick_into_super_sidebar? + def pick_into_super_sidebar? + true + end + private def general_menu_item diff --git a/lib/sidebars/projects/menus/snippets_menu.rb b/lib/sidebars/projects/menus/snippets_menu.rb index 060341b3c51..535f12963b1 100644 --- a/lib/sidebars/projects/menus/snippets_menu.rb +++ b/lib/sidebars/projects/menus/snippets_menu.rb @@ -35,6 +35,15 @@ module Sidebars def active_routes { controller: :snippets } end + + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + super.deep_merge({ + super_sidebar_parent: ::Sidebars::Projects::Menus::RepositoryMenu, + super_sidebar_before: :contributors, + item_id: :project_snippets + }) + end end end end diff --git a/lib/sidebars/projects/menus/wiki_menu.rb b/lib/sidebars/projects/menus/wiki_menu.rb index 3980b193fd1..d800dae8be3 100644 --- a/lib/sidebars/projects/menus/wiki_menu.rb +++ b/lib/sidebars/projects/menus/wiki_menu.rb @@ -35,6 +35,14 @@ module Sidebars def active_routes { controller: :wikis } end + + override :serialize_as_menu_item_args + def serialize_as_menu_item_args + super.merge({ + super_sidebar_parent: ::Sidebars::Projects::SuperSidebarMenus::PlanMenu, + item_id: :project_wiki + }) + end end end end diff --git a/lib/sidebars/projects/panel.rb b/lib/sidebars/projects/panel.rb index 9d0f5eb87bd..5d8bc18ac88 100644 --- a/lib/sidebars/projects/panel.rb +++ b/lib/sidebars/projects/panel.rb @@ -34,14 +34,6 @@ module Sidebars add_wiki_menus add_menu(Sidebars::Projects::Menus::SnippetsMenu.new(context)) add_menu(Sidebars::Projects::Menus::SettingsMenu.new(context)) - add_invite_members_menu - end - - def add_invite_members_menu - experiment(:invite_members_in_side_nav, group: context.project.group) do |e| - e.control {} - e.candidate { add_menu(Sidebars::Projects::Menus::InviteTeamMembersMenu.new(context)) } - end end def add_wiki_menus diff --git a/lib/sidebars/projects/super_sidebar_menus/operations_menu.rb b/lib/sidebars/projects/super_sidebar_menus/operations_menu.rb new file mode 100644 index 00000000000..5490aac5a65 --- /dev/null +++ b/lib/sidebars/projects/super_sidebar_menus/operations_menu.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Sidebars + module Projects + module SuperSidebarMenus + class OperationsMenu < ::Sidebars::Menu + override :title + def title + _('Operations') + end + + override :sprite_icon + def sprite_icon + 'deployments' + end + end + end + end +end diff --git a/lib/sidebars/projects/super_sidebar_menus/plan_menu.rb b/lib/sidebars/projects/super_sidebar_menus/plan_menu.rb new file mode 100644 index 00000000000..ae9b2d826b7 --- /dev/null +++ b/lib/sidebars/projects/super_sidebar_menus/plan_menu.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Sidebars + module Projects + module SuperSidebarMenus + class PlanMenu < ::Sidebars::Menu + override :title + def title + _('Plan') + end + + override :sprite_icon + def sprite_icon + 'planning' + end + end + end + end +end diff --git a/lib/sidebars/projects/super_sidebar_panel.rb b/lib/sidebars/projects/super_sidebar_panel.rb new file mode 100644 index 00000000000..f76f28eb642 --- /dev/null +++ b/lib/sidebars/projects/super_sidebar_panel.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module Sidebars + module Projects + class SuperSidebarPanel < ::Sidebars::Projects::Panel + include ::Sidebars::Concerns::SuperSidebarPanel + extend ::Gitlab::Utils::Override + + override :configure_menus + def configure_menus + super + old_menus = @menus + @menus = [] + + add_menu(Sidebars::StaticMenu.new(context)) + add_menu(Sidebars::Projects::SuperSidebarMenus::PlanMenu.new(context)) + + pick_from_old_menus(old_menus) + + insert_menu_before( + Sidebars::Projects::Menus::MonitorMenu, + Sidebars::Projects::SuperSidebarMenus::OperationsMenu.new(context) + ) + + insert_menu_before( + Sidebars::Projects::Menus::SettingsMenu, + Sidebars::UncategorizedMenu.new(context) + ) + + transform_old_menus(@menus, @scope_menu, *old_menus) + end + + override :super_sidebar_context_header + def super_sidebar_context_header + { + title: context.project.name, + avatar: context.project.avatar_url, + id: context.project.id + } + end + end + end +end diff --git a/lib/sidebars/static_menu.rb b/lib/sidebars/static_menu.rb new file mode 100644 index 00000000000..b7ba69b1717 --- /dev/null +++ b/lib/sidebars/static_menu.rb @@ -0,0 +1,13 @@ +# frozen_string_literal: true + +module Sidebars + # This is a special menu which does not serialize as + # a section and instead hoists all of menu items + # to be top-level items + class StaticMenu < ::Sidebars::Menu + override :serialize_for_super_sidebar + def serialize_for_super_sidebar + serialize_items_for_super_sidebar + end + end +end diff --git a/lib/sidebars/uncategorized_menu.rb b/lib/sidebars/uncategorized_menu.rb new file mode 100644 index 00000000000..dc9ed8308fa --- /dev/null +++ b/lib/sidebars/uncategorized_menu.rb @@ -0,0 +1,19 @@ +# frozen_string_literal: true + +module Sidebars + # This Menu is a temporary help while we implement the new menu + # categories for everything. Once every Menu Item is categorized, + # we can remove this. This should be done before the Super Sidebar + # moves out of Alpha. + class UncategorizedMenu < ::Sidebars::Menu + override :title + def title + _('Uncategorized') + end + + override :sprite_icon + def sprite_icon + 'question' + end + end +end diff --git a/lib/sidebars/user_profile/base_menu.rb b/lib/sidebars/user_profile/base_menu.rb new file mode 100644 index 00000000000..5594d7c3c65 --- /dev/null +++ b/lib/sidebars/user_profile/base_menu.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +module Sidebars + module UserProfile + class BaseMenu < ::Sidebars::Menu + override :render? + def render? + can?(context.current_user, :read_user_profile, context.container) + end + end + end +end diff --git a/lib/sidebars/user_profile/menus/activity_menu.rb b/lib/sidebars/user_profile/menus/activity_menu.rb new file mode 100644 index 00000000000..95e99a3dcbc --- /dev/null +++ b/lib/sidebars/user_profile/menus/activity_menu.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Sidebars + module UserProfile + module Menus + class ActivityMenu < ::Sidebars::UserProfile::BaseMenu + override :link + def link + user_activity_path(context.container) + end + + override :title + def title + s_('UserProfile|Activity') + end + + override :active_routes + def active_routes + { path: 'users#activity' } + end + end + end + end +end diff --git a/lib/sidebars/user_profile/menus/contributed_projects_menu.rb b/lib/sidebars/user_profile/menus/contributed_projects_menu.rb new file mode 100644 index 00000000000..0644982a143 --- /dev/null +++ b/lib/sidebars/user_profile/menus/contributed_projects_menu.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Sidebars + module UserProfile + module Menus + class ContributedProjectsMenu < ::Sidebars::UserProfile::BaseMenu + override :link + def link + user_contributed_projects_path(context.container) + end + + override :title + def title + s_('UserProfile|Contributed projects') + end + + override :active_routes + def active_routes + { path: 'users#contributed' } + end + end + end + end +end diff --git a/lib/sidebars/user_profile/menus/followers_menu.rb b/lib/sidebars/user_profile/menus/followers_menu.rb new file mode 100644 index 00000000000..53411c21a86 --- /dev/null +++ b/lib/sidebars/user_profile/menus/followers_menu.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Sidebars + module UserProfile + module Menus + class FollowersMenu < ::Sidebars::UserProfile::BaseMenu + include Gitlab::Utils::StrongMemoize + + override :link + def link + user_followers_path(context.container) + end + + override :title + def title + s_('UserProfile|Followers') + end + + override :active_routes + def active_routes + { path: 'users#followers' } + end + + override :has_pill? + def has_pill? + context.container.followers.any? + end + strong_memoize_attr :has_pill? + + override :pill_count + def pill_count + context.container.followers.count + end + strong_memoize_attr :pill_count + end + end + end +end diff --git a/lib/sidebars/user_profile/menus/following_menu.rb b/lib/sidebars/user_profile/menus/following_menu.rb new file mode 100644 index 00000000000..5da76a0100a --- /dev/null +++ b/lib/sidebars/user_profile/menus/following_menu.rb @@ -0,0 +1,38 @@ +# frozen_string_literal: true + +module Sidebars + module UserProfile + module Menus + class FollowingMenu < ::Sidebars::UserProfile::BaseMenu + include Gitlab::Utils::StrongMemoize + + override :link + def link + user_following_path(context.container) + end + + override :title + def title + s_('UserProfile|Following') + end + + override :active_routes + def active_routes + { path: 'users#following' } + end + + override :has_pill? + def has_pill? + context.container.followees.any? + end + strong_memoize_attr :has_pill? + + override :pill_count + def pill_count + context.container.followees.count + end + strong_memoize_attr :pill_count + end + end + end +end diff --git a/lib/sidebars/user_profile/menus/groups_menu.rb b/lib/sidebars/user_profile/menus/groups_menu.rb new file mode 100644 index 00000000000..38c2c3f042d --- /dev/null +++ b/lib/sidebars/user_profile/menus/groups_menu.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Sidebars + module UserProfile + module Menus + class GroupsMenu < ::Sidebars::UserProfile::BaseMenu + override :link + def link + user_groups_path(context.container) + end + + override :title + def title + s_('UserProfile|Groups') + end + + override :active_routes + def active_routes + { path: 'users#groups' } + end + end + end + end +end diff --git a/lib/sidebars/user_profile/menus/overview_menu.rb b/lib/sidebars/user_profile/menus/overview_menu.rb new file mode 100644 index 00000000000..ebf184e7e4e --- /dev/null +++ b/lib/sidebars/user_profile/menus/overview_menu.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Sidebars + module UserProfile + module Menus + class OverviewMenu < ::Sidebars::UserProfile::BaseMenu + override :link + def link + user_path(context.container) + end + + override :title + def title + s_('UserProfile|Overview') + end + + override :active_routes + def active_routes + { path: 'users#show' } + end + end + end + end +end diff --git a/lib/sidebars/user_profile/menus/personal_projects_menu.rb b/lib/sidebars/user_profile/menus/personal_projects_menu.rb new file mode 100644 index 00000000000..14fcddf2cc2 --- /dev/null +++ b/lib/sidebars/user_profile/menus/personal_projects_menu.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Sidebars + module UserProfile + module Menus + class PersonalProjectsMenu < ::Sidebars::UserProfile::BaseMenu + override :link + def link + user_projects_path(context.container) + end + + override :title + def title + s_('UserProfile|Personal projects') + end + + override :active_routes + def active_routes + { path: 'users#projects' } + end + end + end + end +end diff --git a/lib/sidebars/user_profile/menus/snippets_menu.rb b/lib/sidebars/user_profile/menus/snippets_menu.rb new file mode 100644 index 00000000000..a8e5077098c --- /dev/null +++ b/lib/sidebars/user_profile/menus/snippets_menu.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Sidebars + module UserProfile + module Menus + class SnippetsMenu < ::Sidebars::UserProfile::BaseMenu + override :link + def link + user_snippets_path(context.container) + end + + override :title + def title + s_('UserProfile|Snippets') + end + + override :active_routes + def active_routes + { path: 'users#snippets' } + end + end + end + end +end diff --git a/lib/sidebars/user_profile/menus/starred_projects_menu.rb b/lib/sidebars/user_profile/menus/starred_projects_menu.rb new file mode 100644 index 00000000000..762b451f4a1 --- /dev/null +++ b/lib/sidebars/user_profile/menus/starred_projects_menu.rb @@ -0,0 +1,24 @@ +# frozen_string_literal: true + +module Sidebars + module UserProfile + module Menus + class StarredProjectsMenu < ::Sidebars::UserProfile::BaseMenu + override :link + def link + user_starred_projects_path(context.container) + end + + override :title + def title + s_('UserProfile|Starred projects') + end + + override :active_routes + def active_routes + { path: 'users#starred' } + end + end + end + end +end diff --git a/lib/sidebars/user_profile/panel.rb b/lib/sidebars/user_profile/panel.rb new file mode 100644 index 00000000000..9a595fdf64c --- /dev/null +++ b/lib/sidebars/user_profile/panel.rb @@ -0,0 +1,52 @@ +# frozen_string_literal: true + +module Sidebars + module UserProfile + class Panel < ::Sidebars::Panel + include UsersHelper + + override :configure_menus + def configure_menus + add_menus + end + + override :aria_label + def aria_label + s_('UserProfile|User profile navigation') + end + + override :super_sidebar_context_header + def super_sidebar_context_header + @super_sidebar_context_header ||= { + title: user_name, + avatar: context.container.avatar_url, + avatar_shape: 'circle' + } + end + + private + + def user + context.container + end + + def user_name + return user_display_name(user) if user.blocked? || !user.confirmed? + + user.name + end + + def add_menus + add_menu(Sidebars::UserProfile::Menus::OverviewMenu.new(context)) + add_menu(Sidebars::UserProfile::Menus::ActivityMenu.new(context)) + add_menu(Sidebars::UserProfile::Menus::GroupsMenu.new(context)) + add_menu(Sidebars::UserProfile::Menus::ContributedProjectsMenu.new(context)) + add_menu(Sidebars::UserProfile::Menus::PersonalProjectsMenu.new(context)) + add_menu(Sidebars::UserProfile::Menus::StarredProjectsMenu.new(context)) + add_menu(Sidebars::UserProfile::Menus::SnippetsMenu.new(context)) + add_menu(Sidebars::UserProfile::Menus::FollowersMenu.new(context)) + add_menu(Sidebars::UserProfile::Menus::FollowingMenu.new(context)) + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/access_tokens_menu.rb b/lib/sidebars/user_settings/menus/access_tokens_menu.rb new file mode 100644 index 00000000000..f52be22e044 --- /dev/null +++ b/lib/sidebars/user_settings/menus/access_tokens_menu.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class AccessTokensMenu < ::Sidebars::Menu + override :link + def link + profile_personal_access_tokens_path + end + + override :title + def title + _('Access Tokens') + end + + override :sprite_icon + def sprite_icon + 'token' + end + + override :render? + def render? + !!context.current_user && !Gitlab::CurrentSettings.personal_access_tokens_disabled? + end + + override :active_routes + def active_routes + { controller: :personal_access_tokens } + end + + override :extra_container_html_options + def extra_container_html_options + { 'data-qa-selector': 'access_token_link' } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/account_menu.rb b/lib/sidebars/user_settings/menus/account_menu.rb new file mode 100644 index 00000000000..a26dee83da3 --- /dev/null +++ b/lib/sidebars/user_settings/menus/account_menu.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class AccountMenu < ::Sidebars::Menu + include ::Sidebars::Concerns::RenderIfLoggedIn + + override :link + def link + profile_account_path + end + + override :title + def title + _('Account') + end + + override :sprite_icon + def sprite_icon + 'account' + end + + override :active_routes + def active_routes + { controller: [:accounts, :two_factor_auths] } + end + + override :extra_container_html_options + def extra_container_html_options + { 'data-qa-selector': 'profile_account_link' } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/active_sessions_menu.rb b/lib/sidebars/user_settings/menus/active_sessions_menu.rb new file mode 100644 index 00000000000..f806c04e77c --- /dev/null +++ b/lib/sidebars/user_settings/menus/active_sessions_menu.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class ActiveSessionsMenu < ::Sidebars::Menu + include ::Sidebars::Concerns::RenderIfLoggedIn + + override :link + def link + profile_active_sessions_path + end + + override :title + def title + _('Active Sessions') + end + + override :sprite_icon + def sprite_icon + 'monitor-lines' + end + + override :active_routes + def active_routes + { controller: :active_sessions } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/applications_menu.rb b/lib/sidebars/user_settings/menus/applications_menu.rb new file mode 100644 index 00000000000..c71f9a9660b --- /dev/null +++ b/lib/sidebars/user_settings/menus/applications_menu.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class ApplicationsMenu < ::Sidebars::Menu + include ::Sidebars::Concerns::RenderIfLoggedIn + + override :link + def link + applications_profile_path + end + + override :title + def title + _('Applications') + end + + override :sprite_icon + def sprite_icon + 'applications' + end + + override :active_routes + def active_routes + { controller: 'oauth/applications' } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/authentication_log_menu.rb b/lib/sidebars/user_settings/menus/authentication_log_menu.rb new file mode 100644 index 00000000000..c5a27acf1fd --- /dev/null +++ b/lib/sidebars/user_settings/menus/authentication_log_menu.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class AuthenticationLogMenu < ::Sidebars::Menu + include ::Sidebars::Concerns::RenderIfLoggedIn + + override :link + def link + audit_log_profile_path + end + + override :title + def title + _('Authentication Log') + end + + override :sprite_icon + def sprite_icon + 'log' + end + + override :active_routes + def active_routes + { path: 'profiles#audit_log' } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/chat_menu.rb b/lib/sidebars/user_settings/menus/chat_menu.rb new file mode 100644 index 00000000000..54795c29b3f --- /dev/null +++ b/lib/sidebars/user_settings/menus/chat_menu.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class ChatMenu < ::Sidebars::Menu + include ::Sidebars::Concerns::RenderIfLoggedIn + + override :link + def link + profile_chat_names_path + end + + override :title + def title + _('Chat') + end + + override :sprite_icon + def sprite_icon + 'comment' + end + + override :active_routes + def active_routes + { controller: :chat_names } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/emails_menu.rb b/lib/sidebars/user_settings/menus/emails_menu.rb new file mode 100644 index 00000000000..3b6b4ae1daf --- /dev/null +++ b/lib/sidebars/user_settings/menus/emails_menu.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class EmailsMenu < ::Sidebars::Menu + include ::Sidebars::Concerns::RenderIfLoggedIn + + override :link + def link + profile_emails_path + end + + override :title + def title + _('Emails') + end + + override :sprite_icon + def sprite_icon + 'mail' + end + + override :active_routes + def active_routes + { controller: :emails } + end + + override :extra_container_html_options + def extra_container_html_options + { 'data-qa-selector': 'profile_emails_link' } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/gpg_keys_menu.rb b/lib/sidebars/user_settings/menus/gpg_keys_menu.rb new file mode 100644 index 00000000000..89e447aa277 --- /dev/null +++ b/lib/sidebars/user_settings/menus/gpg_keys_menu.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class GpgKeysMenu < ::Sidebars::Menu + include ::Sidebars::Concerns::RenderIfLoggedIn + + override :link + def link + profile_gpg_keys_path + end + + override :title + def title + _('GPG Keys') + end + + override :sprite_icon + def sprite_icon + 'key' + end + + override :active_routes + def active_routes + { controller: :gpg_keys } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/notifications_menu.rb b/lib/sidebars/user_settings/menus/notifications_menu.rb new file mode 100644 index 00000000000..f7ea0de0cc9 --- /dev/null +++ b/lib/sidebars/user_settings/menus/notifications_menu.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class NotificationsMenu < ::Sidebars::Menu + include ::Sidebars::Concerns::RenderIfLoggedIn + + override :link + def link + profile_notifications_path + end + + override :title + def title + _('Notifications') + end + + override :sprite_icon + def sprite_icon + 'notifications' + end + + override :active_routes + def active_routes + { controller: :notifications } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/password_menu.rb b/lib/sidebars/user_settings/menus/password_menu.rb new file mode 100644 index 00000000000..9a53e0c727e --- /dev/null +++ b/lib/sidebars/user_settings/menus/password_menu.rb @@ -0,0 +1,39 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class PasswordMenu < ::Sidebars::Menu + override :link + def link + edit_profile_password_path + end + + override :title + def title + _('Password') + end + + override :sprite_icon + def sprite_icon + 'lock' + end + + override :render? + def render? + !!context.current_user&.allow_password_authentication? + end + + override :active_routes + def active_routes + { controller: :passwords } + end + + override :extra_container_html_options + def extra_container_html_options + { 'data-qa-selector': 'profile_password_link' } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/preferences_menu.rb b/lib/sidebars/user_settings/menus/preferences_menu.rb new file mode 100644 index 00000000000..b6b94ec1ad9 --- /dev/null +++ b/lib/sidebars/user_settings/menus/preferences_menu.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class PreferencesMenu < ::Sidebars::Menu + include ::Sidebars::Concerns::RenderIfLoggedIn + + override :link + def link + profile_preferences_path + end + + override :title + def title + _('Preferences') + end + + override :sprite_icon + def sprite_icon + 'preferences' + end + + override :active_routes + def active_routes + { controller: :preferences } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/profile_menu.rb b/lib/sidebars/user_settings/menus/profile_menu.rb new file mode 100644 index 00000000000..73119070586 --- /dev/null +++ b/lib/sidebars/user_settings/menus/profile_menu.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class ProfileMenu < ::Sidebars::Menu + include ::Sidebars::Concerns::RenderIfLoggedIn + + override :link + def link + profile_path + end + + override :title + def title + _('Profile') + end + + override :sprite_icon + def sprite_icon + 'profile' + end + + override :active_routes + def active_routes + { path: 'profiles#show' } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/saved_replies_menu.rb b/lib/sidebars/user_settings/menus/saved_replies_menu.rb new file mode 100644 index 00000000000..25c8b2f8eb2 --- /dev/null +++ b/lib/sidebars/user_settings/menus/saved_replies_menu.rb @@ -0,0 +1,42 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class SavedRepliesMenu < ::Sidebars::Menu + include UsersHelper + + override :link + def link + profile_saved_replies_path + end + + override :title + def title + _('Saved Replies') + end + + override :sprite_icon + def sprite_icon + 'symlink' + end + + override :render? + def render? + !!context.current_user && saved_replies_enabled? + end + + override :active_routes + def active_routes + { controller: :saved_replies } + end + + private + + def current_user + context.current_user + end + end + end + end +end diff --git a/lib/sidebars/user_settings/menus/ssh_keys_menu.rb b/lib/sidebars/user_settings/menus/ssh_keys_menu.rb new file mode 100644 index 00000000000..8d92db0147a --- /dev/null +++ b/lib/sidebars/user_settings/menus/ssh_keys_menu.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + module Menus + class SshKeysMenu < ::Sidebars::Menu + include ::Sidebars::Concerns::RenderIfLoggedIn + + override :link + def link + profile_keys_path + end + + override :title + def title + _('SSH Keys') + end + + override :sprite_icon + def sprite_icon + 'key' + end + + override :active_routes + def active_routes + { controller: :keys } + end + + override :extra_container_html_options + def extra_container_html_options + { 'data-qa-selector': 'ssh_keys_link' } + end + end + end + end +end diff --git a/lib/sidebars/user_settings/panel.rb b/lib/sidebars/user_settings/panel.rb new file mode 100644 index 00000000000..14a52a8fb23 --- /dev/null +++ b/lib/sidebars/user_settings/panel.rb @@ -0,0 +1,51 @@ +# frozen_string_literal: true + +module Sidebars + module UserSettings + class Panel < ::Sidebars::Panel + override :configure_menus + def configure_menus + add_menus + end + + override :aria_label + def aria_label + _('User settings') + end + + override :render_raw_scope_menu_partial + def render_raw_scope_menu_partial + "shared/nav/user_settings_scope_header" + end + + override :super_sidebar_context_header + def super_sidebar_context_header + @super_sidebar_context_header ||= { + title: aria_label, + avatar: context.current_user.avatar_url + } + end + + private + + def add_menus + add_menu(Sidebars::UserSettings::Menus::ProfileMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::AccountMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::ApplicationsMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::ChatMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::AccessTokensMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::EmailsMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::PasswordMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::NotificationsMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::SshKeysMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::GpgKeysMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::PreferencesMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::SavedRepliesMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::ActiveSessionsMenu.new(context)) + add_menu(Sidebars::UserSettings::Menus::AuthenticationLogMenu.new(context)) + end + end + end +end + +Sidebars::UserSettings::Panel.prepend_mod_with('Sidebars::UserSettings::Panel') diff --git a/lib/sidebars/your_work/panel.rb b/lib/sidebars/your_work/panel.rb index 215a2a2da09..5f917976872 100644 --- a/lib/sidebars/your_work/panel.rb +++ b/lib/sidebars/your_work/panel.rb @@ -18,6 +18,14 @@ module Sidebars "shared/nav/your_work_scope_header" end + override :super_sidebar_context_header + def super_sidebar_context_header + @super_sidebar_context_header ||= { + title: aria_label, + icon: 'work' + } + end + private def add_menus @@ -33,3 +41,4 @@ module Sidebars end end end +Sidebars::YourWork::Panel.prepend_mod_with('Sidebars::YourWork::Panel') |