discourse/plugins/discourse-graphviz/plugin.rb
Kris 59d53dc18a
UX: add admin sidebar icons for preinstalled plugins (#36764)
Currently all our preinstalled plugins use the default "gear" icon from
fontawesome — this PR adds unique icons for each of them using a new
plugin API method, `setAdminPluginIcon()`. Plugins without an icon
defined will still fall back to the gear icon.


Before:
<img width="250" alt="image"
src="https://github.com/user-attachments/assets/06b5c6e7-0aae-44f8-b8ee-1486b98bfc6b"
/>


After: 
<img width="250" alt="image"
src="https://github.com/user-attachments/assets/46d45d4b-6c75-4473-ae53-1a0e71c4d6fb"
/>
2025-12-18 16:39:00 -05:00

90 lines
3 KiB
Ruby

# frozen_string_literal: true
# name: discourse-graphviz
# about: Provides the ability to add graphs to posts using the DOT language.
# meta_topic_id: 97554
# version: 0.0.1
# authors: Maja Komel, Joffrey Jaffeux
# url: https://github.com/discourse/discourse/tree/main/plugins/discourse-graphviz
enabled_site_setting :discourse_graphviz_enabled
register_svg_icon "diagram-project"
register_asset "stylesheets/common/graphviz.scss"
module ::DiscourseGraphviz
def self.context
context = MiniRacer::Context.new
context.load("#{Rails.root}/plugins/discourse-graphviz/public/javascripts/viz-3.0.1.js")
context
end
def self.allowed_svg_xpath
@@allowed_svg_xpath ||=
"//*[#{UploadCreator::ALLOWED_SVG_ELEMENTS.map { |e| "name()!='#{e}'" }.join(" and ")}]"
end
end
after_initialize do
on(:before_post_process_cooked) do |doc, post|
if SiteSetting.discourse_graphviz_enabled
doc
.css("div.graphviz")
.each do |graph|
engine = graph.attribute("data-engine").value
svg_graph =
begin
DiscourseGraphviz.context.eval(
"vizRenderStringSync(#{graph.children[0].content.inspect}, {engine: '#{engine}'})",
)
rescue StandardError
nil
end
next if svg_graph.nil?
should_use_svg = SiteSetting.graphviz_default_svg
should_use_svg ||= graph.classes.include?("graphviz-svg")
should_use_svg &&= !graph.classes.include?("graphviz-no-svg")
if should_use_svg
# Changing to Nokogiri::HTML5.fragment returns `nil` for `.css('svg')`
# rubocop:todo Discourse/NoNokogiriHtmlFragment
new_graph_node = Nokogiri::HTML.fragment(svg_graph).css("svg").first
# rubocop:enable Discourse/NoNokogiriHtmlFragment
new_graph_node["class"] = "graphviz-svg-render"
new_graph_node.xpath(DiscourseGraphviz.allowed_svg_xpath).remove
graph.replace new_graph_node
next
end
tmp_svg = Tempfile.new(%w[svgfile .svg])
tmp_png = Tempfile.new(%w[vizgraph- .png])
tmp_svg.write(svg_graph)
tmp_svg.rewind
graph_title =
Nokogiri
.parse(svg_graph)
.at("//comment()[contains(.,'Title')]")
&.content
&.match(/Title:\s(?<title>.+)\sPages:/)
&.[](:title)
filename = graph_title != "%0" ? graph_title : File.basename(tmp_png.path)
Discourse::Utils.execute_command("convert", "-density", "300", tmp_svg.path, tmp_png.path)
upload = UploadCreator.new(tmp_png, filename).create_for(-1)
# replace div.graphviz with image node
new_graph_node = Nokogiri::XML::Node.new("img", doc)
new_graph_node["src"] = upload.url
new_graph_node["alt"] = filename
graph.replace new_graph_node
tmp_svg.close!
tmp_png.close!
end
end
end
end