Skip to content
Open
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
6 changes: 3 additions & 3 deletions app/models/exports/pdf/common/common.rb
Original file line number Diff line number Diff line change
Expand Up @@ -428,9 +428,9 @@ def make_link_href_cell(href, caption)
"<color rgb='#{styles.link_color}'>#{make_link_href(href, caption)}</color>"
end

def get_id_column_cell(work_package, value)
def get_id_column_cell(work_package)
href = url_helpers.work_package_url(work_package)
make_link_href_cell(href, value)
make_link_href_cell(href, work_package.display_id)
end
Comment thread
judithroth marked this conversation as resolved.

def get_subject_column_cell(work_package, value)
Expand Down Expand Up @@ -469,7 +469,7 @@ def get_cf_link_cell(custom_url)
def get_value_cell_by_column(work_package, column_name, format_subject)
value = get_column_value(work_package, column_name)
return get_cf_link_cell(value) if value.is_a?(::Exports::Formatters::LinkFormatter)
return get_id_column_cell(work_package, value) if column_name == :id
return get_id_column_cell(work_package) if column_name == :id
return get_subject_column_cell(work_package, value) if format_subject && column_name == :subject

escape_tags(value)
Expand Down
36 changes: 3 additions & 33 deletions app/models/exports/pdf/common/markdown.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class MD2PDFExport
include MarkdownToPDF::Core
include MarkdownToPDF::Parser
include Exports::PDF::Common::Common
include Exports::PDF::Common::WorkPackageMentions

def initialize(styling_yml, pdf, hyphenation_language)
@styles = MarkdownToPDF::Styles.new(styling_yml)
Expand Down Expand Up @@ -94,45 +95,14 @@ def handle_wp_mention_html_tag(tag, node, opts)
wp_mention_macro(tag.attr("data-text") || "", tag.attr("data-id") || "", opts)
end

def expand_wp_mention(work_package, content)
detail_level = content.count("#")
return content if detail_level == 1

# ##1234: {Type} #{ID}: {Subject}
content = "#{work_package.type} ##{work_package.id}: #{work_package.subject}"
return content if detail_level == 2

# ###1234: {Status} {Type} #{ID}: {Subject} ({Start Date} - {End Date})
"#{work_package.status.name} #{content}#{work_package_dates(work_package)}"
end

def wp_mention_macro(content, id, opts)
id = id[/\d+/]
return [text_hash(content, opts)] if id.blank?

work_package = WorkPackage.find_by(id: id)
work_package = WorkPackage.find_by_display_id(id)
return [text_hash(content, opts)] unless work_package&.visible?

content = expand_wp_mention(work_package, content)
[text_hash(content, opts.merge({ link: url_helpers.work_package_url(id) }))]
end

def work_package_dates(work_package)
return "" if work_package.start_date.blank? && work_package.due_date.blank?

if work_package.due_date.present? && work_package.start_date == work_package.due_date
return " (#{format_date(work_package.due_date)})"
end

work_package_date_range(work_package)
end

def work_package_date_range(work_package)
content = [
work_package.start_date.present? ? format_date(work_package.start_date) : I18n.t("label_no_start_date"),
work_package.due_date.present? ? format_date(work_package.due_date) : I18n.t("label_no_due_date")
].join(" - ")
" (#{content})"
[text_hash(content, opts.merge({ link: url_helpers.work_package_url(work_package) }))]
end

def handle_mention_html_tag(tag, node, opts)
Expand Down
63 changes: 63 additions & 0 deletions app/models/exports/pdf/common/work_package_mentions.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# 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 Exports::PDF::Common::WorkPackageMentions
include Redmine::I18n

def expand_wp_mention(work_package, content)
detail_level = content.count("#")
return work_package.formatted_id if detail_level == 1

# ##: {Type} {formatted_id}: {Subject}
content = "#{work_package.type} #{work_package.formatted_id}: #{work_package.subject}"
return content if detail_level == 2

# ###: {Status} {Type} {formatted_id}: {Subject} ({Start Date} - {End Date})
"#{work_package.status.name} #{content}#{work_package_dates(work_package)}"
end

def work_package_dates(work_package)
return "" if work_package.start_date.blank? && work_package.due_date.blank?

if work_package.due_date.present? && work_package.start_date == work_package.due_date
return " (#{format_date(work_package.due_date)})"
end

work_package_date_range(work_package)
end

def work_package_date_range(work_package)
content = [
work_package.start_date.present? ? format_date(work_package.start_date) : I18n.t("label_no_start_date"),
work_package.due_date.present? ? format_date(work_package.due_date) : I18n.t("label_no_due_date")
].join(" - ")
" (#{content})"
end
end
2 changes: 1 addition & 1 deletion app/models/exports/pdf/components/gantt/gantt_builder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -742,7 +742,7 @@ def build_row_text_lines_wp_info(entry, left, right, top)
# @param [WorkPackage] work_package
# @return [String]
def work_package_info_line(work_package)
"#{work_package.type} ##{work_package.id} • #{work_package.status} • #{work_package_info_line_date work_package}"
"#{work_package.type} #{work_package.formatted_id} • #{work_package.status} • #{work_package_info_line_date work_package}"
end

def work_package_info_line_date(work_package)
Expand Down
2 changes: 1 addition & 1 deletion app/models/work_package/pdf_export/document_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def footer_title
def title
# <project>_<type>_<ID>_<subject><YYYY-MM-DD>_<HH-MM>.pdf
build_pdf_filename([work_package.project, work_package.type,
"##{work_package.id}", work_package.subject].join("_"))
work_package.display_id, work_package.subject].join("_"))
end

def with_images?
Expand Down
32 changes: 28 additions & 4 deletions app/models/work_package/pdf_export/generator/generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class MD2PDFGenerator
include MarkdownToPDF::Core
include MarkdownToPDF::Parser
include MarkdownToPDF::StyleSchema
include Exports::PDF::Common::WorkPackageMentions

def initialize(styling_yml)
symbol_yml = symbolize(styling_yml)
Expand Down Expand Up @@ -94,17 +95,40 @@ def hyphenate(text)
@hyphens.hyphenate(text)
end

def handle_wp_mention_html_tag(tag, node, opts)
# <mention class="mention" data-id="185" data-type="work_package" data-text="#185">#185</mention>
# <mention class="mention" data-id="185" data-type="work_package" data-text="##185">##185</mention>
# <mention class="mention" data-id="185" data-type="work_package" data-text="###185">###185</mention>
next_node = node&.next
if next_node && next_node.type == :text && next_node.respond_to?(:string_content)
next_node.string_content = ""
end
render_wp_mention(tag.attr("data-text") || "", tag.attr("data-id") || "", opts)
end

def render_wp_mention(content, id, opts)
return [text_hash(content, opts)] if id.blank?

work_package = WorkPackage.find_by_display_id(id)
return [text_hash(content, opts)] unless work_package&.visible?

[text_hash(expand_wp_mention(work_package, content), opts)]
end
Comment thread
judithroth marked this conversation as resolved.

def handle_mention_html_tag(tag, node, opts)
if tag.text.blank?
# <mention class="mention" data-id="46012" data-type="work_package" data-text="#46012"></mention>
if tag.attr("data-type") == "work_package"
handle_wp_mention_html_tag(tag, node, opts)
elsif tag.text.blank?
# <mention class="mention" data-id="3" data-type="user" data-text="@Some User">
text = tag.attr("data-text")
if text.present? && !node.next.respond_to?(:string_content) && node.next.string_content != text
return [text_hash(text, opts)]
end
[]
else
# <mention class="mention" data-id="3" data-type="user" data-text="@Some User">@Some User</mention>
[]
end
# <mention class="mention" data-id="3" data-type="user" data-text="@Some User">@Some User</mention>
[]
end

def handle_unknown_inline_html_tag(tag, node, opts)
Expand Down
4 changes: 2 additions & 2 deletions app/models/work_package/pdf_export/work_package_to_pdf.rb
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ def wp_title_formatted_text(work_package, style)
end

def heading
"#{work_package.type} ##{work_package.id} - #{work_package.subject}"
"#{work_package.type} #{work_package.formatted_id} - #{work_package.subject}"
end

def footer_title
Expand All @@ -118,7 +118,7 @@ def footer_title
def title
# <project>_<type>_<ID>_<subject><YYYY-MM-DD>_<HH-MM>.pdf
build_pdf_filename([work_package.project, work_package.type,
"##{work_package.id}", work_package.subject].join("_"))
work_package.display_id, work_package.subject].join("_"))
end

def write_description!(work_package)
Expand Down
108 changes: 108 additions & 0 deletions spec/models/exports/pdf/common/markdown/md2_pdf_export_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# 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"

RSpec.describe Exports::PDF::Common::Markdown::MD2PDFExport do
let(:exporter) { described_class.allocate }
let(:type) { build_stubbed(:type, name: "Bug") }
let(:status) { build_stubbed(:status, name: "In Progress") }
let(:work_package) do
build_stubbed(:work_package, type:, status:, subject: "Fix login")
end

describe "#wp_mention_macro" do
let(:admin) { create(:admin) }
let(:wp) { create(:work_package) }

before do
User.current = admin
end

context "in classic mode",
with_flag: { semantic_work_package_ids: false } do
it "generates a link URL with the numeric id" do
result = exporter.wp_mention_macro("##{wp.id}", wp.id.to_s, {})
expect(result.first[:link]).to include(wp.id.to_s)
end
end

context "in semantic mode",
with_flag: { semantic_work_package_ids: true },
with_settings: { work_packages_identifier: "semantic" } do
it "generates a link URL with the semantic identifier" do
result = exporter.wp_mention_macro("##{wp.id}", wp.id.to_s, {})
expect(result.first[:link]).to include(wp.identifier)
end
end
end

describe "#expand_wp_mention" do
context "in classic mode", with_settings: { work_packages_identifier: "classic" } do
it "returns formatted_id for level 1" do
expect(exporter.expand_wp_mention(work_package, "##{work_package.id}"))
.to eq("##{work_package.id}")
end

it "uses formatted_id in level 2 expansion" do
result = exporter.expand_wp_mention(work_package, "###{work_package.id}")
expect(result).to eq("Bug ##{work_package.id}: Fix login")
end

it "uses formatted_id in level 3 expansion" do
allow(exporter).to receive(:work_package_dates).with(work_package).and_return("")
result = exporter.expand_wp_mention(work_package, "####{work_package.id}")
expect(result).to eq("In Progress Bug ##{work_package.id}: Fix login")
end
end

context "in semantic mode",
with_flag: { semantic_work_package_ids: true },
with_settings: { work_packages_identifier: "semantic" } do
before { work_package.identifier = "PROJ-42" }

it "returns semantic formatted_id for level 1" do
expect(exporter.expand_wp_mention(work_package, "##{work_package.id}"))
.to eq("PROJ-42")
end

it "uses semantic formatted_id in level 2 expansion" do
result = exporter.expand_wp_mention(work_package, "###{work_package.id}")
expect(result).to eq("Bug PROJ-42: Fix login")
end

it "uses semantic formatted_id in level 3 expansion" do
allow(exporter).to receive(:work_package_dates).with(work_package).and_return("")
result = exporter.expand_wp_mention(work_package, "####{work_package.id}")
expect(result).to eq("In Progress Bug PROJ-42: Fix login")
end
end
end
end
32 changes: 30 additions & 2 deletions spec/models/work_package/pdf_export/document_generator_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
include Redmine::I18n
include PDFExportSpecUtils

let(:project) { create(:project) }
let(:project) { create(:project, name: "PDF Project") }
let(:user) { create(:admin) }
let(:description) do
"This is a test description with an macro: workPackageValue:assignee"
Expand All @@ -20,7 +20,7 @@
subject: "Document Generator Specs",
type:)
end
let(:type) { create(:type) }
let(:type) { create(:type, name: "Feature") }
let(:options) do
{}
end
Expand Down Expand Up @@ -52,6 +52,25 @@
PDF::Inspector::Text.analyze(content).strings
end

describe "#title" do
context "in classic mode",
with_flag: { semantic_work_package_ids: false } do
it "uses the numeric id in the filename" do
expected_title = "PDF_Project_Feature_#{work_package.id}_Document_Generator_Specs_2023-06-30_23-59.pdf"
expect(export_pdf.title).to eql(expected_title)
end
end

context "in semantic mode",
with_flag: { semantic_work_package_ids: true },
with_settings: { work_packages_identifier: "semantic" } do
it "uses the semantic identifier in the filename" do
expected_title = "PDF_Project_Feature_#{work_package.identifier}_Document_Generator_Specs_2023-06-30_23-59.pdf"
expect(export_pdf.title).to eql(expected_title)
end
end
end

describe "with a request for a PDF" do
it "contains correct data" do
expected_result = [
Expand All @@ -65,6 +84,15 @@
expect(result.join(" ")).to eq(expected_result.join(" "))
end

describe "with a work package mention in the description" do
let(:other_work_package) { create(:work_package, project:, type:) }
let(:description) { "##{other_work_package.id}" }

it "renders the mention using formatted_id" do
expect(pdf).to include(other_work_package.formatted_id)
end
end

describe "with a request for a PDF with hyphenation and no header/footer text" do
let(:options) do
{
Expand Down
Loading
Loading