Website: actually make the changes (not just in src)
[openxpki.git] / tools / automated_wf_visual / wf_graphs.pl
blobede46c8e3b9f3f4338d8ff144dc971a2928e194e
1 #!/usr/bin/perl -w
3 # Written by Petr Grigoriev the OpenXPKI project 2007
4 # Copyright (c) 2007 by The OpenXPKI Project
5 # $Revision$
9 use strict;
10 use warnings;
11 use utf8;
12 use English;
13 use Encode;
14 use XML::Simple;
15 use Pod::Usage;
16 use Getopt::Long;
17 use Locale::Messages qw (:locale_h :libintl_h nl_putenv bind_textdomain_filter);
18 use POSIX qw (setlocale);
19 use Image::Size 'html_imgsize';
23 # command line options
24 my $man = 0;
25 my $help = 0;
26 my $html = 0;
28 # file name for the website document
29 my $html_name = 'workflow-graphs.html';
31 #default i18n language
32 my $language = "en_GB";
34 # default list of workflow definitions
35 # assumed to be in the corresponding SVN directory
36 my $wf_all = '../../trunk/deployment/etc/templates/default/workflow.xml';
38 # default directory for html pages and pictures
39 # assumed to be in the corresponding SVN directory (website leaf)
40 my $dot_dir = '../../www.openxpki.org/trunk/src/htdocs/docs/wf-pictures';
42 my $pic_format = 'png';
44 if( GetOptions( 'help|?' => \$help,
45 'man' => \$man,
46 'def=s' => \$wf_all,
47 'directory=s' => \$dot_dir,
48 'format=s' => \$pic_format,
49 'lang=s' => \$language,
50 'html' => \$html,
53 pod2usage(1) if $help;
54 pod2usage(-exitstatus => 0, -verbose => 2) if $man;
55 } else {
56 print STDERR "See manual: wf_grapfs.pl --man\n"; exit 0;
58 #print "LANGUAGE <".$language.">\n"; exit 1;
59 my $loc = "${language}.UTF-8";
60 setlocale(LC_MESSAGES, $loc);
61 setlocale(LC_TIME, $loc);
62 nl_putenv("LC_MESSAGES=$loc");
63 nl_putenv("LC_TIME=$loc");
64 textdomain ('openxpki');
65 bindtextdomain ('openxpki' => '/usr/local/share/locale');
66 bind_textdomain_codeset("openxpki", "UTF-8");
67 #my $msg=gettext ("I18N_OPENXPKI_SERVER_AUTHENTICATION_PASSWORD_LOGIN_FAILED");
68 #print STDERR $msg."\n";
69 #exit 1;
71 #print STDERR $dot_dir."\n"; exit 1;
73 #subdirectory for html references
74 my $ref_dir = $dot_dir;
75 $ref_dir =~ s/^.*\///;
77 # call for a graphviz picture creator
78 my $genpic = "dot -T" . $pic_format . " -o ";
80 # get directory with workflow definitions
81 # it assumed to be the same as the directory with definition list
82 # if wf_all is /x/y/z then def_dir is /x/y/
84 my $def_dir = $wf_all;
85 $def_dir =~ s/[^\/]*$//;
87 #print STDERR $def_dir."\n";exit 1;
89 # get a list of workflow definitions
90 my $config = XMLin($wf_all);
92 my $wf_config = $config->{'workflows'}->{'xi:include'};
93 if($html){
94 open(WEBFILE, ">", $dot_dir ."/../". $html_name ) or
95 die "Opening file $html_name in $dot_dir: $!";
96 print WEBFILE "<%attr>\n";
97 print WEBFILE "title => 'OpenXPKI workflows graphical representation'\n";
98 print WEBFILE "</%attr>\n\n";
99 print WEBFILE "<h1>OpenXPKI workflows' graphical representation</h1>\n";
100 print WEBFILE "<p>\n";
101 print WEBFILE "Here are links to graphs describing main OpenXPKI workflows\n";
102 print WEBFILE "</p>\n\n"
105 foreach my $wf_file_hash (@{$wf_config}){
106 my $wf_def = $wf_file_hash->{'href'};
108 # dot file has the same name but suffix 'dot' instead of 'xml'
109 my $wf_dot = $wf_def;
110 $wf_dot =~ s/xml$/dot/;
112 # build a full path to workflow definition file
113 $wf_def = $def_dir . $wf_def;
115 # we get workflow title after parsing it's definition
116 my $wf_title='';
117 my $data=wf_visualize($wf_def,\$wf_title);
118 # print STDERR "TITLE ". $wf_title."\n";
119 $wf_title = gettext($wf_title);
120 print STDERR "Processing <".$wf_title.">\n";
122 # now we build the full path to dot-file and create it
123 my $dot_file= $dot_dir . "/" . $wf_dot;
124 open(DOTFILE, ">:encoding(UTF8)", $dot_file ) or
125 die "Opening file $wf_dot in $dot_dir: $!";
126 print DOTFILE $data;
127 close(DOTFILE);
129 # create graphviz command line and compile a picture
130 my $generator =
131 $genpic . " " . $dot_file . "." . $pic_format . " < " . $dot_file;
132 my $picture_done=`$generator`;
134 # create html file with the picture and write a link to it
135 # to the main html file
136 if($html){
137 open(WEBLINKFILE, ">", $dot_file . ".html") or
138 die "Opening file $dot_file" . ".html in $dot_dir: $!";
139 print WEBLINKFILE "<%attr>\n";
140 print WEBLINKFILE "title => 'OpenXPKI workflows graphical representation'\n";
141 print WEBLINKFILE "</%attr>\n\n";
142 print WEBLINKFILE "<h1>OpenXPKI workflows' graphical representation</h1>\n";
143 print WEBLINKFILE "<h2> Workflow ". $wf_title ."</h2>\n\n";
144 print WEBLINKFILE "<p>\nAutorun states are yellow.\n";
145 print WEBLINKFILE "You may need to pan or scroll to view a picture.\n</p>\n\n";
146 my $image_name = $dot_file . "." . $pic_format;
147 my $image_size = html_imgsize($image_name);
148 print WEBLINKFILE '<img ' . $image_size .
149 ' src="'. $wf_dot . '.' . $pic_format . '"' .
150 'alt="' . $wf_title . '" ' .
151 'title="' . $wf_title . '" ' .
152 '/>'."\n";
153 close(WEBLINKFILE);
154 print WEBFILE '<a href="' . $ref_dir . '/' . $wf_dot . '.html">' .
155 $wf_title . '</a><br/>'."\n";
159 if($html){
160 close(WEBFILE);
163 exit 1;
166 sub wf_visualize
168 my $wf_xml = shift;
169 my $wf_title = shift;
170 my %wf_parsed = wfparse($wf_xml);
171 my $wf = \%wf_parsed;
173 #return type for picture caption in html file
174 ${$wf_title}=$wf->{'type'};
176 my $data = wf_make_graph($wf);
177 return $data;
180 sub wf_make_graph
182 my $wf = shift;
184 # Configuration variables for special states colors
185 my $SUCCESS_STATE = 'SUCCESS';
186 my $SUCCESS_COLOR = 'darkgreen';
187 my $FAILURE_STATE = 'FAILURE';
188 my $FAILURE_COLOR = 'firebrick';
189 my $AUTORUN_COLOR = '"yellow"';
191 my $type=$wf->{'type'};
192 my $data=get_preamble($type);
194 my $nodes="";
195 my $edges="";
196 foreach my $state (keys %{$wf->{'states'}}){
197 $nodes .= "$state ";
198 if(defined $wf->{'states'}->{$state} ->{'autorun'}){
199 $nodes .= "[ fillcolor = ".$AUTORUN_COLOR."];\n";
200 } else {
201 $nodes .= ";\n";
203 if( defined $wf->{'states'}->{$state}->{'actions'}){
204 foreach my $action (keys %{$wf->{'states'}->
205 {$state}->{'actions'}}
207 my $edge_label=' [label="'.$action;
208 my $result=$wf->
209 {'states'}->{$state}->
210 {'actions'}->{$action}->{'result'};
211 $edges .= $state ." -> ".$result;
212 if( defined $wf->{'states'}->
213 {$state}->{'actions'}->
214 {$action}->{'conditions'}){
215 $edge_label .='\\n if ';
216 foreach my $condition (keys %{$wf->
217 {'states'}->{$state}->
218 {'actions'}->{$action}->
219 {'conditions'}}){
220 $edge_label .= " $condition \\n ";
223 $edges .= $edge_label .'"];'."\n";
227 # finalize the graphic file
228 $data .= $nodes;
229 $data .= $edges;
230 $data .= "}\n";
231 return $data;
234 sub wfparse
236 my $wf_xml=shift;
237 $config = XMLin($wf_xml);
238 my $wf = {};
240 my $wf_type = $config->{'type'};
241 # print "TYPE $wf_type \n";
242 $wf->{'type'} = $wf_type;
243 $wf->{'states'} = {};
244 my $wf_states = $config->{'state'};
245 foreach my $state (keys %{$wf_states}){
246 # print " STATE $state";
247 $wf->{'states'}->{$state} = {};
248 my $autorun = $wf_states->{$state}->{'autorun'};
249 if(defined $autorun){
250 # print " AUTORUN $autorun \n";
251 $wf->{'states'}->{$state} ->{'autorun'}='yes';
252 } else {
253 # print "\n";
255 my $state_actions = $wf_states->{$state}->{'action'};
256 if( ref($state_actions) ne 'HASH'){
257 # print " NO ACTIONS\n";
258 } else {
259 $wf->{'states'}->{$state}->{'actions'} = {};
260 if( defined $state_actions->{'name'}){
261 my $resulting_state =
262 $state_actions->{'resulting_state'};
263 my $name =
264 $state_actions->{'name'};
265 my $conditions =
266 $state_actions->{'condition'};
267 # print " ACTION $name \n";
268 # print " RESULTING STATE $resulting_state \n";
269 $wf->{'states'}->{$state}->{'actions'}->{$name} = {};
270 $wf->{'states'}->
271 {$state}->
272 {'actions'}->
273 {$name}->{'result'} = $resulting_state;
274 if( defined $conditions ){
275 $wf->{'states'} -> {$state} ->
276 {'actions'} -> {$name} ->
277 {'conditions'} = {};
278 if( defined $conditions->{'name'}){
279 my $condition = $conditions->{'name'};
280 # print " CONDITION $condition \n";
281 $wf->{'states'} -> {$state}->
282 {'actions'} -> {$name}->
283 {'conditions'} -> {$condition} = 'if';
284 } else {
285 foreach my $condition (keys %{$conditions}){
286 # print " CONDITION $condition \n";
287 $wf->{'states'} -> {$state}->
288 {'actions'} -> {$name}->
289 {'conditions'} -> {$condition} = 'if';
294 } else {
295 foreach my $action (keys %{$state_actions}){
296 # print " ACTION $action \n";
297 my $resulting_state =
298 $state_actions->{$action}->{'resulting_state'};
299 my $conditions =
300 $state_actions->{$action}->{'condition'};
301 # print " RESULTING STATE $resulting_state \n";
302 $wf->{'states'}->{$state}->{'actions'}->{$action} = {};
303 $wf->{'states'}->
304 {$state}->
305 {'actions'}->
306 {$action}->{'result'} = $resulting_state;
307 if( defined $conditions ){
308 $wf->{'states'} -> {$state} ->
309 {'actions'} -> {$action} ->
310 {'conditions'} = {};
311 if( defined $conditions->{'name'}){
312 my $condition = $conditions->{'name'};
313 # print " CONDITION $condition \n";
314 $wf->{'states'} -> {$state}->
315 {'actions'} -> {$action}->
316 {'conditions'} -> {$condition} = 'if';
317 } else {
318 foreach my $condition (keys %{$conditions}){
319 # print " CONDITION $condition \n";
320 $wf->{'states'} -> {$state}->
321 {'actions'} -> {$action}->
322 {'conditions'} -> {$condition} = 'if';
330 return %{$wf};
333 sub get_preamble {
334 my $type = shift;
335 $type = '"'.gettext($type).'"';
336 my $label = $type;
337 my $RANKDIR = '"TB"'; #rank direction, LR or TB (left to right or top to bottom)
338 my $RATIO = 0.71; # useful for A4 paper
339 my $ROTATE = 0; # either 0 or 90
340 my $GRAPH_FONTNAME = '"Futura-CondensedMedium"';
341 my $GRAPH_FONTSIZE = 24;
342 my $LABEL_LOCATION = "b"; # t for top or b for bottom
343 my $MARGIN = 1;
344 my $NODE_SHAPE = '"rect"';
345 my $NODE_STYLE = '"filled"';
346 my $NODE_FONTNAME = $GRAPH_FONTNAME;
347 my $NODE_FONTSIZE = 16;
348 my $EDGE_FONTNAME = $GRAPH_FONTNAME;
349 my $EDGE_FONTSIZE = 16;
351 my $preamble ="digraph $type" . " {\n".
352 "graph [ rankdir=$RANKDIR,".
353 "ratio=$RATIO,".
354 "rotate=$ROTATE,".
355 "center=1,".
356 "fontname=$GRAPH_FONTNAME,".
357 "fontsize=$GRAPH_FONTSIZE,".
358 "labeljust=c,".
359 "labelloc=$LABEL_LOCATION,".
360 "margin=$MARGIN,".
361 "label=$label".
362 "\n];\n".
363 "node [shape=$NODE_SHAPE,".
364 "style=$NODE_STYLE,".
365 "fontname=$NODE_FONTNAME,".
366 "fontsize=$NODE_FONTSIZE".
367 "];\n".
368 "edge [fontname=$EDGE_FONTNAME,".
369 "fontsize=$EDGE_FONTSIZE".
370 "];\n";
371 return $preamble;
377 __END__
379 =head1 NAME
381 wf_graphs.pl - workflow graph generator
383 =head1 USAGE
385 wf_graphs.pl [options]
387 Options:
388 --help brief help message
389 --man full documentation
390 --html create html files for website
391 with links to pictures
392 --directory <dir> create pictures in directory 'dir'
393 --def <file> use 'file' as a list of workflow
394 definitions
395 --format <type> format of the graphics files to be created
396 (use only types supported by graphviz)
397 --lang <locale> locale to print picture captions
398 supports en_GB and de_DE
400 =head1 DESCRIPTION
402 B<wf_graphs.pl> creates dot-files for graphviz program
403 to create pictures of all workflow types
404 specified in the files with workflow definitions.
405 B<workflow.xml> is used by default as a list of files
406 with workflow definitions which will be processed.
407 Workflow definition files are being parsed and then the corresponding
408 dot-files for graphviz are created. Autorun states are marked yellow.
409 Using I18N translations is supported for B<en_GB> (default) and B<de_DE>.
410 Definition files are supposed to have 'xml'
411 extension. Names for dot-files are built by substituting
412 extension 'dot' instead of 'xml'. After dot-files are created
413 graphviz B<dot> utility is called to create pictures in desirable format.
414 Optionally html-files with links to pictures are created in the same directory
415 and the main html-file with link list of workflow types in the parent directory.
417 The name of the main html-file is predefined: B<workflow-graphs.html>
419 The default list of workflow definitions is assumed to be
420 in the corresponding SVN directory:
422 ../../trunk/deployment/etc/templates/default/workflow.xml
424 Default directory for html pages and pictures is
425 assumed to be in the corresponding SVN directory (website leaf)
427 ../../www.openxpki.org/trunk/src/htdocs/docs/wf-pictures
429 =head1 OPTIONS
431 =over 6
433 =item B<--man>
435 Use it to read all the manual
437 =item B<--help>
439 Use it to get information on command line options only
441 =item B<--html>
443 Creates html file 'workflow-graphs.html' and child
444 html-pages with pictures.
446 =item B<--directory> dir
448 Makes it possible to create dot-files in another directory.
449 If B<--html> option is used html-files with pictures will also be
450 created in the given directory but the main html-file with links to
451 picture pages will be created in the parent directory.
453 =item B<--def> file
455 Use 'file' as a list of workflow definitions which will
456 be processed to create their graphs. The file format must be
457 the same as in OpenXPKI workflow.xml:
459 <workflow_config id="default">
460 <workflows>
461 <configfile>workflow_def_1.xml</configfile>
462 <configfile>workflow_def_2.xml</configfile>
463 ...........................................
464 </workflows>
465 ..............................................
466 </workflow_config>
468 The path of the specified file will be used to search
469 workflow-definitions files.
471 =item B<--format> type
473 Generate graphics files in B<type> format.
474 As B<dot> is called only formats supported by B<graphviz>
475 make sense.
477 =item B<--lang> locale
479 Uses B<locale> to select language for picture captions.
480 Supports en_GB and de_DE translations of OpenXPKI tags.
481 Assumes i18n is installed in /usr/local/share/locale.
483 =back
485 =head1 Built-in-functions
487 =over 6
489 =item B<wf_visualize>
491 Calls wfparse and wf_make_graph for passed filename with
492 workflow definitions. Returns the output of wf_make_graph.
493 Writes the workflow type to the variable which link is
494 passed to the function as the second parameter .
496 =item B<wf_make_graph>
498 Creates data to write in dot-file using passed hash with workflow
499 definitions. Autorun states are marked yellow.
501 =item B<get_preamble>
503 Creates a header of the dot-file and returns it.
504 The only argument is used as a label for the picture.
505 Picture options are concentrated in this function.
507 =item B<wfparse>
509 Parses the passed xml-file with workflow definitions and creates
510 a hash with those definitions. Returns the hash.
512 An example is self-explanatory:
514 { 'type' => 'WORKFLOW TYPE',
515 'states' => {
516 'STATE_1' => {
517 autorun => 'yes',
518 actions => {
519 'ACTION_1' => {
520 'result' => 'RESULTING_STATE_1',
521 'conditions' => {
522 'CONDITION_1' => 'if',
523 'CONDITION_2' => 'if',
526 'ACTION_2' => {
527 'result' => 'RESULTING_STATE_2',
528 'conditions' => {
529 'CONDITION_3' => 'if',
530 'CONDITION_4' => 'if',
534 'STATE_2' => {
535 autorun => 'yes',
536 actions => {
537 'ACTION_3' => {
538 'result' => 'RESULTING_STATE_3',
539 'conditions' => {
540 'CONDITION_5' => 'if',
541 'CONDITION_6' => 'if',
544 'ACTION_4' => {
545 'result' => 'RESULTING_STATE_4',
546 'conditions' => {
547 'CONDITION_7' => 'if',
548 'CONDITION_8' => 'if',
554 =back
556 =head1 Examples
558 To get pictures of the current SVN-snapshot go to the directory
559 where wf_graphs is stored ( tools/automated_wf_visual ) and say:
561 ./wf_graphs.pl --directory .
563 Set of dot-files and png-pictures will be created in the current
564 directory.
566 To update pictures of the current SVN snapshot in the website
567 source directory (new set of files must be then registered with svn
568 B<add> and B<delete> commands):
570 ./wf_graphs.pl --html
575 =cut