From 75a9c95025308e2f92668d81d7edf6da395dfac3 Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Wed, 13 May 2026 15:11:59 +0200 Subject: [PATCH 1/9] [#73354] add deletion confirmation dialog for page links - https://community.openproject.org/work_packages/73354 - add confirmation dialog component - add delete action and controller - add action to permission - add page link data to page info object - add additional routes - NOT ADDED: deletion service --- ...ink_confirmation_dialog_component.html.erb | 36 +++++++++++ ...page_link_confirmation_dialog_component.rb | 61 +++++++++++++++++++ .../wikis/page_link_component.html.erb | 7 +-- .../components/wikis/page_link_component.rb | 32 +++++++++- ...b => inline_page_link_macro_controller.rb} | 2 +- .../wikis/relation_page_link_controller.rb | 54 ++++++++++++++++ .../wikis/adapters/results/page_info.rb | 6 +- .../internal/queries/relation_page_links.rb | 6 +- modules/wikis/config/locales/en.yml | 3 + modules/wikis/config/routes.rb | 8 ++- .../wikis/lib/open_project/wikis/engine.rb | 2 +- 11 files changed, 205 insertions(+), 12 deletions(-) create mode 100644 modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.html.erb create mode 100644 modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.rb rename modules/wikis/app/controllers/wikis/{page_link_controller.rb => inline_page_link_macro_controller.rb} (97%) create mode 100644 modules/wikis/app/controllers/wikis/relation_page_link_controller.rb diff --git a/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.html.erb b/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.html.erb new file mode 100644 index 000000000000..53efb1fdde4d --- /dev/null +++ b/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.html.erb @@ -0,0 +1,36 @@ +<%#-- copyright +OpenProject is an open source project management software. +Copyright (C) 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. + +++#%> + +<%= + render(Primer::OpenProject::DangerDialog.new(title: t(".title"), form_arguments:)) do |dialog| + dialog.with_confirmation_message do |message| + message.with_heading(tag: :h2) { t(".heading") } + end + end +%> diff --git a/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.rb b/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.rb new file mode 100644 index 000000000000..e527e1258099 --- /dev/null +++ b/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.rb @@ -0,0 +1,61 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 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 Wikis + class DeletePageLinkConfirmationDialogComponent < ApplicationComponent + include OpTurbo::Streamable + + def initialize(page_link:) + super + @page_link = page_link + end + + def form_arguments + { + action: page_link_url, + method: :delete + } + end + + private + + def page_link_url + url_helpers.project_work_package_relation_wiki_page_link_path(@page_link, work_package_id:, project_id:) + end + + def work_package_id + @page_link.linkable_id + end + + def project_id + @page_link.linkable.project_id + end + end +end diff --git a/modules/wikis/app/components/wikis/page_link_component.html.erb b/modules/wikis/app/components/wikis/page_link_component.html.erb index df6d99bec5f2..9a77c4af97b0 100644 --- a/modules/wikis/app/components/wikis/page_link_component.html.erb +++ b/modules/wikis/app/components/wikis/page_link_component.html.erb @@ -40,12 +40,7 @@ See COPYRIGHT and LICENSE files for more details. if show_action_menu? render(Primer::Alpha::ActionMenu.new) do |menu| menu.with_show_button(icon: :"kebab-horizontal", "aria-label": t(:label_more), scheme: :invisible) - - if actions.include?(:remove) - menu.with_item(label: t(".remove"), scheme: :danger) do |item| - item.with_leading_visual_icon(icon: :trash) - end - end + menu_items(menu) end end %> diff --git a/modules/wikis/app/components/wikis/page_link_component.rb b/modules/wikis/app/components/wikis/page_link_component.rb index 521db5bf1ee4..e77772fc25fc 100644 --- a/modules/wikis/app/components/wikis/page_link_component.rb +++ b/modules/wikis/app/components/wikis/page_link_component.rb @@ -54,7 +54,37 @@ def page_href end def show_action_menu? - actions.any? + page_info_result.success? && actions.any? + end + + def menu_items(menu) + if actions.include?(:remove) + deletion_action_item(menu) + end + end + + private + + def page_link + page_info_result.value!.page_link + end + + def work_package_id = page_link.linkable_id + + def project_id = page_link.linkable.project_id + + def deletion_action_item(menu) + href = url_helpers.confirm_delete_project_work_package_relation_wiki_page_link_path(page_link, + work_package_id:, + project_id:) + + menu.with_item(label: t(".remove"), + scheme: :danger, + tag: :a, + href:, + content_arguments: { data: { controller: "async-dialog" } }) do |item| + item.with_leading_visual_icon(icon: :trash) + end end end end diff --git a/modules/wikis/app/controllers/wikis/page_link_controller.rb b/modules/wikis/app/controllers/wikis/inline_page_link_macro_controller.rb similarity index 97% rename from modules/wikis/app/controllers/wikis/page_link_controller.rb rename to modules/wikis/app/controllers/wikis/inline_page_link_macro_controller.rb index 535461e389d8..2c81ca3d27e6 100644 --- a/modules/wikis/app/controllers/wikis/page_link_controller.rb +++ b/modules/wikis/app/controllers/wikis/inline_page_link_macro_controller.rb @@ -29,7 +29,7 @@ #++ module Wikis - class PageLinkController < ApplicationController + class InlinePageLinkMacroController < ApplicationController include Dry::Monads[:result] before_action :find_provider diff --git a/modules/wikis/app/controllers/wikis/relation_page_link_controller.rb b/modules/wikis/app/controllers/wikis/relation_page_link_controller.rb new file mode 100644 index 000000000000..c5d81fefaef6 --- /dev/null +++ b/modules/wikis/app/controllers/wikis/relation_page_link_controller.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 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 Wikis + class RelationPageLinkController < ApplicationController + include OpTurbo::ComponentStream + + before_action :find_page_link + before_action :authorize, except: %i[confirm_delete] + + no_authorization_required! :confirm_delete + + def destroy + # TODO: implement delete service + end + + def confirm_delete + respond_with_dialog(DeletePageLinkConfirmationDialogComponent.new(page_link: @page_link)) + end + + private + + def find_page_link + @page_link = RelationPageLink.find(params[:id]) + end + end +end diff --git a/modules/wikis/app/models/wikis/adapters/results/page_info.rb b/modules/wikis/app/models/wikis/adapters/results/page_info.rb index e887846e2402..d6acceaf444e 100644 --- a/modules/wikis/app/models/wikis/adapters/results/page_info.rb +++ b/modules/wikis/app/models/wikis/adapters/results/page_info.rb @@ -29,5 +29,9 @@ #++ module Wikis::Adapters::Results - PageInfo = Data.define(:identifier, :provider, :title, :href) + PageInfo = Data.define(:identifier, :provider, :title, :href, :page_link) do + def initialize(identifier:, provider:, title:, href:, page_link: nil) + super + end + end end diff --git a/modules/wikis/app/services/wikis/adapters/providers/internal/queries/relation_page_links.rb b/modules/wikis/app/services/wikis/adapters/providers/internal/queries/relation_page_links.rb index c7deeaaf9b39..23350823a43d 100644 --- a/modules/wikis/app/services/wikis/adapters/providers/internal/queries/relation_page_links.rb +++ b/modules/wikis/app/services/wikis/adapters/providers/internal/queries/relation_page_links.rb @@ -38,7 +38,11 @@ def call(input_data:, auth_strategy:) page_link_infos = provider.page_links .merge(RelationPageLink.all) .where(linkable: input_data.linkable) - .map { |page_link| page_info(identifier: page_link.identifier, auth_strategy:) } + .map do |page_link| + page_info(identifier: page_link.identifier, auth_strategy:).fmap do |page_info| + page_info.with(page_link:) + end + end success(page_link_infos) end diff --git a/modules/wikis/config/locales/en.yml b/modules/wikis/config/locales/en.yml index ad8a3c5320d0..033228ca422d 100644 --- a/modules/wikis/config/locales/en.yml +++ b/modules/wikis/config/locales/en.yml @@ -32,6 +32,9 @@ en: done_continue: Done, continue save_and_continue: Save and continue wiki_page: Wiki page + delete_page_link_confirmation_dialog_component: + title: Delete related wiki page link + heading: Delete related wiki page link? work_package_wikis_tab_component: inline_page_links: Inline page links referencing_pages: Referenced in diff --git a/modules/wikis/config/routes.rb b/modules/wikis/config/routes.rb index ead743ca1915..5fde4ef61e34 100644 --- a/modules/wikis/config/routes.rb +++ b/modules/wikis/config/routes.rb @@ -44,7 +44,7 @@ end end - resource :wiki_page_link_macro, controller: "wikis/page_link" do + resource :wiki_page_link_macro, controller: "wikis/inline_page_link_macro", only: [] do get :load end @@ -55,6 +55,12 @@ resources :tab, only: %i[index], controller: "work_package_wikis_tab", as: "wikis_tab" end end + + resources :relation_wiki_page_links, only: %i[destroy], controller: "wikis/relation_page_link" do + member do + get :confirm_delete + end + end end end end diff --git a/modules/wikis/lib/open_project/wikis/engine.rb b/modules/wikis/lib/open_project/wikis/engine.rb index aa9cd4e8ce81..15c219692b26 100644 --- a/modules/wikis/lib/open_project/wikis/engine.rb +++ b/modules/wikis/lib/open_project/wikis/engine.rb @@ -72,7 +72,7 @@ class Engine < ::Rails::Engine register "openproject-wikis", author_url: "https://openproject.org" do project_module :work_package_tracking do permission :manage_wiki_page_links, - {}, + { "wikis/relation_page_link": %i[destroy] }, permissible_on: :project, dependencies: %i[edit_work_packages], contract_actions: { wiki_page_links: %i[manage] } From 3ea01ef5676e13b22c40ce8d0c79aacb502f6d2b Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Tue, 19 May 2026 13:44:25 +0200 Subject: [PATCH 2/9] [#73354] move relation page link routes out of wp context --- ...delete_page_link_confirmation_dialog_component.rb | 10 +--------- .../app/components/wikis/page_link_component.rb | 8 +------- .../wikis/relation_page_link_controller.rb | 8 ++++---- modules/wikis/config/routes.rb | 12 ++++++------ 4 files changed, 12 insertions(+), 26 deletions(-) diff --git a/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.rb b/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.rb index e527e1258099..c0d48e303dee 100644 --- a/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.rb +++ b/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.rb @@ -47,15 +47,7 @@ def form_arguments private def page_link_url - url_helpers.project_work_package_relation_wiki_page_link_path(@page_link, work_package_id:, project_id:) - end - - def work_package_id - @page_link.linkable_id - end - - def project_id - @page_link.linkable.project_id + url_helpers.relation_wiki_page_link_path(@page_link) end end end diff --git a/modules/wikis/app/components/wikis/page_link_component.rb b/modules/wikis/app/components/wikis/page_link_component.rb index e77772fc25fc..1da9dfdce3ba 100644 --- a/modules/wikis/app/components/wikis/page_link_component.rb +++ b/modules/wikis/app/components/wikis/page_link_component.rb @@ -69,14 +69,8 @@ def page_link page_info_result.value!.page_link end - def work_package_id = page_link.linkable_id - - def project_id = page_link.linkable.project_id - def deletion_action_item(menu) - href = url_helpers.confirm_delete_project_work_package_relation_wiki_page_link_path(page_link, - work_package_id:, - project_id:) + href = url_helpers.confirm_delete_dialog_relation_wiki_page_link_path(page_link) menu.with_item(label: t(".remove"), scheme: :danger, diff --git a/modules/wikis/app/controllers/wikis/relation_page_link_controller.rb b/modules/wikis/app/controllers/wikis/relation_page_link_controller.rb index c5d81fefaef6..629400eeea60 100644 --- a/modules/wikis/app/controllers/wikis/relation_page_link_controller.rb +++ b/modules/wikis/app/controllers/wikis/relation_page_link_controller.rb @@ -33,22 +33,22 @@ class RelationPageLinkController < ApplicationController include OpTurbo::ComponentStream before_action :find_page_link - before_action :authorize, except: %i[confirm_delete] + before_action :authorize, except: %i[confirm_delete_dialog] - no_authorization_required! :confirm_delete + no_authorization_required! :confirm_delete_dialog def destroy # TODO: implement delete service end - def confirm_delete + def confirm_delete_dialog respond_with_dialog(DeletePageLinkConfirmationDialogComponent.new(page_link: @page_link)) end private def find_page_link - @page_link = RelationPageLink.find(params[:id]) + @page_link = RelationPageLink.find(params.expect(:id)) end end end diff --git a/modules/wikis/config/routes.rb b/modules/wikis/config/routes.rb index 5fde4ef61e34..0dd92d5eae4c 100644 --- a/modules/wikis/config/routes.rb +++ b/modules/wikis/config/routes.rb @@ -48,6 +48,12 @@ get :load end + resources :relation_wiki_page_links, only: %i[destroy], controller: "wikis/relation_page_link" do + member do + get :confirm_delete_dialog + end + end + resources :projects, only: %i[] do resources :work_packages, only: %i[] do resources :wikis, only: %i[] do @@ -55,12 +61,6 @@ resources :tab, only: %i[index], controller: "work_package_wikis_tab", as: "wikis_tab" end end - - resources :relation_wiki_page_links, only: %i[destroy], controller: "wikis/relation_page_link" do - member do - get :confirm_delete - end - end end end end From 780aa63668213ddbdfc7cd9a8b8f0da1f856e5c6 Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Tue, 19 May 2026 15:02:04 +0200 Subject: [PATCH 3/9] [#73354] fix component test --- .../components/wikis/relation_page_links_component_spec.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb b/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb index 82afda088312..3fb0ce62e667 100644 --- a/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb +++ b/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb @@ -87,12 +87,14 @@ end context "when the user has a token and there are page links" do + let(:page_link) { create(:relation_wiki_page_link, linkable: work_package, provider:) } let(:page_info) do Wikis::Adapters::Results::PageInfo.new( identifier: "MyPage", provider:, title: "My Wiki Page", - href: "https://wiki.example.com/MyPage" + href: "https://wiki.example.com/MyPage", + page_link: ) end From 611230c03c778907f92bf077155c1df6403d4719 Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Tue, 19 May 2026 17:03:00 +0200 Subject: [PATCH 4/9] [#73354] fixed naming of template --- .../wikis/{page_link => inline_page_link_macro}/load.html.erb | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename modules/wikis/app/views/wikis/{page_link => inline_page_link_macro}/load.html.erb (100%) diff --git a/modules/wikis/app/views/wikis/page_link/load.html.erb b/modules/wikis/app/views/wikis/inline_page_link_macro/load.html.erb similarity index 100% rename from modules/wikis/app/views/wikis/page_link/load.html.erb rename to modules/wikis/app/views/wikis/inline_page_link_macro/load.html.erb From a78ebd359a80aca0162a95208940eb13483ce95c Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Wed, 20 May 2026 16:12:30 +0200 Subject: [PATCH 5/9] [#73354] simple renaming of controllers, components, and routes --- ..._relation_page_link_confirmation_dialog.html.erb} | 0 ...delete_relation_page_link_confirmation_dialog.rb} | 2 +- .../app/components/wikis/page_link_component.rb | 9 +++++++++ ...o_controller.rb => page_link_macro_controller.rb} | 2 +- ...ntroller.rb => relation_page_links_controller.rb} | 4 ++-- .../views/wikis/admin/wiki_providers/edit.html.erb | 12 +++++++----- modules/wikis/config/locales/en.yml | 2 +- modules/wikis/config/routes.rb | 4 ++-- 8 files changed, 23 insertions(+), 12 deletions(-) rename modules/wikis/app/components/wikis/{delete_page_link_confirmation_dialog_component.html.erb => delete_relation_page_link_confirmation_dialog.html.erb} (100%) rename modules/wikis/app/components/wikis/{delete_page_link_confirmation_dialog_component.rb => delete_relation_page_link_confirmation_dialog.rb} (95%) rename modules/wikis/app/controllers/wikis/{inline_page_link_macro_controller.rb => page_link_macro_controller.rb} (97%) rename modules/wikis/app/controllers/wikis/{relation_page_link_controller.rb => relation_page_links_controller.rb} (91%) diff --git a/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.html.erb b/modules/wikis/app/components/wikis/delete_relation_page_link_confirmation_dialog.html.erb similarity index 100% rename from modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.html.erb rename to modules/wikis/app/components/wikis/delete_relation_page_link_confirmation_dialog.html.erb diff --git a/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.rb b/modules/wikis/app/components/wikis/delete_relation_page_link_confirmation_dialog.rb similarity index 95% rename from modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.rb rename to modules/wikis/app/components/wikis/delete_relation_page_link_confirmation_dialog.rb index c0d48e303dee..2f492c197ab7 100644 --- a/modules/wikis/app/components/wikis/delete_page_link_confirmation_dialog_component.rb +++ b/modules/wikis/app/components/wikis/delete_relation_page_link_confirmation_dialog.rb @@ -29,7 +29,7 @@ #++ module Wikis - class DeletePageLinkConfirmationDialogComponent < ApplicationComponent + class DeleteRelationPageLinkConfirmationDialog < ApplicationComponent include OpTurbo::Streamable def initialize(page_link:) diff --git a/modules/wikis/app/components/wikis/page_link_component.rb b/modules/wikis/app/components/wikis/page_link_component.rb index 1da9dfdce3ba..c3305c65b79f 100644 --- a/modules/wikis/app/components/wikis/page_link_component.rb +++ b/modules/wikis/app/components/wikis/page_link_component.rb @@ -69,6 +69,10 @@ def page_link page_info_result.value!.page_link end + def project + page_link.linkable.project + end + def deletion_action_item(menu) href = url_helpers.confirm_delete_dialog_relation_wiki_page_link_path(page_link) @@ -76,9 +80,14 @@ def deletion_action_item(menu) scheme: :danger, tag: :a, href:, + disabled: !user_allowed_to_delete?, content_arguments: { data: { controller: "async-dialog" } }) do |item| item.with_leading_visual_icon(icon: :trash) end end + + def user_allowed_to_delete? + helpers.current_user.allowed_in_project?(:manage_wiki_page_links, project) + end end end diff --git a/modules/wikis/app/controllers/wikis/inline_page_link_macro_controller.rb b/modules/wikis/app/controllers/wikis/page_link_macro_controller.rb similarity index 97% rename from modules/wikis/app/controllers/wikis/inline_page_link_macro_controller.rb rename to modules/wikis/app/controllers/wikis/page_link_macro_controller.rb index 16720fb21f5b..35101548749e 100644 --- a/modules/wikis/app/controllers/wikis/inline_page_link_macro_controller.rb +++ b/modules/wikis/app/controllers/wikis/page_link_macro_controller.rb @@ -29,7 +29,7 @@ #++ module Wikis - class InlinePageLinkMacroController < ApplicationController + class PageLinkMacroController < ApplicationController include Dry::Monads[:result] # The view component shown in `load` will be rendered regardless of the current user's authorization status. diff --git a/modules/wikis/app/controllers/wikis/relation_page_link_controller.rb b/modules/wikis/app/controllers/wikis/relation_page_links_controller.rb similarity index 91% rename from modules/wikis/app/controllers/wikis/relation_page_link_controller.rb rename to modules/wikis/app/controllers/wikis/relation_page_links_controller.rb index 629400eeea60..a3c2c9207bc8 100644 --- a/modules/wikis/app/controllers/wikis/relation_page_link_controller.rb +++ b/modules/wikis/app/controllers/wikis/relation_page_links_controller.rb @@ -29,7 +29,7 @@ #++ module Wikis - class RelationPageLinkController < ApplicationController + class RelationPageLinksController < ApplicationController include OpTurbo::ComponentStream before_action :find_page_link @@ -42,7 +42,7 @@ def destroy end def confirm_delete_dialog - respond_with_dialog(DeletePageLinkConfirmationDialogComponent.new(page_link: @page_link)) + respond_with_dialog(DeleteRelationPageLinkConfirmationDialog.new(page_link: @page_link)) end private diff --git a/modules/wikis/app/views/wikis/admin/wiki_providers/edit.html.erb b/modules/wikis/app/views/wikis/admin/wiki_providers/edit.html.erb index 2802c6400f43..8c9a161588be 100644 --- a/modules/wikis/app/views/wikis/admin/wiki_providers/edit.html.erb +++ b/modules/wikis/app/views/wikis/admin/wiki_providers/edit.html.erb @@ -35,11 +35,13 @@ See COPYRIGHT and LICENSE files for more details. <% header.with_title { page_title } %> <% - breadcrumbs = [{ href: admin_index_path, text: t(:label_administration) }, - { href: admin_settings_wiki_providers_path, text: t(:project_module_wiki_platforms) }, - page_title] - - header.with_breadcrumbs(breadcrumbs) + header.with_breadcrumbs( + [ + { href: admin_index_path, text: t(:label_administration) }, + { href: admin_settings_wiki_providers_path, text: t(:project_module_wiki_platforms) }, + page_title + ] + ) %> <%# TODO: temp — move to a dedicated per-user connection status component once designed %> diff --git a/modules/wikis/config/locales/en.yml b/modules/wikis/config/locales/en.yml index b742d5ca55b8..6584f37749b2 100644 --- a/modules/wikis/config/locales/en.yml +++ b/modules/wikis/config/locales/en.yml @@ -37,7 +37,7 @@ en: done_continue: Done, continue save_and_continue: Save and continue wiki_page: Wiki page - delete_page_link_confirmation_dialog_component: + delete_relation_page_link_confirmation_dialog: title: Delete related wiki page link heading: Delete related wiki page link? health_checks: diff --git a/modules/wikis/config/routes.rb b/modules/wikis/config/routes.rb index 0c0f91e96160..4a8ac57407f4 100644 --- a/modules/wikis/config/routes.rb +++ b/modules/wikis/config/routes.rb @@ -49,11 +49,11 @@ end end - resource :wiki_page_link_macro, controller: "wikis/inline_page_link_macro", only: [] do + resource :wiki_page_link_macro, controller: "wikis/page_link_macro", only: [] do get :load end - resources :relation_wiki_page_links, only: %i[destroy], controller: "wikis/relation_page_link" do + resources :relation_wiki_page_links, only: %i[destroy], controller: "wikis/relation_page_links" do member do get :confirm_delete_dialog end From e9c7a8e51222931e8e523b8540f8d7cc2948dd6c Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Wed, 20 May 2026 18:20:09 +0200 Subject: [PATCH 6/9] [#73354] introduced page link aggregate - revert page link addition to page info result --- .../components/wikis/page_link_component.rb | 13 ++++---- .../relation_page_links_component.html.erb | 27 +++++++-------- .../wikis/relation_page_links_component.rb | 4 +-- .../wikis/adapters/results/page_info.rb | 6 +--- .../adapters/results/page_link_aggregate.rb | 33 +++++++++++++++++++ .../internal/queries/relation_page_links.rb | 15 ++++----- .../xwiki/queries/relation_page_links.rb | 24 +++++--------- .../app/services/wikis/page_link_service.rb | 10 +++--- .../wikis/lib/open_project/wikis/engine.rb | 4 +-- 9 files changed, 78 insertions(+), 58 deletions(-) create mode 100644 modules/wikis/app/models/wikis/adapters/results/page_link_aggregate.rb diff --git a/modules/wikis/app/components/wikis/page_link_component.rb b/modules/wikis/app/components/wikis/page_link_component.rb index c3305c65b79f..17f5570ced68 100644 --- a/modules/wikis/app/components/wikis/page_link_component.rb +++ b/modules/wikis/app/components/wikis/page_link_component.rb @@ -37,8 +37,9 @@ class PageLinkComponent < ApplicationComponent attr_reader :actions - def initialize(model = nil, actions: [], **) + def initialize(model = nil, actions: [], page_link: nil, **) @actions = actions + @page_link = page_link super(model, **) end @@ -65,16 +66,14 @@ def menu_items(menu) private - def page_link - page_info_result.value!.page_link - end - def project - page_link.linkable.project + @page_link&.linkable&.project end def deletion_action_item(menu) - href = url_helpers.confirm_delete_dialog_relation_wiki_page_link_path(page_link) + return if @page_link.nil? + + href = url_helpers.confirm_delete_dialog_relation_wiki_page_link_path(@page_link) menu.with_item(label: t(".remove"), scheme: :danger, diff --git a/modules/wikis/app/components/wikis/relation_page_links_component.html.erb b/modules/wikis/app/components/wikis/relation_page_links_component.html.erb index 183fbb6b45ce..60fb4d2480be 100644 --- a/modules/wikis/app/components/wikis/relation_page_links_component.html.erb +++ b/modules/wikis/app/components/wikis/relation_page_links_component.html.erb @@ -33,7 +33,7 @@ See COPYRIGHT and LICENSE files for more details. flex_layout(align_items: :center, justify_content: :space_between) do |header| header.with_column do concat(render(Primer::Beta::Text.new(font_weight: :bold, mr: 2)) { provider.name }) - concat(render(Primer::Beta::Counter.new(count: page_link_infos.count, round: true, scheme: :primary))) + concat(render(Primer::Beta::Counter.new(count: page_links.count, round: true, scheme: :primary))) end header.with_column do render(Primer::Alpha::ActionMenu.new) do |menu| @@ -52,12 +52,9 @@ See COPYRIGHT and LICENSE files for more details. if !user_connected? box.with_row do - render(Wikis::OAuthLoginComponent.new( - provider, - return_url: work_package_url(@work_package, tab: :wikis) - )) + render(Wikis::OAuthLoginComponent.new(provider, return_url: work_package_url(@work_package, tab: :wikis))) end - elsif page_link_infos.empty? + elsif page_links.empty? box.with_row do if user_connected? render(Primer::Beta::Blankslate.new(border: false)) do |blankslate| @@ -65,16 +62,20 @@ See COPYRIGHT and LICENSE files for more details. blankslate.with_description { t(".empty_text") } end else - render(Wikis::OAuthLoginComponent.new( - provider, - return_url: work_package_url(@work_package, tab: :wikis) - ) - ) + render(Wikis::OAuthLoginComponent.new(provider, return_url: work_package_url(@work_package, tab: :wikis))) end end else - page_link_infos.each do |info| - box.with_row { render(Wikis::PageLinkComponent.new(info, actions: [:remove])) } + page_links.each do |page_link_aggregate| + box.with_row do + render( + Wikis::PageLinkComponent.new( + page_link_aggregate.page_info_result, + actions: [:remove], + page_link: page_link_aggregate.page_link + ) + ) + end end end end diff --git a/modules/wikis/app/components/wikis/relation_page_links_component.rb b/modules/wikis/app/components/wikis/relation_page_links_component.rb index 7baa2f950f4b..02686e724140 100644 --- a/modules/wikis/app/components/wikis/relation_page_links_component.rb +++ b/modules/wikis/app/components/wikis/relation_page_links_component.rb @@ -40,8 +40,8 @@ def initialize(model = nil, work_package: nil, **) super(model, **) end - def page_link_infos - @page_link_infos ||= page_link_service.relation_page_link_infos_for(provider:, linkable: @work_package) + def page_links + @page_links ||= page_link_service.relation_page_links_for(provider:, linkable: @work_package) end def user_connected? diff --git a/modules/wikis/app/models/wikis/adapters/results/page_info.rb b/modules/wikis/app/models/wikis/adapters/results/page_info.rb index d6acceaf444e..e887846e2402 100644 --- a/modules/wikis/app/models/wikis/adapters/results/page_info.rb +++ b/modules/wikis/app/models/wikis/adapters/results/page_info.rb @@ -29,9 +29,5 @@ #++ module Wikis::Adapters::Results - PageInfo = Data.define(:identifier, :provider, :title, :href, :page_link) do - def initialize(identifier:, provider:, title:, href:, page_link: nil) - super - end - end + PageInfo = Data.define(:identifier, :provider, :title, :href) end diff --git a/modules/wikis/app/models/wikis/adapters/results/page_link_aggregate.rb b/modules/wikis/app/models/wikis/adapters/results/page_link_aggregate.rb new file mode 100644 index 000000000000..7cb17c2cf454 --- /dev/null +++ b/modules/wikis/app/models/wikis/adapters/results/page_link_aggregate.rb @@ -0,0 +1,33 @@ +# frozen_string_literal: true + +#-- copyright +# OpenProject is an open source project management software. +# Copyright (C) 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 Wikis::Adapters::Results + PageLinkAggregate = Data.define(:page_info_result, :page_link) +end diff --git a/modules/wikis/app/services/wikis/adapters/providers/internal/queries/relation_page_links.rb b/modules/wikis/app/services/wikis/adapters/providers/internal/queries/relation_page_links.rb index 23350823a43d..bce9ae211596 100644 --- a/modules/wikis/app/services/wikis/adapters/providers/internal/queries/relation_page_links.rb +++ b/modules/wikis/app/services/wikis/adapters/providers/internal/queries/relation_page_links.rb @@ -35,16 +35,15 @@ module Internal module Queries class RelationPageLinks < BaseQuery def call(input_data:, auth_strategy:) - page_link_infos = provider.page_links - .merge(RelationPageLink.all) - .where(linkable: input_data.linkable) - .map do |page_link| - page_info(identifier: page_link.identifier, auth_strategy:).fmap do |page_info| - page_info.with(page_link:) - end + page_links = provider.page_links + .merge(RelationPageLink.all) + .where(linkable: input_data.linkable) + .map do |page_link| + page_info_result = page_info(identifier: page_link.identifier, auth_strategy:) + Results::PageLinkAggregate.new(page_info_result:, page_link:) end - success(page_link_infos) + success(page_links) end end end diff --git a/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/relation_page_links.rb b/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/relation_page_links.rb index ba56bc9cf986..519bd537c886 100644 --- a/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/relation_page_links.rb +++ b/modules/wikis/app/services/wikis/adapters/providers/xwiki/queries/relation_page_links.rb @@ -34,24 +34,16 @@ module Providers module XWiki module Queries class RelationPageLinks < BaseQuery - def call(input_data:, **) # rubocop:disable Metrics/AbcSize - # TODO: use real API endpoints once available - - title = [ - "What makes XWiki special?", - "API documentation", - "A brief introduction on configuring your own XWiki instance and connect it to OpenProject." - ] - - results = [] - - if input_data.linkable.id % 2 == 1 - results << Success(Results::PageInfo.new(identifier: "1337", provider:, title: title.sample, href: "#")) - results << Success(Results::PageInfo.new(identifier: "1338", provider:, title: title.sample, href: "#")) - results << Success(Results::PageInfo.new(identifier: "1338", provider:, title: title.sample, href: "#")) + def call(input_data:, auth_strategy:) + page_links = provider.page_links + .merge(RelationPageLink.all) + .where(linkable: input_data.linkable) + .map do |page_link| + page_info_result = page_info(identifier: page_link.identifier, auth_strategy:) + Results::PageLinkAggregate.new(page_info_result:, page_link:) end - success(results) + success(page_links) end end end diff --git a/modules/wikis/app/services/wikis/page_link_service.rb b/modules/wikis/app/services/wikis/page_link_service.rb index d4616369fc79..e029f3b34d7c 100644 --- a/modules/wikis/app/services/wikis/page_link_service.rb +++ b/modules/wikis/app/services/wikis/page_link_service.rb @@ -33,20 +33,20 @@ class PageLinkService include Dry::Monads[:result] def count(linkable) - relation_page_links = Provider.enabled.sum { |provider| relation_page_link_infos_for(provider:, linkable:).size } + relation_page_links = Provider.enabled.sum { |provider| relation_page_links_for(provider:, linkable:).size } relation_page_links + inline_page_link_infos_for(linkable:).size + referencing_wiki_page_infos_for(linkable:).size end - def relation_page_link_infos_for(provider:, linkable:) - Adapters::Input::RelationPageLinks.build(linkable:).bind do |input| + def relation_page_links_for(provider:, linkable:) + Adapters::Input::RelationPageLinks.build(linkable:).bind do |input_data| provider.auth_strategy_for(User.current).bind do |auth_strategy| provider.resolve("queries.relation_page_links") - .call(input_data: input, auth_strategy:) + .call(input_data:, auth_strategy:) .either( - ->(page_link_infos) { page_link_infos }, + ->(page_links) { page_links }, -> { [] } ) end diff --git a/modules/wikis/lib/open_project/wikis/engine.rb b/modules/wikis/lib/open_project/wikis/engine.rb index 137d3301f639..ab1ebbab1e84 100644 --- a/modules/wikis/lib/open_project/wikis/engine.rb +++ b/modules/wikis/lib/open_project/wikis/engine.rb @@ -72,7 +72,7 @@ class Engine < ::Rails::Engine register "openproject-wikis", author_url: "https://openproject.org" do project_module :work_package_tracking do permission :manage_wiki_page_links, - { "wikis/relation_page_link": %i[destroy] }, + { "wikis/relation_page_links": %i[destroy] }, permissible_on: :project, dependencies: %i[edit_work_packages], contract_actions: { wiki_page_links: %i[manage] } @@ -94,7 +94,7 @@ class Engine < ::Rails::Engine { controller: "/wikis/admin/wiki_providers", action: :index }, if: ->(_) { OpenProject::FeatureDecisions.wiki_enhancements_active? }, caption: :project_module_wiki_platforms, - icon: "book" + icon: :book end patch_with_namespace :WikiPages, :CreateService From 6c8c8af771d1952b81ebf8b9efa69651ed623f77 Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Thu, 21 May 2026 10:47:50 +0200 Subject: [PATCH 7/9] [#73354] hide delete action instead of disabling - add authorisation check for deletion action --- modules/wikis/app/components/wikis/page_link_component.rb | 2 +- .../app/controllers/wikis/relation_page_links_controller.rb | 4 +--- modules/wikis/lib/open_project/wikis/engine.rb | 2 +- 3 files changed, 3 insertions(+), 5 deletions(-) diff --git a/modules/wikis/app/components/wikis/page_link_component.rb b/modules/wikis/app/components/wikis/page_link_component.rb index 17f5570ced68..a94c3ffa76db 100644 --- a/modules/wikis/app/components/wikis/page_link_component.rb +++ b/modules/wikis/app/components/wikis/page_link_component.rb @@ -72,6 +72,7 @@ def project def deletion_action_item(menu) return if @page_link.nil? + return unless user_allowed_to_delete? href = url_helpers.confirm_delete_dialog_relation_wiki_page_link_path(@page_link) @@ -79,7 +80,6 @@ def deletion_action_item(menu) scheme: :danger, tag: :a, href:, - disabled: !user_allowed_to_delete?, content_arguments: { data: { controller: "async-dialog" } }) do |item| item.with_leading_visual_icon(icon: :trash) end diff --git a/modules/wikis/app/controllers/wikis/relation_page_links_controller.rb b/modules/wikis/app/controllers/wikis/relation_page_links_controller.rb index a3c2c9207bc8..895499df76a3 100644 --- a/modules/wikis/app/controllers/wikis/relation_page_links_controller.rb +++ b/modules/wikis/app/controllers/wikis/relation_page_links_controller.rb @@ -33,9 +33,7 @@ class RelationPageLinksController < ApplicationController include OpTurbo::ComponentStream before_action :find_page_link - before_action :authorize, except: %i[confirm_delete_dialog] - - no_authorization_required! :confirm_delete_dialog + before_action :authorize def destroy # TODO: implement delete service diff --git a/modules/wikis/lib/open_project/wikis/engine.rb b/modules/wikis/lib/open_project/wikis/engine.rb index ab1ebbab1e84..5d70952d9aaf 100644 --- a/modules/wikis/lib/open_project/wikis/engine.rb +++ b/modules/wikis/lib/open_project/wikis/engine.rb @@ -72,7 +72,7 @@ class Engine < ::Rails::Engine register "openproject-wikis", author_url: "https://openproject.org" do project_module :work_package_tracking do permission :manage_wiki_page_links, - { "wikis/relation_page_links": %i[destroy] }, + { "wikis/relation_page_links": %i[destroy confirm_delete_dialog] }, permissible_on: :project, dependencies: %i[edit_work_packages], contract_actions: { wiki_page_links: %i[manage] } From 4d61d1d325a91e3daf8b2669b43d2fbddd7d6ed4 Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Thu, 21 May 2026 10:51:37 +0200 Subject: [PATCH 8/9] [#73354] removed unused test setup --- .../spec/components/wikis/relation_page_links_component_spec.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb b/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb index 073878b625ff..bbc4f3943348 100644 --- a/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb +++ b/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb @@ -87,7 +87,6 @@ end context "when the user has a token and there are page links" do - let(:page_link) { create(:relation_wiki_page_link, linkable: work_package, provider:) } let(:page_info) do Wikis::Adapters::Results::PageInfo.new( identifier: "MyPage", From cada809a24f4f959b58ad9eab33a6e9bb65e4906 Mon Sep 17 00:00:00 2001 From: Eric Schubert Date: Thu, 21 May 2026 11:59:51 +0200 Subject: [PATCH 9/9] [#73354] fixed current unit tests --- .../relation_page_links_component_spec.rb | 9 ++++--- .../queries/relation_page_links_query_spec.rb | 25 +++++++++++-------- 2 files changed, 21 insertions(+), 13 deletions(-) diff --git a/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb b/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb index bbc4f3943348..38f4e54c6c34 100644 --- a/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb +++ b/modules/wikis/spec/components/wikis/relation_page_links_component_spec.rb @@ -37,7 +37,7 @@ let(:provider) { create(:xwiki_provider) } let(:oauth_client) { create(:oauth_client, integration: provider) } - let(:page_link_service) { instance_double(Wikis::PageLinkService, relation_page_link_infos_for: []) } + let(:page_link_service) { instance_double(Wikis::PageLinkService, relation_page_links_for: []) } subject(:render_component) { render_inline(described_class.new(provider, work_package:)) } @@ -87,6 +87,7 @@ end context "when the user has a token and there are page links" do + let(:page_link) { create(:relation_wiki_page_link, provider:, linkable: work_package) } let(:page_info) do Wikis::Adapters::Results::PageInfo.new( identifier: "MyPage", @@ -95,11 +96,13 @@ provider: ) end + let(:page_link_aggregate) do + Wikis::Adapters::Results::PageLinkAggregate.new(page_link:, page_info_result: Dry::Monads::Success(page_info)) + end before do allow(provider).to receive(:user_connected?).and_return(true) - allow(page_link_service).to receive(:relation_page_link_infos_for) - .and_return([Dry::Monads::Success(page_info)]) + allow(page_link_service).to receive(:relation_page_links_for).and_return([page_link_aggregate]) render_component end diff --git a/modules/wikis/spec/services/wikis/adapters/providers/internal/queries/relation_page_links_query_spec.rb b/modules/wikis/spec/services/wikis/adapters/providers/internal/queries/relation_page_links_query_spec.rb index 2127c5ac8b03..694f97638674 100644 --- a/modules/wikis/spec/services/wikis/adapters/providers/internal/queries/relation_page_links_query_spec.rb +++ b/modules/wikis/spec/services/wikis/adapters/providers/internal/queries/relation_page_links_query_spec.rb @@ -59,15 +59,18 @@ it { is_expected.to be_success } - it "returns the page info results of the wiki pages" do + it "returns aggregates with the page info results of the wiki pages and the page links" do result = subject.value! expect(result.size).to eq(2) - expect(result[0]).to be_success - expect(result[0].value!.title).to eq(wiki_page.title) - expect(result[0].value!.href).to eq("/projects/#{project.identifier}/wiki/#{wiki_page.slug}") + expect(result[0]).to be_a(Wikis::Adapters::Results::PageLinkAggregate) + expect(result[0].page_info_result.value!.title).to eq(wiki_page.title) + expect(result[0].page_info_result.value!.href).to eq("/projects/#{project.identifier}/wiki/#{wiki_page.slug}") + expect(result[0].page_link).to eq(link_to_existing_page) - expect(result[1]).to be_failure - expect(result[1].failure.code).to eq(:not_found) + expect(result[1]).to be_a(Wikis::Adapters::Results::PageLinkAggregate) + expect(result[1].page_info_result).to be_failure + expect(result[1].page_info_result.failure.code).to eq(:not_found) + expect(result[1].page_link).to eq(link_to_non_existing_page) end context "when user can't see wiki pages" do @@ -77,10 +80,12 @@ result = subject.value! expect(result.size).to eq(2) - expect(result[0]).to be_failure - expect(result[0].failure.code).to eq(:not_found) - expect(result[1]).to be_failure - expect(result[1].failure.code).to eq(:not_found) + expect(result[0].page_info_result).to be_failure + expect(result[0].page_info_result.failure.code).to eq(:not_found) + expect(result[0].page_link).to eq(link_to_existing_page) + expect(result[1].page_info_result).to be_failure + expect(result[1].page_info_result.failure.code).to eq(:not_found) + expect(result[1].page_link).to eq(link_to_non_existing_page) end end end