Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@ See COPYRIGHT and LICENSE files for more details.
end
end

page_links.each do |page_link|
box.with_row { render(Wikis::PageLinkComponent.new(page_link)) }
page_links.each do |view_model|
box.with_row { render(Wikis::PageLinkComponent.new(view_model.page_info_result, source: view_model.source)) }
end
end
%>
21 changes: 16 additions & 5 deletions modules/wikis/app/components/wikis/page_link_component.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,22 @@ See COPYRIGHT and LICENSE files for more details.
%>

<%=
render(Primer::Alpha::StackItem.new(grow: true, classes: "ellipsis")) do
if error?
render(Primer::Beta::Text.new(color: :muted)) { page_title }
else
render(Primer::Beta::Link.new(href: page_href, scheme: :primary)) { page_title }
render(Primer::Alpha::StackItem.new(grow: true)) do
render(Primer::Alpha::Stack.new(direction: :horizontal, gap: :condensed, align: :center)) do
concat(
render(Primer::Beta::Truncate.new) do |truncate|
truncate.with_item(priority: true) do
if error?
render(Primer::Beta::Text.new(color: :muted)) { page_title }
else
render(Primer::Beta::Link.new(href: page_href, scheme: :primary)) { page_title }
end
end
end
)
if badge_label.present?
concat(render(Primer::Beta::Label.new(scheme: :secondary, test_selector: "wiki-page-link-source-badge")) { badge_label })
end
end
end
%>
Expand Down
9 changes: 7 additions & 2 deletions modules/wikis/app/components/wikis/page_link_component.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,20 @@ class PageLinkComponent < ApplicationComponent

alias_method :page_info_result, :model

attr_reader :actions
attr_reader :actions, :source

def initialize(model = nil, actions: [], page_link: nil, **)
def initialize(model = nil, actions: [], page_link: nil, source: nil, **)
@actions = actions
@page_link = page_link
@source = source

super(model, **)
end

def badge_label
I18n.t("wikis.page_links.source.parent") if source == :link
end

def page_title
page_info_result.either(
->(pi) { pi.title },
Expand Down
45 changes: 45 additions & 0 deletions modules/wikis/app/components/wikis/page_link_view_model.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# 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
# View model for a single row in a CollapsiblePageLinksComponent.
# Factory methods normalize each domain result into a common shape:
# a page_info_result plus the source that produced it (nil for inline links).
# Mapping source -> badge label and styling is the component's concern.
PageLinkViewModel = Data.define(:page_info_result, :source) do
def self.from_reference(result)
new(page_info_result: result.fmap(&:page_info), source: result.fmap(&:source).value_or(nil))
end

def self.from_inline(result)
new(page_info_result: result, source: nil)
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -48,10 +48,12 @@ def show_inline_and_references_section?

def inline_page_links
@inline_page_links ||= page_link_service.inline_page_link_infos_for(linkable: work_package)
.map { PageLinkViewModel.from_inline(it) }
end

def referencing_wiki_pages
@referencing_wiki_pages ||= page_link_service.referencing_wiki_page_infos_for(linkable: work_package)
.map { PageLinkViewModel.from_reference(it) }
end

private
Expand Down
36 changes: 36 additions & 0 deletions modules/wikis/app/models/wikis/adapters/results/page_reference.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# 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
# Pairs a PageInfo with how it relates to a given work package.
# source: :link — page has a structured WorkPackage link
# source: :mention — page mentions the WP in its content
PageReference = Data.define(:page_info, :source)
end
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,10 @@ def call(input_data:, auth_strategy:)
.merge(ReverseInlinePageLink.all)
.where(linkable: input_data.linkable)
.order(created_at: :desc)
.map { page_info(identifier: it.identifier, auth_strategy:) }
.map do |link|
page_info(identifier: link.identifier, auth_strategy:)
.fmap { Wikis::Adapters::Results::PageReference.new(page_info: it, source: :link) }
end
)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,15 +47,25 @@ def call(input_data:, auth_strategy:)
authenticated(auth_strategy) do |http|
fetch_reference_ids(http, input_data).bind do |reference_ids|
fetch_mention_ids(http, input_data).bind do |mention_ids|
ids = (reference_ids + mention_ids).uniq
success(ids.map { canonical_page_info(identifier: it, auth_strategy:) })
mention_only_ids = mention_ids - reference_ids
success(
canonical_pages(reference_ids, source: :link, auth_strategy:) +
canonical_pages(mention_only_ids, source: :mention, auth_strategy:)
)
end
end
end
end

private

def canonical_pages(ids, source:, auth_strategy:)
ids.map do |id|
canonical_page_info(identifier: id, auth_strategy:)
.fmap { Wikis::Adapters::Results::PageReference.new(page_info: it, source:) }
end
end

def fetch_reference_ids(http, input_data)
fetch_page_ids(http, rest_url("openproject/links/workPackages/#{input_data.linkable.id}"),
params: { number: MAXIMUM_RESULTS, withInstance: instance_id })
Expand Down
2 changes: 2 additions & 0 deletions modules/wikis/config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,8 @@ en:
page_access_forbidden: You do not have permission to access this wiki page
page_not_found: Linked wiki page no longer available
unexpected: An unexpected error occurred
source:
parent: As parent
provider_types:
xwiki:
name: XWiki
Expand Down
26 changes: 25 additions & 1 deletion modules/wikis/spec/components/wikis/page_link_component_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,17 +47,41 @@
let(:page_info_result) { Success(page_info) }
let(:permissions) { [:manage_wiki_page_links] }
let(:actions) { [] }
let(:source) { nil }

current_user { create(:user, member_with_permissions: { project => permissions }) }

subject(:render_component) { render_inline(described_class.new(page_info_result, actions:, page_link:)) }
subject(:render_component) { render_inline(described_class.new(page_info_result, actions:, page_link:, source:)) }

before { render_component }

it "renders the page link successfully" do
expect(page).to have_link(text: page_info.title, href: page_info.href)
end

context "when the page is referenced as a parent" do
let(:source) { :link }

it "renders the parent badge" do
expect(page).to have_test_selector("wiki-page-link-source-badge",
text: I18n.t("wikis.page_links.source.parent"))
end
end

context "when the page is referenced as a mention" do
let(:source) { :mention }

it "renders no badge" do
expect(page).not_to have_test_selector("wiki-page-link-source-badge")
end
end

context "when the page has no source" do
it "renders no badge" do
expect(page).not_to have_test_selector("wiki-page-link-source-badge")
end
end

context "when the page link has the remove action" do
let(:actions) { [:remove] }

Expand Down
116 changes: 116 additions & 0 deletions modules/wikis/spec/components/wikis/page_link_view_model_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
# 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.
#++

require "spec_helper"
require_module_spec_helper

RSpec.describe Wikis::PageLinkViewModel do
let(:provider) { build_stubbed(:internal_wiki_provider) }
let(:page_info) do
Wikis::Adapters::Results::PageInfo.new(
identifier: "42",
title: "Test page",
provider:,
href: "https://wiki.example.com/test"
)
end
let(:failure_result) { Failure(Wikis::Adapters::Results::Error.new(source: nil, code: :not_found)) }

describe ".from_reference" do
subject(:view_model) { described_class.from_reference(result) }

context "when the result is a link" do
let(:result) { Success(Wikis::Adapters::Results::PageReference.new(page_info:, source: :link)) }

it "extracts the page_info_result from the reference" do
expect(view_model.page_info_result).to be_success
expect(view_model.page_info_result.value!).to eq(page_info)
end

it "carries the link source" do
expect(view_model.source).to eq(:link)
end
end

context "when the result is a mention" do
let(:result) { Success(Wikis::Adapters::Results::PageReference.new(page_info:, source: :mention)) }

it "extracts the page_info_result from the reference" do
expect(view_model.page_info_result).to be_success
expect(view_model.page_info_result.value!).to eq(page_info)
end

it "carries the mention source" do
expect(view_model.source).to eq(:mention)
end
end

context "when the result is a failure" do
let(:result) { failure_result }

it "passes the failure through as page_info_result" do
expect(view_model.page_info_result).to be_failure
end

it "carries no source" do
expect(view_model.source).to be_nil
end
end
end

describe ".from_inline" do
subject(:view_model) { described_class.from_inline(result) }

context "when the result is a success" do
let(:result) { Success(page_info) }

it "passes the page_info_result through unchanged" do
expect(view_model.page_info_result).to be_success
expect(view_model.page_info_result.value!).to eq(page_info)
end

it "carries no source" do
expect(view_model.source).to be_nil
end
end

context "when the result is a failure" do
let(:result) { failure_result }

it "passes the failure through as page_info_result" do
expect(view_model.page_info_result).to be_failure
end

it "carries no source" do
expect(view_model.source).to be_nil
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,9 @@
it "returns pages indicated by reverse links" do
results = subject.value!
expect(results).to all(be_success)
infos = results.map(&:value!)
expect(infos.map(&:title)).to contain_exactly(wiki_page.title)
page_references = results.map(&:value!)
expect(page_references.map { it.page_info.title }).to contain_exactly(wiki_page.title)
expect(page_references.map(&:source)).to all(eq(:link))
end

context "when there are no reverse links" do
Expand Down
Loading
Loading