mirror of
https://github.com/opf/openproject.git
synced 2026-06-14 03:30:14 +00:00
Merge pull request #19449 from opf/bug/65401-project-phase-field-shows-in-pdf-report-even-when-it-doesn-t-show-on-wp-i-e-when-project-phases-are-not-active
[#65401] Project phase field shows in pdf report even when it doesn't show on wp
This commit is contained in:
@@ -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 WorkPackage::Exports
|
||||
module Attributes
|
||||
def allowed_to_view_project_phases?(project)
|
||||
User.current.allowed_in_project?(:view_project_phases, project) && project.phases.active.any?
|
||||
end
|
||||
|
||||
def allowed_to_view_attribute?(obj, attribute_name)
|
||||
if attribute_name.to_sym == :project_phase && obj.is_a?(WorkPackage)
|
||||
allowed_to_view_project_phases?(obj.project)
|
||||
else
|
||||
true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -45,6 +45,7 @@ module WorkPackage::Exports
|
||||
# projectValue:1234:active # Outputs project with id 1234 value for "active"
|
||||
# projectValue:my-project-identifier:active # Outputs project with identifier my-project-identifier value for "active"
|
||||
class Attributes < OpenProject::TextFormatting::Matchers::RegexMatcher
|
||||
extend WorkPackage::Exports::Attributes
|
||||
DISABLED_PROJECT_RICH_TEXT_FIELDS = %i[description status_explanation status_description].freeze
|
||||
DISABLED_WORK_PACKAGE_RICH_TEXT_FIELDS = %i[description].freeze
|
||||
|
||||
@@ -101,11 +102,22 @@ module WorkPackage::Exports
|
||||
end
|
||||
|
||||
def self.resolve_label(model, attribute)
|
||||
model.human_attribute_name(
|
||||
::API::Utilities::PropertyNameConverter.to_ar_name(attribute.to_sym, context: model.new)
|
||||
)
|
||||
model.human_attribute_name(to_ar_name(attribute, model.new))
|
||||
end
|
||||
|
||||
def self.to_ar_name(attribute, context)
|
||||
::API::Utilities::PropertyNameConverter.to_ar_name(attribute.to_sym, context:)
|
||||
end
|
||||
|
||||
##
|
||||
# Resolves a work package or project match based on the type and id.
|
||||
# Returns the formatted value or an error message if not found.
|
||||
#
|
||||
# @param id [String] The ID of the work package or project.
|
||||
# @param type [String] The type of the match (label or value).
|
||||
# @param attribute [String] The attribute to resolve.
|
||||
# @param user [User] The user context for visibility checks.
|
||||
|
||||
def self.resolve_work_package_match(id, type, attribute, user)
|
||||
return resolve_label_work_package(attribute) if type == "label"
|
||||
return msg_macro_error(I18n.t("export.macro.model_not_found", model: type)) unless type == "value"
|
||||
@@ -145,18 +157,30 @@ module WorkPackage::Exports
|
||||
end
|
||||
|
||||
def self.resolve_value(obj, attribute, disabled_rich_text_fields)
|
||||
cf = obj.available_custom_fields.find { |pcf| pcf.name == attribute }
|
||||
custom_field = find_custom_field(obj, attribute)
|
||||
return msg_macro_error_rich_text if custom_field&.formattable?
|
||||
|
||||
return msg_macro_error_rich_text if cf&.formattable?
|
||||
attribute_name = convert_to_attribute_name(custom_field, attribute, obj)
|
||||
return " " unless can_view_attribute?(custom_field, obj, attribute_name)
|
||||
return msg_macro_error_rich_text if disabled_rich_text_fields.include?(attribute_name.to_sym)
|
||||
|
||||
ar_name = if cf.nil?
|
||||
::API::Utilities::PropertyNameConverter.to_ar_name(attribute.to_sym, context: obj)
|
||||
else
|
||||
"cf_#{cf.id}"
|
||||
end
|
||||
return msg_macro_error_rich_text if disabled_rich_text_fields.include?(ar_name.to_sym)
|
||||
format_attribute_value(attribute_name, obj.class, obj)
|
||||
end
|
||||
|
||||
format_attribute_value(ar_name, obj.class, obj)
|
||||
def self.can_view_attribute?(custom_field, obj, attribute_name)
|
||||
custom_field || allowed_to_view_attribute?(obj, attribute_name)
|
||||
end
|
||||
|
||||
def self.convert_to_attribute_name(custom_field, attribute, obj)
|
||||
if custom_field.nil?
|
||||
to_ar_name(attribute, obj)
|
||||
else
|
||||
"cf_#{custom_field.id}"
|
||||
end
|
||||
end
|
||||
|
||||
def self.find_custom_field(obj, attribute)
|
||||
obj.available_custom_fields.find { |pcf| pcf.name == attribute }
|
||||
end
|
||||
|
||||
def self.format_attribute_value(ar_name, model, obj)
|
||||
|
||||
@@ -29,6 +29,8 @@
|
||||
#++
|
||||
|
||||
module WorkPackage::PDFExport::Export::Wp::Attributes
|
||||
include WorkPackage::Exports::Attributes
|
||||
|
||||
def write_attributes!(work_package)
|
||||
work_package
|
||||
.type.attribute_groups
|
||||
@@ -138,6 +140,8 @@ module WorkPackage::PDFExport::Export::Wp::Attributes
|
||||
current_part = { type: :attribute, list: [] }
|
||||
parts = [current_part]
|
||||
group.attributes.each do |form_key|
|
||||
next unless show_attribute?(form_key, work_package)
|
||||
|
||||
if allowed_long_text_custom_field?(form_key, work_package)
|
||||
cf = form_key_to_custom_field(form_key)
|
||||
if current_part[:type] == :long_text
|
||||
@@ -156,6 +160,10 @@ module WorkPackage::PDFExport::Export::Wp::Attributes
|
||||
parts
|
||||
end
|
||||
|
||||
def show_attribute?(form_key, work_package)
|
||||
CustomField.custom_field_attribute?(form_key) || allowed_to_view_attribute?(work_package, form_key)
|
||||
end
|
||||
|
||||
def allowed_long_text_custom_field?(form_key, work_package)
|
||||
return false unless CustomField.custom_field_attribute? form_key
|
||||
|
||||
|
||||
@@ -31,14 +31,111 @@
|
||||
require "spec_helper"
|
||||
|
||||
RSpec.describe WorkPackage::PDFExport::Common::Macro do
|
||||
let(:work_package) do
|
||||
create(:work_package, id: 185, subject: "Work package 1")
|
||||
shared_let(:type_task) { create(:type_task) }
|
||||
shared_let(:custom_field) do
|
||||
create(
|
||||
:work_package_custom_field,
|
||||
name: "Custom Field 1",
|
||||
field_format: "string",
|
||||
types: [type_task]
|
||||
)
|
||||
end
|
||||
shared_let(:formatted_custom_field) do
|
||||
create(
|
||||
:work_package_custom_field,
|
||||
name: "Custom Formatted Field",
|
||||
field_format: "text",
|
||||
is_for_all: true,
|
||||
types: [type_task]
|
||||
)
|
||||
end
|
||||
shared_let(:project_custom_field_section) { create(:project_custom_field_section) }
|
||||
shared_let(:project_custom_field) do
|
||||
create(:string_project_custom_field, name: "Project Custom Field 1", project_custom_field_section:)
|
||||
end
|
||||
shared_let(:project) do
|
||||
create(
|
||||
:project,
|
||||
status_code: "on_track",
|
||||
work_package_custom_fields: [custom_field, formatted_custom_field],
|
||||
project_custom_fields: [project_custom_field],
|
||||
custom_field_values: { project_custom_field.id => "Project custom value 1" }
|
||||
)
|
||||
end
|
||||
shared_let(:other_project) do
|
||||
create(
|
||||
:project,
|
||||
name: "Other Project",
|
||||
work_package_custom_fields: [custom_field, formatted_custom_field],
|
||||
project_custom_fields: [project_custom_field],
|
||||
custom_field_values: { project_custom_field.id => "Project custom value 2" }
|
||||
)
|
||||
end
|
||||
shared_let(:work_package) do
|
||||
create(
|
||||
:work_package,
|
||||
subject: "Work package 1",
|
||||
type: type_task,
|
||||
status: create(:status, name: "In Progress"),
|
||||
project: project,
|
||||
custom_field_values: {
|
||||
custom_field.id => "Custom value 1",
|
||||
formatted_custom_field.id => "**Formatted** _text_ content"
|
||||
}
|
||||
)
|
||||
end
|
||||
shared_let(:other_work_package) do
|
||||
create(
|
||||
:work_package,
|
||||
subject: "Work package 2",
|
||||
project: other_project,
|
||||
type: type_task,
|
||||
custom_field_values: {
|
||||
custom_field.id => "Custom value 2"
|
||||
}
|
||||
)
|
||||
end
|
||||
shared_let(:restricted_other_project) do
|
||||
create(
|
||||
:project,
|
||||
name: "Other Project",
|
||||
work_package_custom_fields: [custom_field, formatted_custom_field],
|
||||
project_custom_fields: [project_custom_field],
|
||||
custom_field_values: { project_custom_field.id => "Project custom value 3" }
|
||||
)
|
||||
end
|
||||
shared_let(:restricted_work_package) do
|
||||
create(
|
||||
:work_package,
|
||||
subject: "Work package 3",
|
||||
project: restricted_other_project,
|
||||
type: type_task,
|
||||
custom_field_values: {
|
||||
custom_field.id => "Custom value 3"
|
||||
}
|
||||
)
|
||||
end
|
||||
shared_let(:formatter) { Class.new { extend WorkPackage::PDFExport::Common::Macro } }
|
||||
let(:additional_permissions) { [] }
|
||||
let(:user) do
|
||||
create(
|
||||
:user,
|
||||
member_with_permissions: {
|
||||
project => %i[view_work_packages view_project_attributes view_project] + additional_permissions,
|
||||
other_project => %i[view_work_packages view_project_attributes view_project] + additional_permissions
|
||||
}
|
||||
)
|
||||
end
|
||||
let(:markdown) { "" }
|
||||
let(:formatter) { Class.new { extend WorkPackage::PDFExport::Common::Macro } }
|
||||
|
||||
before do
|
||||
User.current = user
|
||||
end
|
||||
|
||||
subject(:formatted) do
|
||||
formatter.apply_markdown_field_macros(markdown, { work_package: work_package, user: User.current })
|
||||
formatter
|
||||
.apply_markdown_field_macros(markdown, { work_package: work_package, project: project, user: })
|
||||
.sub("\n", "")
|
||||
end
|
||||
|
||||
describe "empty text" do
|
||||
@@ -47,48 +144,542 @@ RSpec.describe WorkPackage::PDFExport::Common::Macro do
|
||||
end
|
||||
end
|
||||
|
||||
describe "wp mention tag" do
|
||||
let(:markdown) { '<mention class="mention" data-id="185" data-type="work_package" data-text="#185">#185</mention>' }
|
||||
describe "wp mention macro" do
|
||||
let(:expected_tag) do
|
||||
"<mention class=\"mention\" data-id=\"#{
|
||||
work_package.id
|
||||
}\" data-type=\"work_package\" data-text=\"##{
|
||||
work_package.id
|
||||
}\">##{
|
||||
work_package.id
|
||||
}</mention>"
|
||||
end
|
||||
|
||||
it "ignores the tag" do
|
||||
expect(formatted).to eq("<mention class=\"mention\" data-id=\"185\" " +
|
||||
"data-type=\"work_package\" data-text=\"#185\">\\#185</mention>\n")
|
||||
describe "with tag" do
|
||||
let(:markdown) { expected_tag }
|
||||
|
||||
it "loops the tag through" do
|
||||
# note: escaped backslash in the tag text for correct markdown rendering
|
||||
expect(formatted).to eq(
|
||||
"<mention class=\"mention\" data-id=\"#{
|
||||
work_package.id
|
||||
}\" data-type=\"work_package\" data-text=\"##{
|
||||
work_package.id
|
||||
}\">\\##{work_package.id}</mention>"
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with plain" do
|
||||
let(:markdown) { "##{work_package.id}" }
|
||||
|
||||
it "contains correct data" do
|
||||
expect(formatted).to eq(expected_tag)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with markdown formating bold" do
|
||||
let(:markdown) { "**##{work_package.id}**" }
|
||||
|
||||
it "contains correct data" do
|
||||
expect(formatted).to eq("**#{expected_tag}**")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with markdown formating strikethrough" do
|
||||
let(:markdown) { "~~##{work_package.id}~~" }
|
||||
|
||||
it "contains correct data" do
|
||||
expect(formatted).to eq("~~#{expected_tag}~~")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with strikethrough in table" do
|
||||
let(:markdown) { "<table><tr><td><p><s>##{work_package.id}</s></p></td></tr></table>" }
|
||||
|
||||
it "contains correct data" do
|
||||
expect(formatted).to eq("<table><tr><td><p><s>#{expected_tag}</s></p></td></tr></table>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "wp mention plain" do
|
||||
let(:markdown) { "#185" }
|
||||
describe "workPackageValue macro" do
|
||||
describe "with current work package attribute" do
|
||||
let(:markdown) { "workPackageValue:subject" }
|
||||
|
||||
it "contains correct data" do
|
||||
expect(formatted).to eq("<mention class=\"mention\" data-id=\"185\" " +
|
||||
"data-type=\"work_package\" data-text=\"#185\">#185</mention>\n")
|
||||
it "outputs the attribute value" do
|
||||
expect(formatted).to eq("Work package 1")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with specific work package ID and attribute" do
|
||||
let(:markdown) { "workPackageValue:#{work_package.id}:subject" }
|
||||
|
||||
it "outputs the attribute value for the specified work package" do
|
||||
expect(formatted).to eq("Work package 1")
|
||||
end
|
||||
end
|
||||
|
||||
describe "withh another work package ID and attribute" do
|
||||
let(:markdown) { "workPackageValue:#{other_work_package.id}:subject" }
|
||||
|
||||
it "outputs the attribute value for the specified work package" do
|
||||
expect(formatted).to eq("Work package 2")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with restricted work package ID and attribute" do
|
||||
let(:markdown) { "workPackageValue:#{restricted_work_package.id}:subject" }
|
||||
|
||||
it "outputs an error message" do
|
||||
expect(formatted).to include("Macro error, resource not found")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with restricted work package ID and custom field" do
|
||||
let(:markdown) { "workPackageValue:#{restricted_work_package.id}:\"Custom Field 1\"" }
|
||||
|
||||
it "outputs an error message" do
|
||||
expect(formatted).to include("Macro error, resource not found")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with restricted work package ID and formatted custom field" do
|
||||
let(:markdown) { "workPackageValue:#{restricted_work_package.id}:\"Custom Formatted Field\"" }
|
||||
|
||||
it "outputs an error message" do
|
||||
expect(formatted).to include("Macro error, resource not found")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with non-existent work package ID" do
|
||||
let(:markdown) { "workPackageValue:999:subject" }
|
||||
|
||||
it "outputs an error message" do
|
||||
expect(formatted).to include("Macro error, resource not found")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with status attribute" do
|
||||
let(:markdown) { "workPackageValue:status" }
|
||||
|
||||
it "outputs the status name" do
|
||||
expect(formatted).to eq("In Progress")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with project_phase attribute" do
|
||||
let(:project_phase_active) { true }
|
||||
let!(:project_phase) do
|
||||
create(:project_phase, project: project, active: project_phase_active)
|
||||
end
|
||||
let(:markdown) { "workPackageValue:project_phase" }
|
||||
|
||||
before do
|
||||
work_package.update!(project_phase_definition_id: project_phase.definition.id)
|
||||
end
|
||||
|
||||
describe "without the permission" do
|
||||
it "outputs a single space" do
|
||||
expect(formatted).to eq(" ")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with the permission" do
|
||||
let(:additional_permissions) { [:view_project_phases] }
|
||||
|
||||
describe "with active phase" do
|
||||
it "outputs the project phase name" do
|
||||
expect(formatted).to eq(project_phase.name)
|
||||
end
|
||||
end
|
||||
|
||||
describe "without active phase" do
|
||||
let(:project_phase_active) { false }
|
||||
|
||||
it "outputs the project phase name" do
|
||||
expect(formatted).to eq(" ")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "with custom field by name" do
|
||||
let(:markdown) { 'workPackageValue:"Custom Field 1"' }
|
||||
|
||||
it "outputs the custom field value" do
|
||||
expect(formatted).to eq("Custom value 1")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with specific work package ID and custom field" do
|
||||
let(:markdown) { "workPackageValue:#{work_package.id}:\"Custom Field 1\"" }
|
||||
|
||||
it "outputs the custom field value for the specified work package" do
|
||||
expect(formatted).to eq("Custom value 1")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with another work package ID and custom field" do
|
||||
let(:markdown) { "workPackageValue:#{other_work_package.id}:\"Custom Field 1\"" }
|
||||
|
||||
it "outputs the custom field value for the specified work package" do
|
||||
expect(formatted).to eq("Custom value 2")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with formatted custom field" do
|
||||
let(:markdown) { 'workPackageValue:"Custom Formatted Field"' }
|
||||
|
||||
it "outputs an error message for rich text" do
|
||||
expect(formatted).to include(I18n.t("export.macro.rich_text_unsupported"))
|
||||
end
|
||||
end
|
||||
|
||||
describe "with specific work package ID and formatted custom field" do
|
||||
let(:markdown) { "workPackageValue:#{work_package.id}:\"Custom Formatted Field\"" }
|
||||
|
||||
it "outputs an error message for rich text" do
|
||||
expect(formatted).to include(I18n.t("export.macro.rich_text_unsupported"))
|
||||
end
|
||||
end
|
||||
|
||||
describe "with non-existent attribute" do
|
||||
let(:markdown) { "workPackageValue:nonexistent_attribute" }
|
||||
|
||||
it "outputs an empty value" do
|
||||
expect(formatted).to eq(" ")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with another work package id and a non-existent attribute" do
|
||||
let(:markdown) { "workPackageValue:#{other_work_package.id}:nonexistent_attribute" }
|
||||
|
||||
it "outputs an empty value" do
|
||||
expect(formatted).to eq(" ")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with markdown formatting" do
|
||||
let(:markdown) { "**workPackageValue:subject**" }
|
||||
|
||||
it "preserves the markdown formatting" do
|
||||
expect(formatted).to eq("**Work package 1**")
|
||||
end
|
||||
end
|
||||
|
||||
describe "in a table" do
|
||||
let(:markdown) { "<table><tr><td>workPackageValue:subject</td></tr></table>" }
|
||||
|
||||
it "processes the macro inside HTML" do
|
||||
expect(formatted).to eq("<table><tr><td>Work package 1</td></tr></table>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "wp mention with markdown formating bold" do
|
||||
let(:markdown) { "\n**#185**\n" }
|
||||
describe "workPackageLabel macro" do
|
||||
let!(:original_setting) { ActiveModel::Translation.raise_on_missing_translations }
|
||||
|
||||
it "contains correct data" do
|
||||
expect(formatted).to eq("**<mention class=\"mention\" data-id=\"185\" " +
|
||||
"data-type=\"work_package\" data-text=\"#185\">#185</mention>**\n")
|
||||
before do
|
||||
ActiveModel::Translation.raise_on_missing_translations = false
|
||||
end
|
||||
|
||||
after do
|
||||
ActiveModel::Translation.raise_on_missing_translations = original_setting
|
||||
end
|
||||
|
||||
describe "with current work package attribute" do
|
||||
let(:markdown) { "workPackageLabel:subject" }
|
||||
|
||||
it "outputs the attribute label" do
|
||||
expect(formatted).to eq("Subject")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with specific work package ID and attribute" do
|
||||
let(:markdown) { "workPackageLabel:#{work_package.id}:subject" }
|
||||
|
||||
it "outputs the attribute label for the specified work package" do
|
||||
expect(formatted).to eq("Subject")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with non-existent work package ID" do
|
||||
let(:markdown) { "workPackageLabel:999:subject" }
|
||||
|
||||
it "outputs a humanized form" do
|
||||
expect(formatted).to eq("Subject")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with status attribute" do
|
||||
let(:markdown) { "workPackageLabel:status" }
|
||||
|
||||
it "outputs the status label" do
|
||||
expect(formatted).to eq("Status")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with custom field by name" do
|
||||
let(:markdown) { 'workPackageLabel:"Custom Field 1"' }
|
||||
|
||||
it "outputs the custom field name" do
|
||||
expect(formatted).to eq("Custom field 1")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with specific work package ID and custom field" do
|
||||
let(:markdown) { "workPackageLabel:#{work_package.id}:\"Custom Field 1\"" }
|
||||
|
||||
it "outputs the custom field name for the specified work package" do
|
||||
expect(formatted).to eq("Custom field 1")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with non-existent attribute" do
|
||||
let(:markdown) { "workPackageLabel:nonexistent_attribute" }
|
||||
|
||||
it "outputs the humanized attribute name" do
|
||||
expect(formatted).to eq("Nonexistent attribute")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with markdown formatting" do
|
||||
let(:markdown) { "**workPackageLabel:subject**" }
|
||||
|
||||
it "preserves the markdown formatting" do
|
||||
expect(formatted).to eq("**Subject**")
|
||||
end
|
||||
end
|
||||
|
||||
describe "in a table" do
|
||||
let(:markdown) { "<table><tr><td>workPackageLabel:subject</td></tr></table>" }
|
||||
|
||||
it "processes the macro inside HTML" do
|
||||
expect(formatted).to eq("<table><tr><td>Subject</td></tr></table>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "wp mention with markdown formating strikethrough" do
|
||||
let(:markdown) { "~~#185~~" }
|
||||
describe "projectValue macro" do
|
||||
describe "with current project attribute" do
|
||||
let(:markdown) { "projectValue:name" }
|
||||
|
||||
it "contains correct data" do
|
||||
expect(formatted).to eq("~~<mention class=\"mention\" data-id=\"185\" " +
|
||||
"data-type=\"work_package\" data-text=\"#185\">#185</mention>~~\n")
|
||||
it "outputs the attribute value" do
|
||||
expect(formatted).to eq(project.name)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with specific project ID and attribute" do
|
||||
let(:markdown) { "projectValue:#{project.id}:name" }
|
||||
|
||||
it "outputs the attribute value for the specified project" do
|
||||
expect(formatted).to eq(project.name)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with other project ID and attribute" do
|
||||
let(:markdown) { "projectValue:#{other_project.id}:name" }
|
||||
|
||||
it "outputs the attribute value for the specified project" do
|
||||
expect(formatted).to eq(other_project.name)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with specific project identifier and attribute" do
|
||||
let(:markdown) { "projectValue:\"#{project.identifier}\":name" }
|
||||
|
||||
it "outputs the attribute value for the specified project" do
|
||||
expect(formatted).to eq(project.name)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with other project identifier and attribute" do
|
||||
let(:markdown) { "projectValue:\"#{other_project.identifier}\":name" }
|
||||
|
||||
it "outputs the attribute value for the specified project" do
|
||||
expect(formatted).to eq(other_project.name)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with non-existent project ID" do
|
||||
let(:markdown) { "projectValue:999:name" }
|
||||
|
||||
it "outputs an error message" do
|
||||
expect(formatted).to include("Macro error, resource not found")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with restricted project ID" do
|
||||
let(:markdown) { "projectValue:#{restricted_other_project.id}:name" }
|
||||
|
||||
it "outputs an error message" do
|
||||
expect(formatted).to include("Macro error, resource not found")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with restricted project identifier" do
|
||||
let(:markdown) { "projectValue:\"#{restricted_other_project.identifier}\":name" }
|
||||
|
||||
it "outputs an error message" do
|
||||
expect(formatted).to include("Macro error, resource not found")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with restricted project ID and custom field" do
|
||||
let(:markdown) { "projectValue:#{restricted_other_project.id}:\"Project Custom Field 1\"" }
|
||||
|
||||
it "outputs an error message" do
|
||||
expect(formatted).to include("Macro error, resource not found")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with status attribute" do
|
||||
let(:markdown) { "projectValue:status_code" }
|
||||
|
||||
it "outputs the status code" do
|
||||
expect(formatted).to eq(project.status_code)
|
||||
end
|
||||
end
|
||||
|
||||
describe "with custom field by name" do
|
||||
let(:markdown) { 'projectValue:"Project Custom Field 1"' }
|
||||
|
||||
it "outputs the custom field value" do
|
||||
expect(formatted).to eq("Project custom value 1")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with specific project ID and custom field" do
|
||||
let(:markdown) { "projectValue:#{project.id}:\"Project Custom Field 1\"" }
|
||||
|
||||
it "outputs the custom field value for the specified project" do
|
||||
expect(formatted).to eq("Project custom value 1")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with other project ID and custom field" do
|
||||
let(:markdown) { "projectValue:#{other_project.id}:\"Project Custom Field 1\"" }
|
||||
|
||||
it "outputs the custom field value for the specified project" do
|
||||
expect(formatted).to eq("Project custom value 2")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with non-existent attribute" do
|
||||
let(:markdown) { "projectValue:nonexistent_attribute" }
|
||||
|
||||
it "outputs an empty value" do
|
||||
expect(formatted).to eq(" ")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with markdown formatting" do
|
||||
let(:markdown) { "**projectValue:name**" }
|
||||
|
||||
it "preserves the markdown formatting" do
|
||||
expect(formatted).to eq("**#{project.name}**")
|
||||
end
|
||||
end
|
||||
|
||||
describe "in a table" do
|
||||
let(:markdown) { "<table><tr><td>projectValue:name</td></tr></table>" }
|
||||
|
||||
it "processes the macro inside HTML" do
|
||||
expect(formatted).to eq("<table><tr><td>#{project.name}</td></tr></table>")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "wp mention with strikethrough in table" do
|
||||
let(:markdown) { "<table><tr><td><p><s>##185</s></p></td></tr></table>" }
|
||||
describe "projectLabel macro" do
|
||||
let!(:original_setting) { ActiveModel::Translation.raise_on_missing_translations }
|
||||
|
||||
it "contains correct data" do
|
||||
expect(formatted).to eq("<table><tr><td><p><s><mention class=\"mention\" data-id=\"185\" " +
|
||||
"data-type=\"work_package\" data-text=\"##185\">##185</mention></s></p></td></tr></table>\n")
|
||||
before do
|
||||
ActiveModel::Translation.raise_on_missing_translations = false
|
||||
end
|
||||
|
||||
after do
|
||||
ActiveModel::Translation.raise_on_missing_translations = original_setting
|
||||
end
|
||||
|
||||
describe "with current project attribute" do
|
||||
let(:markdown) { "projectLabel:name" }
|
||||
|
||||
it "outputs the attribute label" do
|
||||
expect(formatted).to eq("Name")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with specific project ID and attribute" do
|
||||
let(:markdown) { "projectLabel:#{project.id}:name" }
|
||||
|
||||
it "outputs the attribute label for the specified project" do
|
||||
expect(formatted).to eq("Name")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with specific project identifier and attribute" do
|
||||
let(:markdown) { "projectLabel:\"#{project.identifier}\":name" }
|
||||
|
||||
it "outputs the attribute label for the specified project" do
|
||||
expect(formatted).to eq("Name")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with non-existent project ID" do
|
||||
let(:markdown) { "projectLabel:999:name" }
|
||||
|
||||
it "outputs the attribute label" do
|
||||
expect(formatted).to eq("Name")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with status attribute" do
|
||||
let(:markdown) { "projectLabel:status_code" }
|
||||
|
||||
it "outputs the status label" do
|
||||
expect(formatted).to eq("Project status")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with custom field by name" do
|
||||
let(:markdown) { 'projectLabel:"Project Custom Field 1"' }
|
||||
|
||||
it "outputs the custom field name" do
|
||||
expect(formatted).to eq("Project custom field 1")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with specific project ID and custom field" do
|
||||
let(:markdown) { "projectLabel:#{project.id}:\"Project Custom Field 1\"" }
|
||||
|
||||
it "outputs the custom field name for the specified project" do
|
||||
expect(formatted).to eq("Project custom field 1")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with non-existent attribute" do
|
||||
let(:markdown) { "projectLabel:nonexistent_attribute" }
|
||||
|
||||
it "outputs the humanized attribute name" do
|
||||
expect(formatted).to eq("Nonexistent attribute")
|
||||
end
|
||||
end
|
||||
|
||||
describe "with markdown formatting" do
|
||||
let(:markdown) { "**projectLabel:name**" }
|
||||
|
||||
it "preserves the markdown formatting" do
|
||||
expect(formatted).to eq("**Name**")
|
||||
end
|
||||
end
|
||||
|
||||
describe "in a table" do
|
||||
let(:markdown) { "<table><tr><td>projectLabel:name</td></tr></table>" }
|
||||
|
||||
it "processes the macro inside HTML" do
|
||||
expect(formatted).to eq("<table><tr><td>Name</td></tr></table>")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -71,6 +71,10 @@ RSpec.describe WorkPackage::PDFExport::WorkPackageToPdf do
|
||||
# cf_disabled_in_project.id not included == disabled
|
||||
work_package_custom_field_ids: [cf_long_text.id, cf_empty_long_text.id, cf_global_bool.id, cf_link.id])
|
||||
end
|
||||
let(:phase_definition) { create(:project_phase_definition, name: "Test Phase") }
|
||||
let!(:project_phase) do
|
||||
create(:project_phase, project: project, definition: phase_definition, active: true)
|
||||
end
|
||||
let(:forbidden_project) do
|
||||
create(:project,
|
||||
name: "Forbidden project",
|
||||
@@ -88,7 +92,9 @@ RSpec.describe WorkPackage::PDFExport::WorkPackageToPdf do
|
||||
end
|
||||
let(:user) do
|
||||
create(:user,
|
||||
member_with_permissions: { project => %w[view_work_packages export_work_packages view_project_attributes] })
|
||||
member_with_permissions: {
|
||||
project => %w[view_work_packages export_work_packages view_project_attributes view_project_phases]
|
||||
})
|
||||
end
|
||||
let(:another_user) do
|
||||
create(:user, firstname: "Secret User")
|
||||
|
||||
Reference in New Issue
Block a user