diff --git a/app/controllers/wiki_controller.rb b/app/controllers/wiki_controller.rb index 4e97b53cb491..95538c1a28ca 100644 --- a/app/controllers/wiki_controller.rb +++ b/app/controllers/wiki_controller.rb @@ -261,10 +261,10 @@ def history def diff if (@diff = @page.diff(params[:version_to], params[:version_from])) - @html_diff = HTMLDiff::DiffBuilder.new( - helpers.format_text(@diff.content_from.data.text, disable_macro_expansion: true), - helpers.format_text(@diff.content_to.data.text, disable_macro_expansion: true) - ).build + @html_diff = OpenProject::HtmlDiff.from_markdown( + @diff.content_from.data.text, + @diff.content_to.data.text + ) else render_404 end diff --git a/app/helpers/wiki_helper.rb b/app/helpers/wiki_helper.rb index 8be019496d02..897db6b44830 100644 --- a/app/helpers/wiki_helper.rb +++ b/app/helpers/wiki_helper.rb @@ -58,10 +58,6 @@ def breadcrumb_for_page(page, action = nil) breadcrumb_paths(*paths) end - def nl2br(content) - content.gsub(/(?:\n\r?|\r\n?)/, "
").html_safe - end - private def wiki_page_options_for_select_of_level(pages, diff --git a/app/views/wiki/diff.html.erb b/app/views/wiki/diff.html.erb index 934f6af2725d..a15ab391feda 100644 --- a/app/views/wiki/diff.html.erb +++ b/app/views/wiki/diff.html.erb @@ -44,5 +44,5 @@ See COPYRIGHT and LICENSE files for more details. (<%= @diff.content_to.user ? link_to_user(@diff.content_to.user) : t(:label_user_anonymous) %>, <%= format_time(@diff.content_to.created_at) %>)

- <%= nl2br @html_diff %> + <%= @html_diff.html_safe %>
diff --git a/lib/open_project/html_diff.rb b/lib/open_project/html_diff.rb new file mode 100644 index 000000000000..6471778aec2f --- /dev/null +++ b/lib/open_project/html_diff.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +#-- 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. +#++ + +module OpenProject + module HtmlDiff + extend OpenProject::TextFormatting + + module_function + + def from_markdown(markdown_from, markdown_to) + ::HTMLDiff::DiffBuilder.new( + format_text(markdown_from, disable_macro_expansion: true), + format_text(markdown_to, disable_macro_expansion: true) + ).build + .gsub(/(\n\r?)<\/ins>/, '\1') + .gsub(/(\n\r?)<\/del>/, '\1') + .gsub(/^
(
)/, '\1') + end + end +end diff --git a/spec/lib/open_project/html_diff_spec.rb b/spec/lib/open_project/html_diff_spec.rb new file mode 100644 index 000000000000..429022babed0 --- /dev/null +++ b/spec/lib/open_project/html_diff_spec.rb @@ -0,0 +1,110 @@ +# frozen_string_literal: true + +#-- 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. +#++ + +require "spec_helper" + +RSpec.describe OpenProject::HtmlDiff do + describe ".from_markdown" do + it "hightlights additions with tags" do + from = "" + to = "Hello, world!" + expect(described_class.from_markdown(from, to)) + .to eq(<<~HTML.strip) +

Hello, world!

+ HTML + end + + it "hightlights removals with tags" do + from = "Hello, world!" + to = "" + expect(described_class.from_markdown(from, to)) + .to eq(<<~HTML.strip) +

Hello, world!

+ HTML + end + + it "hightlights modifications with both and tags" do + from = "Hello, world!" + to = "Hello, OpenProject!" + expect(described_class.from_markdown(from, to)) + .to eq(<<~HTML.strip) +

Hello, world!OpenProject!

+ HTML + end + + context "with a list" do + it "removes extra newlines from the diff" do # rubocop:disable RSpec/ExampleLength + from = <<~MARKDOWN + Deletion: + + * Item 1 + + * Item 2 + + Insertion: + + * Item A + MARKDOWN + to = <<~MARKDOWN + Deletion: + + * Item 1 + + Insertion: + + * Item A + + * Item B + MARKDOWN + expect(described_class.from_markdown(from, to)) + .to eq(<<~HTML.strip) +

Deletion:

+
    +
  • +

    Item 1

    +
  • +
  • +

    Item 2

    +
  • +
+

Insertion:

+
    +
  • +

    Item A

    +
  • +
  • +

    Item B

    +
  • +
+ HTML + end + end + end +end diff --git a/spec/models/journable/with_historic_attributes_spec.rb b/spec/models/journable/with_historic_attributes_spec.rb index 75dcbdf21d5e..718fbb3dbb5d 100644 --- a/spec/models/journable/with_historic_attributes_spec.rb +++ b/spec/models/journable/with_historic_attributes_spec.rb @@ -149,7 +149,7 @@ end context "with active record relation of work packages" do - let(:work_packages) { WorkPackage.all } + let(:work_packages) { WorkPackage.order(subject: :asc).all } it "provides access to the work-package attributes" do expect(subject.map(&:subject)).to eq ["The current work package 1", "The current work package 2"] @@ -246,7 +246,7 @@ end context "with active record relation of work packages" do - let(:work_packages) { WorkPackage.all } + let(:work_packages) { WorkPackage.order(subject: :asc).all } it "provides access to the work-package attributes at timestamps" do expect(subject.first.attributes_by_timestamp["2022-01-01T00:00:00Z"].subject).to eq "The original work package 1"