summaryrefslogtreecommitdiffstats
path: root/lib/redmine/wiki_formatting/common_mark
diff options
context:
space:
mode:
Diffstat (limited to 'lib/redmine/wiki_formatting/common_mark')
-rw-r--r--lib/redmine/wiki_formatting/common_mark/alerts_icons_filter.rb63
-rw-r--r--lib/redmine/wiki_formatting/common_mark/formatter.rb9
-rw-r--r--lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb70
3 files changed, 134 insertions, 8 deletions
diff --git a/lib/redmine/wiki_formatting/common_mark/alerts_icons_filter.rb b/lib/redmine/wiki_formatting/common_mark/alerts_icons_filter.rb
new file mode 100644
index 000000000..27429d778
--- /dev/null
+++ b/lib/redmine/wiki_formatting/common_mark/alerts_icons_filter.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+# Redmine - project management software
+# Copyright (C) 2006- Jean-Philippe Lang
+#
+# 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.
+
+module Redmine
+ module WikiFormatting
+ module CommonMark
+ # Defines the mapping from alert type (from CSS class) to SVG icon name.
+ # These icon names must correspond to IDs in your SVG sprite sheet (e.g., icons.svg).
+ ALERT_TYPE_TO_ICON_NAME = {
+ 'note' => 'help',
+ 'tip' => 'bulb',
+ 'warning' => 'warning',
+ 'caution' => 'alert-circle',
+ 'important' => 'message-report',
+ }.freeze
+
+ class AlertsIconsFilter < HTML::Pipeline::Filter
+ def call
+ doc.search("p.markdown-alert-title").each do |node|
+ parent_node = node.parent
+ parent_class_attr = parent_node['class'] # e.g., "markdown-alert markdown-alert-note"
+ next unless parent_class_attr
+
+ # Extract the specific alert type (e.g., "note", "tip", "warning")
+ # from the parent div's classes.
+ match_data = parent_class_attr.match(/markdown-alert-(\w+)/)
+ next unless match_data && match_data[1] # Ensure a type is found
+
+ alert_type = match_data[1]
+
+ # Get the corresponding icon name from our map.
+ icon_name = ALERT_TYPE_TO_ICON_NAME[alert_type]
+ next unless icon_name # Skip if no specific icon is defined for this alert type
+
+ icon_html = ApplicationController.helpers.sprite_icon(icon_name, node.text)
+
+ if icon_html
+ # Replace the existing text node with the icon HTML and label (text).
+ node.children.first.replace(icon_html)
+ end
+ end
+ doc
+ end
+ end
+ end
+ end
+end
diff --git a/lib/redmine/wiki_formatting/common_mark/formatter.rb b/lib/redmine/wiki_formatting/common_mark/formatter.rb
index aab8eed8b..8b7a18394 100644
--- a/lib/redmine/wiki_formatting/common_mark/formatter.rb
+++ b/lib/redmine/wiki_formatting/common_mark/formatter.rb
@@ -18,7 +18,6 @@
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
require 'html/pipeline'
-require 'task_list/filter'
module Redmine
module WikiFormatting
@@ -32,6 +31,10 @@ module Redmine
tagfilter: true,
autolink: true,
footnotes: true,
+ header_ids: nil,
+ tasklist: true,
+ shortcodes: false,
+ alerts: true,
}.freeze,
# https://github.com/gjtorikian/commonmarker#parse-options
@@ -41,7 +44,9 @@ module Redmine
# https://github.com/gjtorikian/commonmarker#render-options
commonmarker_render_options: {
unsafe: true,
+ github_pre_lang: false,
hardbreaks: Redmine::Configuration['common_mark_enable_hardbreaks'] == true,
+ tasklist_classes: true,
}.freeze,
commonmarker_plugins: {
syntax_highlighter: nil
@@ -54,7 +59,7 @@ module Redmine
SyntaxHighlightFilter,
FixupAutoLinksFilter,
ExternalLinksFilter,
- TaskList::Filter
+ AlertsIconsFilter
], PIPELINE_CONFIG
class Formatter
diff --git a/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb b/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb
index cdefc372b..af72adc32 100644
--- a/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb
+++ b/lib/redmine/wiki_formatting/common_mark/sanitization_filter.rb
@@ -68,6 +68,26 @@ module Redmine
end
}
+ # Allow class on div and p tags only for alert blocks
+ # (must be exactly: "markdown-alert markdown-alert-*" for div, and "markdown-alert-title" for p)
+ (allowlist[:attributes]["div"] ||= []) << "class"
+ (allowlist[:attributes]["p"] ||= []) << "class"
+ allowlist[:transformers].push lambda{|env|
+ node = env[:node]
+ return unless node.element?
+
+ case node.name
+ when 'div'
+ unless /\Amarkdown-alert markdown-alert-[a-z]+\z/.match?(node['class'])
+ node.remove_attribute('class')
+ end
+ when 'p'
+ unless node['class'] == 'markdown-alert-title'
+ node.remove_attribute('class')
+ end
+ end
+ }
+
# Allow table cell alignment by style attribute
#
# Only necessary if we used the TABLE_PREFER_STYLE_ATTRIBUTES
@@ -78,20 +98,58 @@ module Redmine
# allowlist[:attributes]["td"] = %w(style)
# allowlist[:css] = { properties: ["text-align"] }
- # Allow `id` in a and li elements for footnotes
- # and remove any `id` properties not matching for footnotes
+ # Allow `id` in a elements for footnotes
allowlist[:attributes]["a"].push "id"
- allowlist[:attributes]["li"] = %w(id)
+ # Remove any `id` property not matching for footnotes
allowlist[:transformers].push lambda{|env|
node = env[:node]
- return unless node.name == "a" || node.name == "li"
+ return unless node.name == "a"
return unless node.has_attribute?("id")
- return if node.name == "a" && node["id"] =~ /\Afnref-\d+\z/
- return if node.name == "li" && node["id"] =~ /\Afn-\d+\z/
+ return if node.name == "a" && node["id"] =~ /\Afnref(-\d+){1,2}\z/
node.remove_attribute("id")
}
+ # allow `id` in li element for footnotes
+ # allow `class` in li element for task list items
+ allowlist[:attributes]["li"] = %w(id class)
+ allowlist[:transformers].push lambda{|env|
+ node = env[:node]
+ return unless node.name == "li"
+
+ if node.has_attribute?("id") && !(node["id"] =~ /\Afn-\d+\z/)
+ node.remove_attribute("id")
+ end
+
+ if node.has_attribute?("class") && node["class"] != "task-list-item"
+ node.remove_attribute("class")
+ end
+ }
+
+ # allow input type = "checkbox" with class "task-list-item-checkbox"
+ # for task list items
+ allowlist[:elements].push('input')
+ allowlist[:attributes]["input"] = %w(class type)
+ allowlist[:transformers].push lambda{|env|
+ node = env[:node]
+
+ return unless node.name == "input"
+ return if node['type'] == "checkbox" && node['class'] == "task-list-item-checkbox"
+
+ node.replace(node.children)
+ }
+
+ # allow class "contains-task-list" on ul for task list items
+ allowlist[:attributes]["ul"] = %w(class)
+ allowlist[:transformers].push lambda{|env|
+ node = env[:node]
+
+ return unless node.name == "ul"
+ return if node["class"] == "contains-task-list"
+
+ node.remove_attribute("class")
+ }
+
# https://github.com/rgrove/sanitize/issues/209
allowlist[:protocols].delete("a")
allowlist[:transformers].push lambda{|env|