From c056cc105d38e6b5fcb12f1cb6a84823b4f950b5 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Fri, 21 Jun 2024 12:04:34 +0200 Subject: [PATCH 1/7] Use the same structure for the submenu of Members that we used in other modules --- .../members/index_page_header_component.rb | 3 +- app/controllers/members/menus_controller.rb | 4 +- .../members/menu.rb} | 65 +++++++++---------- 3 files changed, 33 insertions(+), 39 deletions(-) rename app/{helpers/menus/members_helper.rb => menus/members/menu.rb} (66%) diff --git a/app/components/members/index_page_header_component.rb b/app/components/members/index_page_header_component.rb index 907a6cc2f4e3..70c416a30191 100644 --- a/app/components/members/index_page_header_component.rb +++ b/app/components/members/index_page_header_component.rb @@ -31,7 +31,6 @@ class Members::IndexPageHeaderComponent < ApplicationComponent include OpPrimer::ComponentHelpers include ApplicationHelper - include Menus::MembersHelper def initialize(project: nil) super @@ -77,7 +76,7 @@ def current_query query_name = nil menu_header = nil - first_level_menu_items.find do |section| + Members::Menu.new(project: @project, params:).menu_items.find do |section| section.children.find do |menu_query| if !!menu_query.selected query_name = menu_query.title diff --git a/app/controllers/members/menus_controller.rb b/app/controllers/members/menus_controller.rb index 75be7ba2c8c2..edfaec250776 100644 --- a/app/controllers/members/menus_controller.rb +++ b/app/controllers/members/menus_controller.rb @@ -27,13 +27,11 @@ #++ module Members class MenusController < ApplicationController - include Menus::MembersHelper - before_action :find_project_by_project_id, :authorize def show - @sidebar_menu_items = first_level_menu_items + @sidebar_menu_items = Members::Menu.new(project: @project, params:).menu_items render layout: nil end end diff --git a/app/helpers/menus/members_helper.rb b/app/menus/members/menu.rb similarity index 66% rename from app/helpers/menus/members_helper.rb rename to app/menus/members/menu.rb index 06fe2d4931d6..d5c9dca9217d 100644 --- a/app/helpers/menus/members_helper.rb +++ b/app/menus/members/menu.rb @@ -1,6 +1,6 @@ -#-- copyright +# -- copyright # OpenProject is an open source project management software. -# Copyright (C) 2012-2024 the OpenProject GmbH +# Copyright (C) 2010-2023 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. @@ -24,50 +24,49 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # See COPYRIGHT and LICENSE files for more details. -#++ +# ++ +module Members + class Menu < Submenu + attr_reader :project, :params -module Menus - module MembersHelper - def first_level_menu_items - [OpenProject::Menu::MenuGroup.new(header: nil, children: user_status_options)] + nested_menu_items - end - - private + def initialize(project: nil, params: nil) + @project = project + @params = params - def user_status_options - [ - OpenProject::Menu::MenuItem.new(title: I18n.t("members.menu.all"), - href: project_members_path, - selected: active_filter_count == 0), - OpenProject::Menu::MenuItem.new(title: I18n.t("members.menu.locked"), - href: project_members_path(status: :locked), - selected: selected?(:status, :locked)), - OpenProject::Menu::MenuItem.new(title: I18n.t("members.menu.invited"), - href: project_members_path(status: :invited), - selected: selected?(:status, :invited)) - ] + super(view_type:, project:, params:) end - def nested_menu_items + def menu_items [ + OpenProject::Menu::MenuGroup.new(header:nil, children: user_status_options), OpenProject::Menu::MenuGroup.new(header: I18n.t("members.menu.project_roles"), children: project_roles_entries), OpenProject::Menu::MenuGroup.new(header: I18n.t("members.menu.wp_shares"), children: permission_menu_entries), OpenProject::Menu::MenuGroup.new(header: I18n.t("members.menu.groups"), children: project_group_entries) ] end + def user_status_options + [ + OpenProject::Menu::MenuItem.new(title: I18n.t("members.menu.all"), + href: project_members_path(project), + selected: active_filter_count == 0), + menu_item({status: :locked}, I18n.t("members.menu.locked")), + menu_item({status: :invited}, I18n.t("members.menu.invited")) + ] + end + def project_roles_entries ProjectRole .where(id: MemberRole.where(member_id: @project.members.select(:id)).select(:role_id)) .distinct .pluck(:id, :name) - .map { |id, name| menu_item(:role_id, id, name) } + .map { |id, name| menu_item( { role_id: id }, name) } end def permission_menu_entries Members::UserFilterComponent .share_options - .map { |name, id| menu_item(:shared_role_id, id, name) } + .map { |name, id| menu_item({ shared_role_id: id }, name) } end def project_group_entries @@ -76,19 +75,17 @@ def project_group_entries .order(lastname: :asc) .distinct .pluck(:id, :lastname) - .map { |id, name| menu_item(:group_id, id, name) } + .map { |id, name| menu_item({ group_id: id }, name) } end - def menu_item(filter_key, id, name) - OpenProject::Menu::MenuItem.new(title: name, - href: project_members_path(filter_key => id), - selected: selected?(filter_key, id)) - end - - def selected?(filter_key, value) + def selected?(query_params) return false if active_filter_count > 1 - params[filter_key] == value.to_s + super(query_params) + end + + def query_path(query_params) + project_members_path(project, query_params) end def active_filter_count From b2415fd12c1d669fc220269b3d62445454c58c67 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Fri, 21 Jun 2024 12:38:26 +0200 Subject: [PATCH 2/7] Use the same structure for the submenu of Projects that we used in other modules --- .../projects/index_page_header_component.rb | 4 +- app/controllers/projects/menus_controller.rb | 5 +- .../projects.rb => menus/projects/menu.rb} | 74 +++++++++---------- .../projects/menu_spec.rb} | 12 +-- 4 files changed, 44 insertions(+), 51 deletions(-) rename app/{helpers/menus/projects.rb => menus/projects/menu.rb} (68%) rename spec/{helpers/menus/projects_spec.rb => menus/projects/menu_spec.rb} (94%) diff --git a/app/components/projects/index_page_header_component.rb b/app/components/projects/index_page_header_component.rb index f75f4e4b7b5b..6d4d5b121199 100644 --- a/app/components/projects/index_page_header_component.rb +++ b/app/components/projects/index_page_header_component.rb @@ -126,9 +126,9 @@ def current_breadcrumb_element def current_section return @current_section if defined?(@current_section) - projects_menu = Menus::Projects.new(controller_path:, params:, current_user:) + projects_menu = Projects::Menu.new(controller_path:, params:, current_user:) - @current_section = projects_menu.first_level_menu_items.find { |section| section.children.any?(&:selected) } + @current_section = projects_menu.menu_items.find { |section| section.children.any?(&:selected) } end def header_save_action(header:, message:, label:, href:, method: nil) diff --git a/app/controllers/projects/menus_controller.rb b/app/controllers/projects/menus_controller.rb index cdecdf19bbdf..e339bbc5a180 100644 --- a/app/controllers/projects/menus_controller.rb +++ b/app/controllers/projects/menus_controller.rb @@ -32,9 +32,8 @@ class MenusController < ApplicationController no_authorization_required! :show def show - projects_menu = Menus::Projects.new(controller_path: params[:controller_path], params:, current_user:) - - @sidebar_menu_items = projects_menu.first_level_menu_items + projects_menu = Projects::Menu.new(controller_path: params[:controller_path], params:, current_user:) + @sidebar_menu_items = projects_menu.menu_items render layout: nil end diff --git a/app/helpers/menus/projects.rb b/app/menus/projects/menu.rb similarity index 68% rename from app/helpers/menus/projects.rb rename to app/menus/projects/menu.rb index f3ea3b2e69e7..cb6bebcad651 100644 --- a/app/helpers/menus/projects.rb +++ b/app/menus/projects/menu.rb @@ -26,33 +26,49 @@ # See COPYRIGHT and LICENSE files for more details. #++ -module Menus - class Projects +module Projects + class Menu < Submenu include Rails.application.routes.url_helpers attr_reader :controller_path, :params, :current_user - def initialize(controller_path:, params:, current_user:) - # rubocop:disable Rails/HelperInstanceVariable - @controller_path = controller_path + def initialize(params:, controller_path:, current_user:) @params = params + @controller_path = controller_path @current_user = current_user - # rubocop:enable Rails/HelperInstanceVariable + + super(view_type:, project:, params:) end - def first_level_menu_items + def menu_items [ - OpenProject::Menu::MenuGroup.new(header: nil, - children: main_static_filters), - OpenProject::Menu::MenuGroup.new(header: I18n.t(:"projects.lists.public"), - children: public_filters), - OpenProject::Menu::MenuGroup.new(header: I18n.t(:"projects.lists.my_private"), - children: my_filters), - OpenProject::Menu::MenuGroup.new(header: I18n.t(:"activerecord.attributes.project.status_code"), - children: status_static_filters) + OpenProject::Menu::MenuGroup.new(header: nil, children: main_static_filters), + OpenProject::Menu::MenuGroup.new(header: I18n.t(:"projects.lists.public"), children: public_filters), + OpenProject::Menu::MenuGroup.new(header: I18n.t(:"projects.lists.my_private"), children: my_filters), + OpenProject::Menu::MenuGroup.new(header: I18n.t(:"activerecord.attributes.project.status_code"), children: status_static_filters) ] end + def selected?(query_params) + case controller_path + when "projects" + case params[:query_id] + when nil + query_params[:query_id].to_s == Queries::Projects::Factory::DEFAULT_STATIC + when /\A\d+\z/ + query_params[:query_id].to_s == params[:query_id] + else + query_params[:query_id].to_s == params[:query_id] unless modification_params? + end + when "projects/queries" + query_params[:query_id].to_s == params[:id] + end + end + + def query_path(query_params) + projects_path(query_params) + end + private def main_static_filters @@ -74,7 +90,7 @@ def status_static_filters def static_filters(ids) ids.map do |id| - query_menu_item(::Queries::Projects::Factory.static_query(id), id:) + menu_item({ query_id: id }, ::Queries::Projects::Factory.static_query(id).name) end end @@ -82,36 +98,14 @@ def public_filters ::ProjectQuery .public_lists .order(:name) - .map { |query| query_menu_item(query) } + .map { |query| menu_item({ query_id: query.id }, query.name) } end def my_filters ::ProjectQuery .private_lists(user: current_user) .order(:name) - .map { |query| query_menu_item(query) } - end - - def query_menu_item(query, id: nil) - OpenProject::Menu::MenuItem.new(title: query.name, - href: projects_path(query_id: id || query.id), - selected: query_item_selected?(id || query.id)) - end - - def query_item_selected?(id) - case controller_path - when "projects" - case params[:query_id] - when nil - id.to_s == Queries::Projects::Factory::DEFAULT_STATIC - when /\A\d+\z/ - id.to_s == params[:query_id] - else - id.to_s == params[:query_id] unless modification_params? - end - when "projects/queries" - id.to_s == params[:id] - end + .map { |query| menu_item({ query_id: query.id }, query.name) } end def modification_params? diff --git a/spec/helpers/menus/projects_spec.rb b/spec/menus/projects/menu_spec.rb similarity index 94% rename from spec/helpers/menus/projects_spec.rb rename to spec/menus/projects/menu_spec.rb index 43bf0c7f2254..461ef99de5bf 100644 --- a/spec/helpers/menus/projects_spec.rb +++ b/spec/menus/projects/menu_spec.rb @@ -30,7 +30,7 @@ require "spec_helper" -RSpec.describe Menus::Projects do +RSpec.describe Projects::Menu do let(:instance) { described_class.new(controller_path:, params:, current_user:) } let(:controller_path) { "foo" } let(:params) { {} } @@ -49,15 +49,15 @@ ProjectQuery.create!(name: "Public query", user: build(:user), public: true) end - subject(:first_level_menu_items) { instance.first_level_menu_items } + subject(:menu_items) { instance.menu_items } it "returns 4 menu groups" do - expect(first_level_menu_items).to all(be_a(OpenProject::Menu::MenuGroup)) - expect(first_level_menu_items.length).to eq(4) + expect(menu_items).to all(be_a(OpenProject::Menu::MenuGroup)) + expect(menu_items.length).to eq(4) end describe "children items" do - subject(:children_menu_items) { first_level_menu_items.flat_map(&:children) } + subject(:children_menu_items) { menu_items.flat_map(&:children) } context "when the current user is an admin" do before do @@ -97,7 +97,7 @@ end describe "selected children items" do - subject(:selected_menu_items) { first_level_menu_items.flat_map(&:children).select(&:selected) } + subject(:selected_menu_items) { menu_items.flat_map(&:children).select(&:selected) } context "when on homescreen page" do let(:controller_path) { "homescreen" } From 73efe65a1b0fab895eec2cf47ef35cb3ed860a13 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Fri, 21 Jun 2024 14:35:51 +0200 Subject: [PATCH 3/7] Use the same structure for the submenu of Meetings that we used in other modules --- .../controllers/meetings/menus_controller.rb | 43 ++++++++ .../meeting/app/helpers/meetings_helper.rb | 97 ------------------- modules/meeting/app/menus/meetings/menu.rb | 86 ++++++++++++++++ .../meetings/_menu_query_select.html.erb | 79 --------------- .../app/views/meetings/menus/_menu.html.erb | 5 + .../app/views/meetings/menus/show.html.erb | 4 + modules/meeting/config/routes.rb | 10 +- .../lib/open_project/meeting/engine.rb | 8 +- 8 files changed, 152 insertions(+), 180 deletions(-) create mode 100644 modules/meeting/app/controllers/meetings/menus_controller.rb create mode 100644 modules/meeting/app/menus/meetings/menu.rb delete mode 100644 modules/meeting/app/views/meetings/_menu_query_select.html.erb create mode 100644 modules/meeting/app/views/meetings/menus/_menu.html.erb create mode 100644 modules/meeting/app/views/meetings/menus/show.html.erb diff --git a/modules/meeting/app/controllers/meetings/menus_controller.rb b/modules/meeting/app/controllers/meetings/menus_controller.rb new file mode 100644 index 000000000000..51c3fd71435e --- /dev/null +++ b/modules/meeting/app/controllers/meetings/menus_controller.rb @@ -0,0 +1,43 @@ +# -- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2010-2023 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +# ++ +module Meetings + class MenusController < ApplicationController + before_action :load_and_authorize_in_optional_project + + def show + @submenu_menu_items = ::Meetings::Menu.new(project: @project, params:).menu_items + @create_btn_options = if @project.present? && User.current.allowed_in_project?(:create_meetings, @project) + { href: new_project_meeting_path(@project), module_key: "meeting" } + elsif @project.nil? && User.current.allowed_in_any_project?(:create_meetings) + { href: new_meeting_path, module_key: "meeting" } + end + + render layout: nil + end + end +end diff --git a/modules/meeting/app/helpers/meetings_helper.rb b/modules/meeting/app/helpers/meetings_helper.rb index cbf86094711c..87b762d685d7 100644 --- a/modules/meeting/app/helpers/meetings_helper.rb +++ b/modules/meeting/app/helpers/meetings_helper.rb @@ -27,103 +27,6 @@ #++ module MeetingsHelper - def top_level_sidebar_menu_items - [ - menu_upcoming_meetings_item, - menu_past_meetings_item - ] - end - - def involvement_sidebar_menu_items - [ - menu_upcoming_invitations_item, - menu_past_invitations_item, - menu_attendee_item, - menu_creator_item - ] - end - - def menu_upcoming_meetings_item - path = project_or_global_meetings_path( - filters: [ - { time: { operator: "=", values: ["future"] } } - ], - sort: "start_time" - ) - - menu_link_element path, t(:label_upcoming_meetings) - end - - def menu_past_meetings_item - path = project_or_global_meetings_path( - filters: [{ time: { operator: "=", values: ["past"] } }], - sort: "start_time:desc" - ) - - menu_link_element path, t(:label_past_meetings) - end - - def menu_upcoming_invitations_item - path = project_or_global_meetings_path - - menu_link_element path, t(:label_upcoming_invitations) - end - - def menu_past_invitations_item - path = project_or_global_meetings_path( - filters: [ - { time: { operator: "=", values: ["past"] } }, - { invited_user_id: { operator: "=", values: [User.current.id.to_s] } } - ], - sort: "start_time:desc" - ) - - menu_link_element path, t(:label_past_invitations) - end - - def menu_attendee_item - path = project_or_global_meetings_path( - filters: [{ attended_user_id: { operator: "=", values: [User.current.id.to_s] } }] - ) - - menu_link_element path, t(:label_attendee) - end - - def menu_creator_item - path = project_or_global_meetings_path( - filters: [{ author_id: { operator: "=", values: [User.current.id.to_s] } }] - ) - - menu_link_element path, t(:label_author) - end - - def project_or_global_meetings_path(filters: nil, sort: nil) - return polymorphic_path([@project, :meetings]) if filters.blank? && sort.blank? - - query_params = {}.tap do |query| - query[:filters] = filters.to_json if filters.present? - query[:sort] = sort if sort.present? - end - - polymorphic_path([@project, :meetings], query_params) - end - - def menu_link_element(path, label) - link_to path, class: menu_item_css_class(path), title: label do - content_tag(:span, class: "op-sidemenu--item-title") do - label - end - end - end - - def menu_item_css_class(path) - "op-sidemenu--item-action#{menu_item_selected(path) ? ' selected' : ''}" - end - - def menu_item_selected(menu_item_path) - menu_item_path == request.fullpath - end - def format_participant_list(participants) if participants.any? user_links = participants diff --git a/modules/meeting/app/menus/meetings/menu.rb b/modules/meeting/app/menus/meetings/menu.rb new file mode 100644 index 000000000000..e7275f8822ad --- /dev/null +++ b/modules/meeting/app/menus/meetings/menu.rb @@ -0,0 +1,86 @@ +# -- copyright +# OpenProject is an open source project management software. +# Copyright (C) 2010-2023 the OpenProject GmbH +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License version 3. +# +# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: +# Copyright (C) 2006-2013 Jean-Philippe Lang +# Copyright (C) 2010-2013 the ChiliProject Team +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +# See COPYRIGHT and LICENSE files for more details. +# ++ +module Meetings + class Menu < Submenu + attr_reader :view_type, :project + + def initialize(project: nil, params: nil) + @project = project + @params = params + + super(view_type:, project:, params:) + end + + def menu_items + [ + OpenProject::Menu::MenuGroup.new(header: nil, children: top_level_menu_items), + OpenProject::Menu::MenuGroup.new(header: I18n.t(:label_involvement), children: involvement_sidebar_menu_items) + ] + end + + def top_level_menu_items + upcoming_filter = [{ time: { operator: "=", values: ["future"] } }].to_json + past_filter = [{ time: { operator: "=", values: ["past"] } }].to_json + + [ + menu_item({ filters: upcoming_filter, sort: "start_time" }, + I18n.t(:label_upcoming_meetings)), + menu_item({ filters: past_filter, sort: "start_time:desc" }, + I18n.t(:label_past_meetings)) + ] + end + + def involvement_sidebar_menu_items + past_filter = [ + { time: { operator: "=", values: ["past"] } }, + { invited_user_id: { operator: "=", values: [User.current.id.to_s] } } + ].to_json + attendee_filter = [{ attended_user_id: { operator: "=", values: [User.current.id.to_s] } }].to_json + author_filter = [{ author_id: { operator: "=", values: [User.current.id.to_s] } }].to_json + + [ + menu_item({}, + I18n.t(:label_upcoming_invitations)), + menu_item({ filters: past_filter, sort: "start_time:desc" }, + I18n.t(:label_past_invitations)), + menu_item({ filters: attendee_filter }, + I18n.t(:label_attendee)), + menu_item({ filters: author_filter }, + I18n.t(:label_author)) + ] + end + + def query_path(query_params) + if project.present? + project_meetings_path(project, params.permit(query_params.keys).merge!(query_params)) + else + meetings_path(params.permit(query_params.keys).merge!(query_params)) + end + end + end +end diff --git a/modules/meeting/app/views/meetings/_menu_query_select.html.erb b/modules/meeting/app/views/meetings/_menu_query_select.html.erb deleted file mode 100644 index 812a78d211fc..000000000000 --- a/modules/meeting/app/views/meetings/_menu_query_select.html.erb +++ /dev/null @@ -1,79 +0,0 @@ -<%#-- copyright -OpenProject is an open source project management software. -Copyright (C) 2012-2024 the OpenProject GmbH - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License version 3. - -OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows: -Copyright (C) 2006-2013 Jean-Philippe Lang -Copyright (C) 2010-2013 the ChiliProject Team - -This program is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License -as published by the Free Software Foundation; either version 2 -of the License, or (at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - -See COPYRIGHT and LICENSE files for more details. - -++#%> - -
-
-
-
    - <% top_level_sidebar_menu_items.each do |menu_item| %> -
  • - <%= menu_item %> -
  • - <% end %> -
- -
- - - -
    - <% involvement_sidebar_menu_items.each do |menu_item| %> -
  • - <%= menu_item %> -
  • - <% end %> -
-
-
-
- - -
diff --git a/modules/meeting/app/views/meetings/menus/_menu.html.erb b/modules/meeting/app/views/meetings/menus/_menu.html.erb new file mode 100644 index 000000000000..34018e7e1a87 --- /dev/null +++ b/modules/meeting/app/views/meetings/menus/_menu.html.erb @@ -0,0 +1,5 @@ + <%= turbo_frame_tag "meeting_sidemenu", + src: @project.present? ? menu_project_meetings_path(@project) : meetings_menu_path(), + target: '_top', + data: { turbo: false }, + loading: :lazy %> diff --git a/modules/meeting/app/views/meetings/menus/show.html.erb b/modules/meeting/app/views/meetings/menus/show.html.erb new file mode 100644 index 000000000000..84fcb51f5019 --- /dev/null +++ b/modules/meeting/app/views/meetings/menus/show.html.erb @@ -0,0 +1,4 @@ +<%= turbo_frame_tag "meeting_sidemenu" do %> + <%= render OpenProject::Common::SubmenuComponent.new(sidebar_menu_items: @submenu_menu_items, + create_btn_options: @create_btn_options) %> +<% end %> diff --git a/modules/meeting/config/routes.rb b/modules/meeting/config/routes.rb index 0044abb9ec46..9a23450880a3 100644 --- a/modules/meeting/config/routes.rb +++ b/modules/meeting/config/routes.rb @@ -28,7 +28,11 @@ Rails.application.routes.draw do resources :projects, only: %i[] do - resources :meetings, only: %i[index new create show] + resources :meetings, only: %i[index new create show] do + collection do + get "menu" => "meetings/menus#show" + end + end end resources :work_packages, only: %i[] do @@ -47,6 +51,10 @@ end end + namespace :meetings do + resource :menu, only: %[show] + end + resources :meetings do member do get :cancel_edit diff --git a/modules/meeting/lib/open_project/meeting/engine.rb b/modules/meeting/lib/open_project/meeting/engine.rb index d2a26c53b74c..0669e0933608 100644 --- a/modules/meeting/lib/open_project/meeting/engine.rb +++ b/modules/meeting/lib/open_project/meeting/engine.rb @@ -43,10 +43,12 @@ class Engine < ::Rails::Engine { meetings: %i[index show download_ics participants_dialog history], meeting_agendas: %i[history show diff], meeting_minutes: %i[history show diff], + "meetings/menus": %i[show], work_package_meetings_tab: %i[index count] }, permissible_on: :project permission :create_meetings, - { meetings: %i[new create copy] }, + { meetings: %i[new create copy], + "meetings/menus": %i[show] }, permissible_on: :project, require: :member, contract_actions: { meetings: %i[create] } @@ -120,7 +122,7 @@ class Engine < ::Rails::Engine menu :project_menu, :meetings_query_select, { controller: "/meetings", action: "index" }, parent: :meetings, - partial: "meetings/menu_query_select" + partial: "meetings/menus/menu" should_render_global_menu_item = Proc.new do (User.current.logged? || !Setting.login_required?) && @@ -145,7 +147,7 @@ class Engine < ::Rails::Engine menu :global_menu, :meetings_query_select, { controller: "/meetings", action: "index", project_id: nil }, parent: :meetings, - partial: "meetings/menu_query_select", + partial: "meetings/menus/menu", if: should_render_global_menu_item ActiveSupport::Inflector.inflections do |inflect| From 752af78ed9af03815a5f0801e9f229c4f5000d28 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Tue, 25 Jun 2024 09:44:59 +0200 Subject: [PATCH 4/7] Adapt test to new rails-based Submenu implementation --- .../spec/features/meetings_global_menu_item_spec.rb | 1 + modules/meeting/spec/features/meetings_index_spec.rb | 8 ++++---- modules/meeting/spec/support/pages/meetings/index.rb | 4 ++-- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/modules/meeting/spec/features/meetings_global_menu_item_spec.rb b/modules/meeting/spec/features/meetings_global_menu_item_spec.rb index 972eb8c567fc..09a36150a64d 100644 --- a/modules/meeting/spec/features/meetings_global_menu_item_spec.rb +++ b/modules/meeting/spec/features/meetings_global_menu_item_spec.rb @@ -33,6 +33,7 @@ require_relative "../support/pages/meetings/index" RSpec.describe "Meetings global menu item", + :js, :with_cuprite do shared_let(:user_without_permissions) { create(:user) } shared_let(:admin) { create(:admin) } diff --git a/modules/meeting/spec/features/meetings_index_spec.rb b/modules/meeting/spec/features/meetings_index_spec.rb index e6ad768f830e..2cc760658268 100644 --- a/modules/meeting/spec/features/meetings_index_spec.rb +++ b/modules/meeting/spec/features/meetings_index_spec.rb @@ -30,7 +30,7 @@ require_relative "../support/pages/meetings/index" -RSpec.describe "Meetings", "Index", :with_cuprite do +RSpec.describe "Meetings", "Index", :js, :with_cuprite do # The order the Projects are created in is important. By naming `project` alphanumerically # after `other_project`, we can ensure that subsequent specs that assert sorting is # correct for the right reasons (sorting by Project name and not id) @@ -209,7 +209,7 @@ def invite_to_meeting(meeting) invite_to_meeting(yesterdays_meeting) invite_to_meeting(other_project_meeting) - meetings_page.navigate_by_modules_menu + meetings_page.visit! meetings_page.expect_meetings_listed(meeting, other_project_meeting) meetings_page.expect_meetings_not_listed(yesterdays_meeting) end @@ -234,7 +234,7 @@ def invite_to_meeting(meeting) let(:permissions) { %i(view_meetings create_meetings) } it "shows the create new buttons" do - meetings_page.navigate_by_modules_menu + meetings_page.visit! meetings_page.expect_create_new_buttons end @@ -244,7 +244,7 @@ def invite_to_meeting(meeting) let(:permissions) { %i[view_meetings] } it "doesn't show a create new button" do - meetings_page.navigate_by_modules_menu + meetings_page.visit! meetings_page.expect_no_create_new_buttons end diff --git a/modules/meeting/spec/support/pages/meetings/index.rb b/modules/meeting/spec/support/pages/meetings/index.rb index 6c810b3797c5..79384ee10bfb 100644 --- a/modules/meeting/spec/support/pages/meetings/index.rb +++ b/modules/meeting/spec/support/pages/meetings/index.rb @@ -56,7 +56,7 @@ def expect_no_create_new_buttons expect(page).not_to have_test_selector("add-meeting-button") within "#main-menu" do - expect(page).to have_no_button "Meeting" + expect(page).not_to have_test_selector "meeting--create-button" end end @@ -68,7 +68,7 @@ def expect_create_new_buttons expect(page).to have_test_selector("add-meeting-button") within "#main-menu" do - expect(page).to have_button "Meeting" + expect(page).to have_test_selector "meeting--create-button" end end From 5673cf43e5abb2cf161ff10ff14066d6d3076b6f Mon Sep 17 00:00:00 2001 From: Christophe Bliard Date: Wed, 26 Jun 2024 11:52:27 +0200 Subject: [PATCH 5/7] Make rubocop happier --- app/menus/members/menu.rb | 21 +++++++++------------ app/menus/projects/menu.rb | 3 ++- 2 files changed, 11 insertions(+), 13 deletions(-) diff --git a/app/menus/members/menu.rb b/app/menus/members/menu.rb index d5c9dca9217d..4ebd17cb9a06 100644 --- a/app/menus/members/menu.rb +++ b/app/menus/members/menu.rb @@ -1,6 +1,6 @@ -# -- copyright +#-- copyright # OpenProject is an open source project management software. -# Copyright (C) 2010-2023 the OpenProject GmbH +# Copyright (C) 2010-2024 the OpenProject GmbH # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License version 3. @@ -24,21 +24,18 @@ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # See COPYRIGHT and LICENSE files for more details. -# ++ +#++ module Members class Menu < Submenu attr_reader :project, :params def initialize(project: nil, params: nil) - @project = project - @params = params - - super(view_type:, project:, params:) + super(view_type: nil, project:, params:) end def menu_items [ - OpenProject::Menu::MenuGroup.new(header:nil, children: user_status_options), + OpenProject::Menu::MenuGroup.new(header: nil, children: user_status_options), OpenProject::Menu::MenuGroup.new(header: I18n.t("members.menu.project_roles"), children: project_roles_entries), OpenProject::Menu::MenuGroup.new(header: I18n.t("members.menu.wp_shares"), children: permission_menu_entries), OpenProject::Menu::MenuGroup.new(header: I18n.t("members.menu.groups"), children: project_group_entries) @@ -50,8 +47,8 @@ def user_status_options OpenProject::Menu::MenuItem.new(title: I18n.t("members.menu.all"), href: project_members_path(project), selected: active_filter_count == 0), - menu_item({status: :locked}, I18n.t("members.menu.locked")), - menu_item({status: :invited}, I18n.t("members.menu.invited")) + menu_item({ status: :locked }, I18n.t("members.menu.locked")), + menu_item({ status: :invited }, I18n.t("members.menu.invited")) ] end @@ -60,7 +57,7 @@ def project_roles_entries .where(id: MemberRole.where(member_id: @project.members.select(:id)).select(:role_id)) .distinct .pluck(:id, :name) - .map { |id, name| menu_item( { role_id: id }, name) } + .map { |id, name| menu_item({ role_id: id }, name) } end def permission_menu_entries @@ -81,7 +78,7 @@ def project_group_entries def selected?(query_params) return false if active_filter_count > 1 - super(query_params) + super end def query_path(query_params) diff --git a/app/menus/projects/menu.rb b/app/menus/projects/menu.rb index cb6bebcad651..50e0a8cc7ccd 100644 --- a/app/menus/projects/menu.rb +++ b/app/menus/projects/menu.rb @@ -45,7 +45,8 @@ def menu_items OpenProject::Menu::MenuGroup.new(header: nil, children: main_static_filters), OpenProject::Menu::MenuGroup.new(header: I18n.t(:"projects.lists.public"), children: public_filters), OpenProject::Menu::MenuGroup.new(header: I18n.t(:"projects.lists.my_private"), children: my_filters), - OpenProject::Menu::MenuGroup.new(header: I18n.t(:"activerecord.attributes.project.status_code"), children: status_static_filters) + OpenProject::Menu::MenuGroup.new(header: I18n.t(:"activerecord.attributes.project.status_code"), + children: status_static_filters) ] end From c07cf311f7b095f742cbfb5645d614494e27409f Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Wed, 26 Jun 2024 14:35:30 +0200 Subject: [PATCH 6/7] Pass allowed parameters and double check for base route whether there are no filters added --- app/menus/submenu.rb | 4 ++++ modules/meeting/app/views/meetings/menus/_menu.html.erb | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/app/menus/submenu.rb b/app/menus/submenu.rb index e2866c67922f..206ba2682aab 100644 --- a/app/menus/submenu.rb +++ b/app/menus/submenu.rb @@ -102,6 +102,10 @@ def selected?(query_params) end end + if query_params.empty? && params[:filters].present? + return false + end + true end diff --git a/modules/meeting/app/views/meetings/menus/_menu.html.erb b/modules/meeting/app/views/meetings/menus/_menu.html.erb index 34018e7e1a87..74bacf441dd4 100644 --- a/modules/meeting/app/views/meetings/menus/_menu.html.erb +++ b/modules/meeting/app/views/meetings/menus/_menu.html.erb @@ -1,5 +1,5 @@ <%= turbo_frame_tag "meeting_sidemenu", - src: @project.present? ? menu_project_meetings_path(@project) : meetings_menu_path(), + src: @project.present? ? menu_project_meetings_path(@project, **params.permit(:filters, :sort)) : meetings_menu_path(**params.permit(:filters, :sort)), target: '_top', data: { turbo: false }, loading: :lazy %> From 6e1f5fe89b1a7d6061231c0dd6214898e954f655 Mon Sep 17 00:00:00 2001 From: Henriette Darge Date: Fri, 28 Jun 2024 08:36:11 +0200 Subject: [PATCH 7/7] Change order of parameters --- app/menus/members/menu.rb | 10 ++++---- app/menus/projects/menu.rb | 6 ++--- app/menus/submenu.rb | 8 +++---- modules/boards/app/menus/boards/menu.rb | 2 +- modules/gantt/app/menus/gantt/menu.rb | 4 ++-- modules/meeting/app/menus/meetings/menu.rb | 24 +++++++++---------- .../reporting/app/menus/cost_reports/menu.rb | 4 ++-- 7 files changed, 29 insertions(+), 29 deletions(-) diff --git a/app/menus/members/menu.rb b/app/menus/members/menu.rb index 4ebd17cb9a06..795148e108b8 100644 --- a/app/menus/members/menu.rb +++ b/app/menus/members/menu.rb @@ -47,8 +47,8 @@ def user_status_options OpenProject::Menu::MenuItem.new(title: I18n.t("members.menu.all"), href: project_members_path(project), selected: active_filter_count == 0), - menu_item({ status: :locked }, I18n.t("members.menu.locked")), - menu_item({ status: :invited }, I18n.t("members.menu.invited")) + menu_item(I18n.t("members.menu.locked"), status: :locked), + menu_item(I18n.t("members.menu.invited"), status: :invited) ] end @@ -57,13 +57,13 @@ def project_roles_entries .where(id: MemberRole.where(member_id: @project.members.select(:id)).select(:role_id)) .distinct .pluck(:id, :name) - .map { |id, name| menu_item({ role_id: id }, name) } + .map { |id, name| menu_item(name, role_id: id) } end def permission_menu_entries Members::UserFilterComponent .share_options - .map { |name, id| menu_item({ shared_role_id: id }, name) } + .map { |name, id| menu_item(name, shared_role_id: id) } end def project_group_entries @@ -72,7 +72,7 @@ def project_group_entries .order(lastname: :asc) .distinct .pluck(:id, :lastname) - .map { |id, name| menu_item({ group_id: id }, name) } + .map { |id, name| menu_item(name, group_id: id) } end def selected?(query_params) diff --git a/app/menus/projects/menu.rb b/app/menus/projects/menu.rb index 50e0a8cc7ccd..bf1f277c5d50 100644 --- a/app/menus/projects/menu.rb +++ b/app/menus/projects/menu.rb @@ -91,7 +91,7 @@ def status_static_filters def static_filters(ids) ids.map do |id| - menu_item({ query_id: id }, ::Queries::Projects::Factory.static_query(id).name) + menu_item(::Queries::Projects::Factory.static_query(id).name, query_id: id) end end @@ -99,14 +99,14 @@ def public_filters ::ProjectQuery .public_lists .order(:name) - .map { |query| menu_item({ query_id: query.id }, query.name) } + .map { |query| menu_item(query.name, query_id: query.id) } end def my_filters ::ProjectQuery .private_lists(user: current_user) .order(:name) - .map { |query| menu_item({ query_id: query.id }, query.name) } + .map { |query| menu_item(query.name, query_id: query.id) } end def modification_params? diff --git a/app/menus/submenu.rb b/app/menus/submenu.rb index 206ba2682aab..0e495d4672d1 100644 --- a/app/menus/submenu.rb +++ b/app/menus/submenu.rb @@ -48,7 +48,7 @@ def starred_queries base_query .where("starred" => "t") .pluck(:id, :name) - .map { |id, name| menu_item(query_params(id), name) } + .map { |id, name| menu_item(name, query_params(id)) } end def default_queries @@ -60,7 +60,7 @@ def global_queries .where("starred" => "f") .where("public" => "t") .pluck(:id, :name) - .map { |id, name| menu_item(query_params(id), name) } + .map { |id, name| menu_item(name, query_params(id)) } end def custom_queries @@ -68,7 +68,7 @@ def custom_queries .where("starred" => "f") .where("public" => "f") .pluck(:id, :name) - .map { |id, name| menu_item(query_params(id), name) } + .map { |id, name| menu_item(name, query_params(id)) } end def base_query @@ -89,7 +89,7 @@ def query_params(id) { query_id: id } end - def menu_item(query_params, name) + def menu_item(name, query_params) OpenProject::Menu::MenuItem.new(title: name, href: query_path(query_params), selected: selected?(query_params)) diff --git a/modules/boards/app/menus/boards/menu.rb b/modules/boards/app/menus/boards/menu.rb index 0784b8145c76..75eba55b77b6 100644 --- a/modules/boards/app/menus/boards/menu.rb +++ b/modules/boards/app/menus/boards/menu.rb @@ -41,7 +41,7 @@ def global_queries .references(:project) .where(project: @project) .pluck(:id, :name) - .map { |id, name| menu_item(query_params(id), name) } + .map { |id, name| menu_item(name, query_params(id)) } end def starred_queries diff --git a/modules/gantt/app/menus/gantt/menu.rb b/modules/gantt/app/menus/gantt/menu.rb index f243ec98bd40..2ee70ad4cb97 100644 --- a/modules/gantt/app/menus/gantt/menu.rb +++ b/modules/gantt/app/menus/gantt/menu.rb @@ -44,8 +44,8 @@ def default_queries next if params.nil? menu_item( - params, - I18n.t("js.queries.#{query_key}") + I18n.t("js.queries.#{query_key}"), + params ) end end diff --git a/modules/meeting/app/menus/meetings/menu.rb b/modules/meeting/app/menus/meetings/menu.rb index e7275f8822ad..6528374a1ebe 100644 --- a/modules/meeting/app/menus/meetings/menu.rb +++ b/modules/meeting/app/menus/meetings/menu.rb @@ -48,10 +48,10 @@ def top_level_menu_items past_filter = [{ time: { operator: "=", values: ["past"] } }].to_json [ - menu_item({ filters: upcoming_filter, sort: "start_time" }, - I18n.t(:label_upcoming_meetings)), - menu_item({ filters: past_filter, sort: "start_time:desc" }, - I18n.t(:label_past_meetings)) + menu_item(I18n.t(:label_upcoming_meetings), + filters: upcoming_filter, sort: "start_time"), + menu_item(I18n.t(:label_past_meetings), + filters: past_filter, sort: "start_time:desc") ] end @@ -64,14 +64,14 @@ def involvement_sidebar_menu_items author_filter = [{ author_id: { operator: "=", values: [User.current.id.to_s] } }].to_json [ - menu_item({}, - I18n.t(:label_upcoming_invitations)), - menu_item({ filters: past_filter, sort: "start_time:desc" }, - I18n.t(:label_past_invitations)), - menu_item({ filters: attendee_filter }, - I18n.t(:label_attendee)), - menu_item({ filters: author_filter }, - I18n.t(:label_author)) + menu_item(I18n.t(:label_upcoming_invitations), + {}), + menu_item(I18n.t(:label_past_invitations), + { filters: past_filter, sort: "start_time:desc" }), + menu_item(I18n.t(:label_attendee), + { filters: attendee_filter }), + menu_item(I18n.t(:label_author), + { filters: author_filter }) ] end diff --git a/modules/reporting/app/menus/cost_reports/menu.rb b/modules/reporting/app/menus/cost_reports/menu.rb index 8677d824ebbd..fabd29c03f6e 100644 --- a/modules/reporting/app/menus/cost_reports/menu.rb +++ b/modules/reporting/app/menus/cost_reports/menu.rb @@ -46,13 +46,13 @@ def menu_items def global_queries CostQuery.public(project) .pluck(:id, :name) - .map { |id, name| menu_item(query_params(id), name) } + .map { |id, name| menu_item(name, query_params(id)) } end def custom_queries CostQuery.private(project, User.current) .pluck(:id, :name) - .map { |id, name| menu_item(query_params(id), name) } + .map { |id, name| menu_item(name, query_params(id)) } end def selected?(query_params)