Files
openproject/lib/open_project/text_formatting.rb
T
Kabiru Mwenja 499d7820a2 Add render_mode flag and MailFormattingHelper
`format_text` accepts `render_mode:` (`:in_app_html`, `:external_html`,
`:external_text`), which resolves the `only_path`, `static_html` and
`plain_text` context flags as a set. External surfaces (mailer HTML
body, future RSS/PDF/webhook) need absolute URLs and static rendering
together; pinning the trio at the public API keeps callers from
forgetting one. Explicit primitive kwargs still override.

`MailFormattingHelper` exposes `format_mail_html` and `format_mail_text`
thin wrappers around `format_text(render_mode:)`. The `_html` / `_text`
suffix matches the `.html.erb` / `.text.erb` template extension so
caller intent stays visible in the view, with no introspection of
`formats`.

The five WorkPackageMailer view sites use the helpers; `_work_package_details`,
`mentioned.html`, `mentioned.text`, `watcher_changed.html`, `watcher_changed.text`
drop the `static_html:`/`only_path:`/`plain_text:` boilerplate.
2026-05-27 13:04:26 +03:00

125 lines
4.8 KiB
Ruby

# 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 OpenProject
# This module provides high-level text formatting functionality.
#
# @!method request
# Expected to be defined in the including class.
# @return [ActionDispatch::Request] the current request context.
#
# @note
# The including class should implement {#request} if {#format_text} is
# called within a request cycle.
module TextFormatting
include ::OpenProject::TextFormatting::Truncation
# @!macro format_text_params
# @param [Project] project a Project context.
# @param [Symbol] render_mode the rendering channel (`:in_app_html`,
# `:external_html`, `:external_text`). Resolves the `only_path`,
# `static_html` and `plain_text` context flags as a set. Prefer this
# over passing the primitives individually. See {RenderMode}.
# @param [Boolean] only_path explicit override for the resolved `only_path`.
# @param [User] current_user the current user context.
# @param [:plain, :rich] format the text format.
# `:plain` will return plain text.
# `:rich` will render raw Markdown as HTML.
# @param ** [Hash] additional context to pass to the underlying rendering
# pipeline. Explicit `static_html:` / `plain_text:` here override the
# values resolved from `render_mode:`.
# rubocop:disable Layout/LineLength
##
# Formats text according to system settings and provided params.
#
# @overload format_text(text, object: nil, project: @project || object.try(:project), render_mode: :in_app_html, only_path: nil, current_user: User.current, format: :rich, **)
#
# @param [String] text the raw text to be formatted, typically Markdown.
# @param [Object] object an object context.
# @macro format_text_params
#
# @example Setting a project context explicitly
# format_text("## Hello world", project: current_project)
# @example Rendering for an external surface (mailer, RSS, export)
# format_text("see #42", render_mode: :external_html)
#
# @overload format_text(object, attribute, project: @project || object.try(:project), render_mode: :in_app_html, only_path: nil, current_user: User.current, format: :rich, **)
#
# @param [Object] object an object, typically a model
# (i.e. `ActiveRecord::Base` descendent).
# @param [Symbol] attribute the method on that object.
# `#to_s` will be called on the return value.
# @macro format_text_params
#
# @example
# format_text(issue, :description, options)
#
# @return [String] the formatted text as an HTML-safe String.
def format_text(*args, object: nil, project: nil, render_mode: :in_app_html,
only_path: nil, current_user: User.current, format: :rich, **kwargs)
case args.size
when 1
attribute = nil
text = args.first
when 2
object, attribute = args
text = object.public_send(attribute).to_s
else
raise ArgumentError, "invalid arguments to format_text"
end
return "".html_safe if text.blank?
project ||= @project || object.try(:project)
resolved = RenderMode.resolve(
render_mode,
only_path:,
static_html: kwargs.delete(:static_html),
plain_text: kwargs.delete(:plain_text)
)
Renderer.format_text(
text,
**kwargs,
format:,
object:,
request: try(:request),
current_user:,
attribute:,
**resolved,
project:
)
end
# rubocop:enable Layout/LineLength
end
end