diff --git a/data/System/GraphvizPlugin.txt b/data/System/GraphvizPlugin.txt index 6bc003e..fa53149 100644 --- a/data/System/GraphvizPlugin.txt +++ b/data/System/GraphvizPlugin.txt @@ -1,4 +1,4 @@ -%META:TOPICINFO{author="ProjectContributor" comment="" date="1439247692" format="1.1" version="1"}% +%META:TOPICINFO{author="ProjectContributor" comment="" date="1439299038" format="1.1" version="1"}% ---+!! %TOPIC% %SHORTDESCRIPTION% @@ -11,7 +11,7 @@ in visual interfaces for many other domains. This plugin uses [[http://www.graphviz.org/][Graphviz's]] applications to create an image of a directed graph. The directed graph is described using a -simple text markup called "The _dot_ Language". This markup is written between +simple text markup called "The DOT Language". This markup is written between =<graphviz>= and =</graphviz>= tags or using the =%GRAPHVIZ= macro. By default, an svg image attachment is created and displayed inline, replacing the =<graphviz>= markup. @@ -27,18 +27,18 @@ There are two ways to specify a graph: 1 using the =<graphviz>= xml tag 2 using the =%GRAPHVIZ= Foswiki macro -Both can be used to generate a graph inline, that is by specifying the =graphviz= code as part of the page. Below +Both can be used to generate a graph inline, that is by specifying the DOT language as part of the page. Below examples are mostly equivalent: -digraph G {Hello->World} +digraph G {Hello[fontcolor="red"]; Hello->World} %GRAPHVIZ{ - "digraph G {Hello->World}" + "digraph G {Hello[fontcolor=\"red\"]; Hello->World}" renderer="dot" type="png" }% @@ -48,7 +48,7 @@ Both should render as %IF{"context GraphvizPluginEnabled" then="$percntGRAPHVIZ{ - \"digraph G {Hello->World}\" + \"digraph G {Hello[fontcolor=\"red\"]; Hello->World}\" renderer=\"dot\" type=\"png\" }$percnt" @@ -58,7 +58,7 @@ Both should render as when installed. The =<graphviz>= xml syntax has got the advantage of being in line with other wikis, such as dokuwiki. Also, -the =graphviz= code does not have to escape any double-quotes (="=) as is the case using the Foswiki macro way. +the DOT code does not have to escape any double-quotes (="=) as is the case using the Foswiki macro way. However using a proper =%GRAPHVIZ= macro has got the advantage of being well integrated into Foswiki's Topic Markup Language. For instance it can be properly escaped when in a [[FormattedSearch]]. @@ -66,12 +66,12 @@ For compatibility with !DirectedGraphPlugin you may use =<dot>= instead of =& ---++ Syntax -=<graphviz [params]> ... graphviz code ... <graphviz>= +=<graphviz [params]> ... DOT code ... <graphviz>= -=%GRAPHVIZ{" ... graphviz code ... " [params]}%= +=%GRAPHVIZ{" ... DOT code ... " [params]}%= | *Parameter* | *Description* | *Default* | -| =graphviz code= | this is the graph specified in the graph description language | | +| =DOT code= | this is the graph specified in the graph description language | | | =type="png|jpeg|gif|svg|svgz|pdf"= | output format | svg | | =renderer="dot|neato|twopi|circle|fdp|sfdp|patchwork"= | rendering engine | dot | | =topic="[web.]topic"= | specify the topic where to store the graph image | current topic | @@ -79,11 +79,51 @@ For compatibility with !DirectedGraphPlugin you may use =<dot>= instead of =& | =section=" ... "= | named section in =topic= that holds a graph description (only available in =%GRAPHVIZ=) | | | =library="[web.]topic"= | specify the topic where to search for images to be used in the dot graph | =topic= | | =inline="on|off"= | boolean flag to specify the way how to add an svg result to the html page; if =inline="on"= then an =<svg>= html tag is generated; otherwise an html =<img ... >= tag is used; note that for properly linking URLs in the =graphviz= graph you will need to switch this on | off | -| =expand="on|off"= | boolean flag, when switched on, will expand TML macros in the graphviz code before rendering it | off | +| =expand="on|off"= | boolean flag, when switched on, will expand TML macros in the DOT code before rendering it | off | | =style=" ... "= | css styles to be added to the output | | | =width=" ... "= | width of the image result | | | =height=" ... "= | height of the image result | | +---++ Graphviz from Table + +Instead of having to learn the dot language, nodes and edges can also be specified in Foswiki tables: + +| *Node* | *Attributes* | +| hello | color="red" | +| world | shape=box | + +| *Source* | *Label* | *Target* | *Attributes* | +| hello | | world | | + +Given these two tables where the only two tables in a topic then this would render the appropriate graph: + + +%GRAPHVIZ{ + topic="SomeTopic" + + nodestable="0" + nodecol="0" + nodeattrcol="1" + + edgestable="1" + sourcecol="0" + labelcol="1" + targetcol="2" + edgeattrcol="3" +}% + + +| *Parameter* | *Description* | *Default* | +| =table="int"= / =edgestable="int"=| index of table in =topic= to extract edges from | | +| =nodestable="int"= | index of table in =topic= to extract nodes from | | +| =nodecol="int"= | index of column that holds the node name | 0 | +| =nodeattrcol="int"= | index of colum that holds node attributs | +| =sourcecol="int"= | index of the colum holding the source node name | 0 | +| =targetcol="int"= | index of the colum holding the target node name | 1 | +| =labelcol="int"= | index of column holding the edge label | | +| =edgeattrcol="int"= | index of column holding the edge attributes | +| =preamble= | some dot code to be added before the generated node and edge code | | + ---++ Examples ---+++ Example 1 @@ -156,5 +196,5 @@ digraph G { | Home page: | Foswiki:Extensions/%TOPIC% | | Support: | Foswiki:Support/%TOPIC% | -%META:FILEATTACHMENT{name="hello_world.png" attachment="hello_world.png" attr="" comment="" date="1439247692" size="6201" user="ProjectContributor" version="1"}% -%META:FILEATTACHMENT{name="softmaint.txt" attachment="softmaint.gv" attr="h" comment="" date="1439247692" moveby="micha" movedto="System.GraphvizPlugin.softmaint.txt" movedwhen="1439247180" movefrom="System.GraphvizPlugin.softmaint.gv" size="14946" user="ProjectContributor" version="1"}% +%META:FILEATTACHMENT{name="hello_world.png" attachment="hello_world.png" attr="" comment="" date="1439299038" size="6201" user="ProjectContributor" version="1"}% +%META:FILEATTACHMENT{name="softmaint.txt" attachment="softmaint.gv" attr="h" comment="" date="1439299038" moveby="micha" movedto="System.GraphvizPlugin.softmaint.txt" movedwhen="1439247180" movefrom="System.GraphvizPlugin.softmaint.gv" size="14946" user="ProjectContributor" version="1"}% diff --git a/lib/Foswiki/Plugins/GraphvizPlugin.pm b/lib/Foswiki/Plugins/GraphvizPlugin.pm index f248eb6..0dda09f 100644 --- a/lib/Foswiki/Plugins/GraphvizPlugin.pm +++ b/lib/Foswiki/Plugins/GraphvizPlugin.pm @@ -23,8 +23,8 @@ use Foswiki::Plugins (); use Foswiki::Attrs (); use Foswiki::Plugins::WysiwygPlugin (); -our $VERSION = '0.01'; -our $RELEASE = '0.01'; +our $VERSION = '0.02'; +our $RELEASE = '11 Aug 2015'; our $SHORTDESCRIPTION = 'Draw graphs using the !GraphViz utility'; our $NO_PREFS_IN_TOPIC = 1; our $core; diff --git a/lib/Foswiki/Plugins/GraphvizPlugin/Core.pm b/lib/Foswiki/Plugins/GraphvizPlugin/Core.pm index 4c3c9b7..5d446f4 100644 --- a/lib/Foswiki/Plugins/GraphvizPlugin/Core.pm +++ b/lib/Foswiki/Plugins/GraphvizPlugin/Core.pm @@ -58,10 +58,23 @@ sub GRAPHVIZ { my $text = ""; my $attachment = $params->{attachment}; my $section = $params->{section}; + my $nodeTable = $params->{nodestable}; + my $edgeTable = $params->{edgestable} || $params->{table}; + my $doGraphFromTable = (defined $nodeTable || defined $edgeTable); + my $parser; + + if ($doGraphFromTable) { + require Foswiki::Plugins::GraphvizPlugin::TableParser; + $parser = Foswiki::Plugins::GraphvizPlugin::TableParser->new(); + my ($meta) = Foswiki::Func::readTopic($theWeb, $theTopic); + $parser->parse($meta->text, $meta); + $text = "digraph $theTopic {\n"; + $text .= "\n".($params->{preamble} || '')."\n"; + } # .... read attachment if (defined $attachment) { - return _inlineError("attachment '$attachment' not found at $theWeb.$theTopic") + return _inlineError("Error: attachment '$attachment' not found at $theWeb.$theTopic") unless Foswiki::Func::attachmentExists($theWeb, $theTopic, $attachment); $text = Foswiki::Func::readAttachment($theWeb, $theTopic, $attachment); } @@ -75,8 +88,14 @@ sub GRAPHVIZ { $thisParams->{$key} = $val; } $thisParams->{_DEFAULT} = "$theWeb.$theTopic"; - my ($obj) = Foswiki::Func::readTopic($web, $topic); - $text = $session->INCLUDE($thisParams, $obj) + my ($meta) = Foswiki::Func::readTopic($web, $topic); + $text = $session->INCLUDE($thisParams, $meta) + } + + # ... from table + elsif (defined $nodeTable || defined $edgeTable) { + $text .= $parser->getNodes($nodeTable, $params) if defined $nodeTable; + $text .= $parser->getEdges($edgeTable, $params) if defined $edgeTable; } # ... from text param @@ -84,18 +103,24 @@ sub GRAPHVIZ { $text = $params->remove("_DEFAULT") || $params->remove("text"); } + if ($doGraphFromTable) { + $text .= "}"; + } + # expand $text = Foswiki::Func::expandCommonVariables($text) if Foswiki::Func::isTrue($params->{expand}, 0); + return _inlineError("Error: no dot code") unless defined $text; + $text = Encode::encode_utf8($text); my $type = $params->{type} || "svg"; - return _inlineError("unknown type '$type'") + return _inlineError("Error: unknown type '$type'") unless $type =~ /^(svgz?|png|gif|jpe?g|pdf)$/; my $renderer = $params->{renderer} || $params->{engine} || "dot"; - return _inlineError("unknown renderer '$renderer'") + return _inlineError("Error: unknown renderer '$renderer'") unless $renderer =~ /^(dot|neato|twopi|circle|s?fdp|patchwork)$/; my $library = $params->{library}; diff --git a/lib/Foswiki/Plugins/GraphvizPlugin/TableParser.pm b/lib/Foswiki/Plugins/GraphvizPlugin/TableParser.pm new file mode 100644 index 0000000..cfaf9d0 --- /dev/null +++ b/lib/Foswiki/Plugins/GraphvizPlugin/TableParser.pm @@ -0,0 +1,98 @@ +# Plugin for Foswiki - The Free and Open Source Wiki, http://foswiki.org/ +# +# GraphvizPlugin is Copyright (C) 2015 Michael Daum http://michaeldaumconsulting.com +# +# 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, published at +# http://www.gnu.org/copyleft/gpl.html + +package Foswiki::Plugins::GraphvizPlugin::TableParser; + +use strict; +use warnings; + +use Foswiki::Tables::Reader (); +use Data::Dump qw(dump); +our @ISA = ('Foswiki::Tables::Reader'); + +sub parse { + my ($this, $text, $meta) = @_; + + $this->SUPER::parse($text, $meta); + + return $this->{result}; +} + +sub getNodes { + my ($this, $tableIndex, $params) = @_; + + my $table = $this->{result}[$tableIndex]; + + return unless $table; + + $params->{nodecol} = 0 unless defined $params->{nodecol}; + + my @lines = (); + + # generate nodes + my $index = 0; + foreach my $table ($table->getCellData) { + foreach my $row (@$table) { + $index++; + next if $index == 1;# skip header + my @attrs = (); + push @attrs, "$params->{nodeattrcol}]\"" if defined $params->{nodeattrcol}; + my $attrs = ''; + $attrs = "[".join(", ", @attrs)."]" if @attrs; + push @lines, " \"$row->[$params->{nodecol}]\" $attrs"; + } + } + + return join("\n", @lines); +} + +sub getEdges { + my ($this, $tableIndex, $params) = @_; + + my $table = $this->{result}[$tableIndex]; + + return unless $table; + + $params->{sourcecol} = 0 unless defined $params->{sourcecol}; + $params->{targetcol} = 1 unless defined $params->{targetcol}; + + my @lines = (); + + # generate edges + my $index = 0; + foreach my $table ($table->getCellData) { + foreach my $row (@$table) { + $index++; + next if $index == 1;# skip header + my @attrs = (); + push @attrs, "xlabel=\"$row->[$params->{labelcol}]\"" if defined $params->{labelcol}; + push @attrs, "$params->{edgeattrcol}]\"" if defined $params->{edgeattrcol}; + my $attrs = ''; + $attrs = "[".join(", ", @attrs)."]" if @attrs; + push @lines, " \"$row->[$params->{sourcecol}]\" -> \"$row->[$params->{targetcol}]\" $attrs"; + } + } + + return join("\n", @lines); +} + +sub line { + my ( $this, $line ) = @_; + + # ignore all non-table lines +} + +1; +