← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 19:05:14 2015
Reported on Fri Jul 31 19:08:10 2015

Filename/var/www/foswiki11/lib/Foswiki/Plugins/DirectedGraphPlugin.pm
StatementsExecuted 206 statements in 6.18ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
13112.68ms2.78msFoswiki::Plugins::DirectedGraphPlugin::::commonTagsHandlerFoswiki::Plugins::DirectedGraphPlugin::commonTagsHandler
1111.97ms2.38msFoswiki::Plugins::DirectedGraphPlugin::::BEGIN@31Foswiki::Plugins::DirectedGraphPlugin::BEGIN@31
111267µs15.9msFoswiki::Plugins::DirectedGraphPlugin::::initPluginFoswiki::Plugins::DirectedGraphPlugin::initPlugin
11155µs189µsFoswiki::Plugins::DirectedGraphPlugin::::_loadHashCodesFoswiki::Plugins::DirectedGraphPlugin::_loadHashCodes
175136µs36µsFoswiki::Plugins::DirectedGraphPlugin::::_writeDebugFoswiki::Plugins::DirectedGraphPlugin::_writeDebug
11117µs39µsFoswiki::Plugins::DirectedGraphPlugin::::BEGIN@30Foswiki::Plugins::DirectedGraphPlugin::BEGIN@30
11114µs27µsFoswiki::Plugins::DirectedGraphPlugin::::BEGIN@28Foswiki::Plugins::DirectedGraphPlugin::BEGIN@28
11114µs47µsFoswiki::Plugins::DirectedGraphPlugin::::BEGIN@197Foswiki::Plugins::DirectedGraphPlugin::BEGIN@197
1115µs5µsFoswiki::Plugins::DirectedGraphPlugin::::BEGIN@33Foswiki::Plugins::DirectedGraphPlugin::BEGIN@33
1114µs4µsFoswiki::Plugins::DirectedGraphPlugin::::BEGIN@35Foswiki::Plugins::DirectedGraphPlugin::BEGIN@35
1114µs4µsFoswiki::Plugins::DirectedGraphPlugin::::BEGIN@36Foswiki::Plugins::DirectedGraphPlugin::BEGIN@36
1114µs4µsFoswiki::Plugins::DirectedGraphPlugin::::BEGIN@34Foswiki::Plugins::DirectedGraphPlugin::BEGIN@34
1113µs3µsFoswiki::Plugins::DirectedGraphPlugin::::BEGIN@38Foswiki::Plugins::DirectedGraphPlugin::BEGIN@38
0000s0sFoswiki::Plugins::DirectedGraphPlugin::::__ANON__[:323]Foswiki::Plugins::DirectedGraphPlugin::__ANON__[:323]
0000s0sFoswiki::Plugins::DirectedGraphPlugin::::__ANON__[:326]Foswiki::Plugins::DirectedGraphPlugin::__ANON__[:326]
0000s0sFoswiki::Plugins::DirectedGraphPlugin::::__ANON__[:459]Foswiki::Plugins::DirectedGraphPlugin::__ANON__[:459]
0000s0sFoswiki::Plugins::DirectedGraphPlugin::::_attachmentExistsFoswiki::Plugins::DirectedGraphPlugin::_attachmentExists
0000s0sFoswiki::Plugins::DirectedGraphPlugin::::_deleteAttachFoswiki::Plugins::DirectedGraphPlugin::_deleteAttach
0000s0sFoswiki::Plugins::DirectedGraphPlugin::::_handleDotFoswiki::Plugins::DirectedGraphPlugin::_handleDot
0000s0sFoswiki::Plugins::DirectedGraphPlugin::::_make_pathFoswiki::Plugins::DirectedGraphPlugin::_make_path
0000s0sFoswiki::Plugins::DirectedGraphPlugin::::_showErrorFoswiki::Plugins::DirectedGraphPlugin::_showError
0000s0sFoswiki::Plugins::DirectedGraphPlugin::::afterRenameHandlerFoswiki::Plugins::DirectedGraphPlugin::afterRenameHandler
0000s0sFoswiki::Plugins::DirectedGraphPlugin::::wrapupTagsHandlerFoswiki::Plugins::DirectedGraphPlugin::wrapupTagsHandler
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# Plugin for Foswiki - The Free and Open Source Wiki, http://foswiki.org/
2#
3# Copyright (C) 2004-2005 Cole Beck, cole.beck@vanderbilt.edu
4# Copyright (C) 2006-2008 TWiki Contributors
5# Copyright (C) 2009-2010 George Clark and other Foswiki Contributors
6#
7# This program is free software; you can redistribute it and/or
8# modify it under the terms of the GNU General Public License
9# as published by the Free Software Foundation; either version 2
10# of the License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details, published at
16# http://www.gnu.org/copyleft/gpl.html
17#
18# =========================
19#
20# This plugin creates a png file by using the graphviz dot command.
21# See http://www.graphviz.org/ for more information.
22# Note that png files created with this plugin can only be deleted manually;
23# it stays there even after the dot tags are removed.
24
25package Foswiki::Plugins::DirectedGraphPlugin;
26
27# =========================
28231µs239µs
# spent 27µs (14+13) within Foswiki::Plugins::DirectedGraphPlugin::BEGIN@28 which was called: # once (14µs+13µs) by Foswiki::Plugin::BEGIN@2.11 at line 28
use strict;
# spent 27µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@28 # spent 13µs making 1 call to strict::import
29
30236µs262µs
# spent 39µs (17+22) within Foswiki::Plugins::DirectedGraphPlugin::BEGIN@30 which was called: # once (17µs+22µs) by Foswiki::Plugin::BEGIN@2.11 at line 30
use Digest::MD5 qw( md5_hex );
# spent 39µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@30 # spent 22µs making 1 call to Exporter::import
312151µs22.43ms
# spent 2.38ms (1.97+416µs) within Foswiki::Plugins::DirectedGraphPlugin::BEGIN@31 which was called: # once (1.97ms+416µs) by Foswiki::Plugin::BEGIN@2.11 at line 31
use Storable qw(nstore retrieve nfreeze thaw);
# spent 2.38ms making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@31 # spent 43µs making 1 call to Exporter::import
32
33220µs15µs
# spent 5µs within Foswiki::Plugins::DirectedGraphPlugin::BEGIN@33 which was called: # once (5µs+0s) by Foswiki::Plugin::BEGIN@2.11 at line 33
use File::Path ();
# spent 5µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@33
34219µs14µs
# spent 4µs within Foswiki::Plugins::DirectedGraphPlugin::BEGIN@34 which was called: # once (4µs+0s) by Foswiki::Plugin::BEGIN@2.11 at line 34
use File::Temp ();
# spent 4µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@34
35219µs14µs
# spent 4µs within Foswiki::Plugins::DirectedGraphPlugin::BEGIN@35 which was called: # once (4µs+0s) by Foswiki::Plugin::BEGIN@2.11 at line 35
use File::Spec ();
# spent 4µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@35
36219µs14µs
# spent 4µs within Foswiki::Plugins::DirectedGraphPlugin::BEGIN@36 which was called: # once (4µs+0s) by Foswiki::Plugin::BEGIN@2.11 at line 36
use File::Copy (); # Used for Foswiki attach API bypass
# spent 4µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@36
37
382395µs13µs
# spent 3µs within Foswiki::Plugins::DirectedGraphPlugin::BEGIN@38 which was called: # once (3µs+0s) by Foswiki::Plugin::BEGIN@2.11 at line 38
use Foswiki::Func ();
# spent 3µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@38
39
401900nsour $VERSION = '1.13';
411300nsour $RELEASE = '1.13';
42
43#
44# # Short description of this plugin
45# # One line description, is shown in the %SYSTEMWEB%.TextFormattingRules topic:
461200nsour $SHORTDESCRIPTION = 'Draw graphs using the !GraphViz utility';
47
48#
49# # You must set $NO_PREFS_IN_TOPIC to 0 if you want your plugin to use
50# # preferences set in the plugin topic. This is required for compatibility
51# # with older plugins, but imposes a significant performance penalty, and
52# # is not recommended. Instead, leave $NO_PREFS_IN_TOPIC at 1 and use
53# # =$Foswiki::cfg= entries set in =LocalSite.cfg=, or if you want the users
54# # to be able to change settings, then use standard Foswiki preferences that
55# # can be defined in your %USERSWEB%.SitePreferences and overridden at the web
56# # and topic level.
571200nsour $NO_PREFS_IN_TOPIC = 1;
58
59#
60# General plugin information
61#
621200nsmy $web; # Current web being processed
631100nsmy $usWeb; # Web name with subwebs delimiter changed to underscore
641100nsmy $topic; # Current topic
6510smy $user; # Current user
6610smy $installWeb; # Web where plugin topic is installed
67
68#
69# Plugin settings passed in URL or by preferences
70#
711100nsmy $debugDefault; # Debug mode
721100nsmy $antialiasDefault; # Anti-ailas setting
731100nsmy $densityDefault; # Density for Postscript document
7410smy $sizeDefault; # Size of graph
7510smy $vectorFormatsDefault; # Types of images to be generated
761100nsmy $hideAttachDefault; # Should attachments be shown in the attachment table
771100nsmy $inlineAttachDefault; # Image type that will be shown inline in the topic
781100nsmy $linkAttachmentsDefault
79 ; # Should other file types have links shown under the inline image
8010smy $engineDefault
81 ; # Which GraphVize engine should generate the image (default is "dot")
8210smy $libraryDefault; # Topic for images
831100nsmy $deleteAttachDefault; # Should old attachments be trashed
841100nsmy $legacyCleanup; # Backwards cleanup from TWiki implementation
8510smy $forceAttachAPI; # Force attachment processing using Foswiki API
8610smy $forceAttachAPIDefault; #
871100nsmy $svgFallbackDefault
88 ; # File graphics type attached as fallback for browsers without svg support
891100nsmy $svgLinkTargetDefault; #
90
91#
92# Locations of the commands, etc. passed in from LocalSite.cfg
93#
9410smy $enginePath; # Location of the "dot" command
9510smy $magickPath; # Location of ImageMagick
961100nsmy $toolsPath; # Location of the Tools directory for helper script
971100nsmy $attachPath; # Location of attachments if not using Foswiki API
9810smy $attachUrlPath; # URL to find attachments
9910smy $perlCmd; # perl command
100
1011200nsmy $HASH_CODE_LENGTH = 32;
102
103#
104# Documentation on the sandbox command options taken from Foswiki/Sandbox.pm
105#
106# '%VAR%' can optionally take the form '%VAR|FLAG%', where FLAG is a
107# single character flag. Permitted flags are
108# * U untaint without further checks -- dangerous,
109# * F normalize as file name,
110# * N generalized number,
111# * S simple, short string,
112# * D rcs format date
113
1141300nsmy $dotHelper = 'DirectedGraphPlugin.pl';
1151200nsmy $engineCmd =
116' %HELPERSCRIPT|F% %DOT|F% %WORKDIR|F% %INFILE|F% %IOSTRING|U% %ERRFILE|F% %DEBUGFILE|F%';
1171200nsmy $antialiasCmd =
118 'convert -density %DENSITY|N% -geometry %GEOMETRY|S% %INFILE|F% %OUTFILE|F%';
1191100nsmy $identifyCmd = 'identify %INFILE|F%';
120
121# The session variables are used to store the file names and md5hash of the input to the dot command
122# xxxHashArray{SET} - Set to 1 if the array has been initialized
123# xxxHashArray{GRNUM} - Counts the unnamed graphs for the page
124# xxxHashArray{FORMATS}{<filename>} - contains the list of output file types for the input file
125# xxxHashArray{MD5HASH}{<filename>} - contains the hash of the input used to create the files
126# xxxHashArray{IMAGESIZE}{<filename>} - contains the image size of the file for rendering
127
128# =========================
129
# spent 15.9ms (267µs+15.6) within Foswiki::Plugins::DirectedGraphPlugin::initPlugin which was called: # once (267µs+15.6ms) by Foswiki::Plugin::__ANON__[/var/www/foswiki11/lib/Foswiki/Plugin.pm:241] at line 234 of /var/www/foswiki11/lib/Foswiki/Plugin.pm
sub initPlugin {
13015µs ( $topic, $web, $user, $installWeb ) = @_;
131
132 #SMELL: topic and web are tainted when using Locale's
13317µs116µs $topic = Foswiki::Sandbox::untaintUnchecked($topic);
# spent 16µs making 1 call to Foswiki::Sandbox::untaintUnchecked
13412µs14µs $web = Foswiki::Sandbox::untaintUnchecked($web);
# spent 4µs making 1 call to Foswiki::Sandbox::untaintUnchecked
135
13616µs16µs _writeDebug(' >>> initPlugin Entered');
137
138 # Disable the plugin if a topic revision is requested in the query.
1391300ns my $query;
140125µs225µs if ( $Foswiki::Plugins::VERSION >= 2.1 ) {
# spent 13µs making 1 call to version::vxs::VCMP # spent 12µs making 1 call to Foswiki::Func::getRequestObject
141 $query = Foswiki::Func::getRequestObject();
142 }
143 else {
144 $query = Foswiki::Func::getCgiQuery();
145 }
146
14719µs160µs if ( $query && $query->param('rev') ) {
# spent 60µs making 1 call to Foswiki::Request::param
148 if (
149 !$Foswiki::cfg{Plugins}{DirectedGraphPlugin}{generateRevAttachments}
150 )
151 {
152 _writeDebug('DirectedGraphPlugin - Disabled - revision provided');
153 return 0;
154 }
155 }
156
157 # Disable the plugin if comparing two revisions (context = diff
15813µs16µs if ( Foswiki::Func::getContext()->{'diff'} ) {
# spent 6µs making 1 call to Foswiki::Func::getContext
159 if ( !$Foswiki::cfg{Plugins}{DirectedGraphPlugin}
160 {generateDiffAttachments} )
161 {
162 _writeDebug('DirectedGraphPlugin - Disabled - diff context');
163 return 0;
164 }
165 }
166
16711µs $usWeb = $web;
16817µs $usWeb =~ s/\//_/g; #Convert any subweb separators to underscore
169
170 # check for Plugins.pm versions
171114µs16µs if ( $Foswiki::Plugins::VERSION < 1 ) {
# spent 6µs making 1 call to version::vxs::VCMP
172 Foswiki::Func::writeWarning(
173 'Version mismatch between DirectedGraphPlugin and Plugins.pm');
174 return 0;
175 }
176
177 # path to dot, neato, twopi, circo and fdp (including trailing /)
178 $enginePath =
17916µs $Foswiki::cfg{DirectedGraphPlugin}{enginePath}
180 || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{enginePath}
181 || '';
182
183 # path to imagemagick convert routine
18412µs $magickPath =
185 $Foswiki::cfg{DirectedGraphPlugin}{magickPath}
186 || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{magickPath}
187 || '';
188
189 # path to Plugin helper script
19012µs $toolsPath =
191 $Foswiki::cfg{DirectedGraphPlugin}{toolsPath}
192 || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{toolsPath}
193 || $Foswiki::cfg{ToolsDir};
194
195 # If toolsPath is not set, guess the current directory.
1961500ns if ( !$toolsPath ) {
19724.87ms281µs
# spent 47µs (14+34) within Foswiki::Plugins::DirectedGraphPlugin::BEGIN@197 which was called: # once (14µs+34µs) by Foswiki::Plugin::BEGIN@2.11 at line 197
use Cwd;
# spent 47µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@197 # spent 34µs making 1 call to Exporter::import
198 $toolsPath = getcwd;
199 $toolsPath =~ s/\/[^\/]+$/\/tools/;
200 }
201
202# Fix the various paths - trim whitespace and add a trailing slash if none is provided.
203
20413µs $toolsPath =~ s/\s+$//;
20512µs $toolsPath .= '/' unless ( substr( $toolsPath, -1 ) eq '/' );
2061400ns if ($enginePath) {
207 $enginePath =~ s/\s+$//;
208 $enginePath .= '/' unless ( substr( $enginePath, -1 ) eq '/' );
209 }
2101600ns if ($magickPath) {
211 $magickPath =~ s/\s+$//;
212 $magickPath .= '/' unless ( substr( $magickPath, -1 ) eq '/' );
213 }
214
215# path to store attachments - optional. If not provided, Foswiki attachment API is used
216 $attachPath =
21712µs $Foswiki::cfg{DirectedGraphPlugin}{attachPath}
218 || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{attachPath}
219 || '';
220
221# URL to retrieve attachments - optional. If not provided, Foswiki pub path is used.
22212µs $attachUrlPath =
223 $Foswiki::cfg{DirectedGraphPlugin}{attachUrlPath}
224 || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{attachUrlPath}
225 || '';
226
227 # path to perl interpreter
22811µs $perlCmd =
229 $Foswiki::cfg{DirectedGraphPlugin}{perlCmd}
230 || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{perlCmd}
231 || 'perl';
232
233 # Get plugin debug flag
23412µs194µs $debugDefault =
# spent 94µs making 1 call to Foswiki::Func::getPreferencesFlag
235 Foswiki::Func::getPreferencesFlag('DIRECTEDGRAPHPLUGIN_DEBUG');
236
237 # Get plugin antialias default
23812µs135µs $antialiasDefault =
# spent 35µs making 1 call to Foswiki::Func::getPreferencesValue
239 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_ANTIALIAS')
240 || 'off';
241
242 # Get plugin density default
24312µs132µs $densityDefault =
# spent 32µs making 1 call to Foswiki::Func::getPreferencesValue
244 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_DENSITY')
245 || '300';
246
247 # Get plugin size default
24813µs131µs $sizeDefault =
# spent 31µs making 1 call to Foswiki::Func::getPreferencesValue
249 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_SIZE')
250 || 'auto';
251
252 # Get plugin vectorFormats default
25313µs132µs $vectorFormatsDefault =
# spent 32µs making 1 call to Foswiki::Func::getPreferencesValue
254 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_VECTORFORMATS')
255 || 'none';
256
257 # Get plugin engine default
25813µs142µs $engineDefault =
# spent 42µs making 1 call to Foswiki::Func::getPreferencesValue
259 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_ENGINE') || 'dot';
260
261 # Get plugin library default
26214µs132µs $libraryDefault =
# spent 32µs making 1 call to Foswiki::Func::getPreferencesValue
263 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_LIBRARY')
264 || $Foswiki::cfg{SystemWebName} . '.DirectedGraphPlugin';
265
266 # Get plugin hideattachments default
26712µs131µs $hideAttachDefault =
# spent 31µs making 1 call to Foswiki::Func::getPreferencesValue
268 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_HIDEATTACHMENTS')
269 || 'on';
270
271 # Get the default inline attachment default
27213µs132µs $inlineAttachDefault =
# spent 32µs making 1 call to Foswiki::Func::getPreferencesValue
273 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_INLINEATTACHMENT')
274 || 'png';
275
276 # Get the default fallback format for SVG output
27715µs133µs $svgFallbackDefault =
# spent 33µs making 1 call to Foswiki::Func::getPreferencesValue
278 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_SVGFALLBACK')
279 || 'png';
280
281 # Get the default for overriding SVG link target.
28213µs133µs $svgLinkTargetDefault =
# spent 33µs making 1 call to Foswiki::Func::getPreferencesValue
283 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_SVGLINKTARGET')
284 || 'on';
285
286 # Get the default link file attachment default
28712µs131µs $linkAttachmentsDefault =
# spent 31µs making 1 call to Foswiki::Func::getPreferencesValue
288 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_LINKATTACHMENTS')
289 || 'on';
290
291 # Get plugin deleteattachments default
29212µs131µs $deleteAttachDefault = Foswiki::Func::getPreferencesValue(
# spent 31µs making 1 call to Foswiki::Func::getPreferencesValue
293 'DIRECTEDGRAPHPLUGIN_DELETEATTACHMENTS')
294 || 'off';
295
296 # Get plugin legacycleanup default
29712µs132µs $legacyCleanup =
# spent 32µs making 1 call to Foswiki::Func::getPreferencesValue
298 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_LEGACYCLEANUP')
299 || 'off';
300
301 # Get plugin force API default
30212µs132µs $forceAttachAPIDefault =
# spent 32µs making 1 call to Foswiki::Func::getPreferencesValue
303 Foswiki::Func::getPreferencesValue('DIRECTEDGRAPHPLUGIN_FORCEATTACHAPI')
304 || 'off';
305
306 # Read in the attachment information from previous runs
307 # and save it into a session variable for use by the tag handlers
308 # Also clear the new attachment table that will be built from this run
309
31017µs1189µs my %oldHashArray =
311 _loadHashCodes(); # Load the -filehash file into the old hash
312110µs2156µs Foswiki::Func::setSessionValue( 'DGP_hash',
# spent 98µs making 1 call to Storable::nfreeze # spent 58µs making 1 call to Foswiki::Func::setSessionValue
313 Storable::nfreeze \%oldHashArray );
31413µs134µs Foswiki::Func::clearSessionValue('DGP_newhash')
# spent 34µs making 1 call to Foswiki::Func::clearSessionValue
315 ; # blank slate for new attachments
316
317 # Tell WyswiygPlugin to protect <dot>...</dot> markup
31812µs if ( defined &Foswiki::Plugins::WysiwygPlugin::addXMLTag ) {
319
320 # Check if addXMLTag is defined, so that DirectedGraphPlugin
321 # continues to work with older versions of WysiwygPlugin
32212µs12µs _writeDebug(" DISABLE the dot tag in WYSIWYIG ");
323110µs114.6ms Foswiki::Plugins::WysiwygPlugin::addXMLTag( 'dot', sub { 1 } );
# spent 14.6ms making 1 call to Foswiki::Plugins::WysiwygPlugin::addXMLTag
324
325# Some older versions of the plugin used upper-case DOT tags - protect these as well.
32618µs116µs Foswiki::Plugins::WysiwygPlugin::addXMLTag( 'DOT', sub { 1 } );
# spent 16µs making 1 call to Foswiki::Plugins::WysiwygPlugin::addXMLTag
327 }
328
329 # Plugin correctly initialized
330 _writeDebug(
33114µs12µs"- Foswiki::Plugins::DirectedGraphPlugin::initPlugin( $web.$topic ) initialized OK"
332 );
333
33419µs return 1;
335} ### sub initPlugin
336
337# =========================
338
# spent 2.78ms (2.68+101µs) within Foswiki::Plugins::DirectedGraphPlugin::commonTagsHandler which was called 13 times, avg 214µs/call: # 13 times (2.68ms+101µs) by Foswiki::Plugin::invoke at line 294 of /var/www/foswiki11/lib/Foswiki/Plugin.pm, avg 214µs/call
sub commonTagsHandler {
339### my ( $text, $topic, $web ) = @_; # do not uncomment, use $_[0], $_[1]... instead
340
3411327µs return if $_[3]; # Called in an include; do not process DOT macros
342
34374µs $topic = $_[1]; # Can't trust globals
34473µs $web = $_[2];
345
346 #SMELL: topic and web are tainted when using Locale's
347716µs752µs $topic = Foswiki::Sandbox::untaintUnchecked($topic);
# spent 52µs making 7 calls to Foswiki::Sandbox::untaintUnchecked, avg 7µs/call
348712µs722µs $web = Foswiki::Sandbox::untaintUnchecked($web);
# spent 22µs making 7 calls to Foswiki::Sandbox::untaintUnchecked, avg 3µs/call
349
35073µs $usWeb = $web;
351724µs $usWeb =~ s/\//_/g; #Convert any subweb separators to underscore
352
353726µs718µs _writeDebug("- DirectedGraphPlugin::commonTagsHandler( $_[2].$_[1] )");
# spent 18µs making 7 calls to Foswiki::Plugins::DirectedGraphPlugin::_writeDebug, avg 3µs/call
354
355 #pass everything within <dot> tags to _handleDot function
356
3577131µs ( $_[0] =~ s/<DOT(.*?)>(.*?)<\/(DOT)>/&_handleDot($2,$1)/gise );
358
359# $3 will be left set if any matches were found in the topic. If found, do cleanup processing
36077µs if ( $3 && ( $3 eq 'dot' ) ) {
361 _writeDebug("DirectedGraphPlugin - FOUND MATCH - $3");
362 wrapupTagsHandler();
363 }
364
365727µs710µs _writeDebug(' <<< EXIT commonTagsHandler ');
# spent 10µs making 7 calls to Foswiki::Plugins::DirectedGraphPlugin::_writeDebug, avg 1µs/call
366
367} ### sub commonTagsHandler
368
369# =========================
370sub _handleDot {
371 _writeDebug(' >>> _handleDot Entered ');
372
373 # Retrieve new attachments hash from the session variable from previous passes
374 my %newHashArray = ();
375 my $newHashRef = thaw( Foswiki::Func::getSessionValue('DGP_newhash') );
376 if ($newHashRef) {
377 %newHashArray = %{$newHashRef};
378 }
379 else {
380 _writeDebug(' _handleDot is initializing the newHashArray');
381 $newHashArray{SET} =
382 1; # Tell afterCommonTagsHandler that commonTagsHandler has run.
383 $newHashArray{GRNUM} = 0; # Initialize graph count
384 }
385
386 my %oldHashArray = ();
387 my $oldHashRef = thaw( Foswiki::Func::getSessionValue('DGP_hash') );
388 if ($oldHashRef) {
389 %oldHashArray = %{$oldHashRef};
390 }
391
392 my $tempdir = '';
393
394 if ( defined $Foswiki::cfg{TempfileDir} ) {
395 $tempdir = $Foswiki::cfg{TempfileDir};
396 }
397 else {
398 $tempdir = File::Spec->tmpdir();
399 }
400
401 my $attr = $_[1] || ''; # Attributes from the <dot ...> tag
402 my $desc = $_[0] || ''; # GraphViz input between the <dot> ... </dot> tags
403
404 my $grNum = $newHashArray{GRNUM};
405
406 my %params = Foswiki::Func::extractParameters($attr)
407 ; #extract all parms into a hash array
408
409 # parameters with defaults set in the DirectedGraphPlugin topic.
410 my $antialias = $params{antialias} || $antialiasDefault;
411 my $density = $params{density} || $densityDefault;
412 my $size = $params{size} || $sizeDefault;
413 my $vectorFormats = $params{vectorformats} || $vectorFormatsDefault;
414 my $engine = $params{engine} || $engineDefault;
415 my $library = $params{library} || $libraryDefault;
416 my $hideAttach = $params{hideattachments} || $hideAttachDefault;
417 my $inlineAttach = $params{inline} || $inlineAttachDefault;
418 $forceAttachAPI = $params{forceattachapi} || $forceAttachAPIDefault;
419
420#SMELL: linkfiles is deprecated setting for linkattachments. Use it if present.
421 my $linkAttachments =
422 $params{linkattachments} || $params{linkfiles} || $linkAttachmentsDefault;
423 my $svgFallback = $params{svgfallback} || $svgFallbackDefault;
424 my $svgLinkTarget = $params{svglinktarget} || $svgLinkTargetDefault;
425
426 # parameters with hardcoded defaults
427 my $outFilename = $params{file} || '';
428 my $doMap = $params{map} || '';
429 my $dotHash = $params{dothash} || 'on';
430
431 # Global parameters only specified in the DirectedGraphPlugin topic.
432 # $debugDefault
433 # $deleteAttachDefault
434 # $legacyCleanup
435
436#<<< Tidy makes a mess here
437# Strip all trailing white space on any parameters set by set statements - WYSIWYG seems to pad it.
438 $antialias =~ s/\s+$//;
439 $density =~ s/\s+$//;
440 $size =~ s/\s+$//;
441 $vectorFormats =~ s/\s+$//;
442 $engine =~ s/\s+$//;
443 $library =~ s/\s+$//;
444 $hideAttach =~ s/\s+$//;
445 $inlineAttach =~ s/\s+$//;
446 $deleteAttachDefault =~ s/\s+$//;
447 $forceAttachAPI =~ s/\s+$//;
448#>>>
449
450 # Make sure outFilename is clean
451 if ( $outFilename ne '' ) {
452 $outFilename = Foswiki::Sandbox::sanitizeAttachmentName($outFilename);
453
454 # Validate the filename if the Sandbox *can* validate filenames
455 # (older Foswikis cannot) otherwise just untaint
456 my $validator =
457 defined(&Foswiki::Sandbox::validateAttachmentName)
458 ? \&Foswiki::Sandbox::validateAttachmentName
459 : sub { return shift @_; };
460 $outFilename = Foswiki::Sandbox::untaint( $outFilename, $validator );
461 }
462
463 # clean up parms
464 if ( $antialias =~ m/off/ ) {
465 $antialias = 0;
466 }
467
468 #
469 ### Validate all of the <dot ...> input parameters
470 #
471
472 unless ( $density =~ m/^\d+$/ ) {
473 return
474"<font color=\"red\"><nop>DirectedGraph Error: density parameter should be given as a number (was: $density)</font>";
475 }
476
477 unless ( $size =~ m/^\d+x\d+|auto$/ ) {
478 return
479"<font color=\"red\"><nop>DirectedGraph Error: size parameter should be given in format: \"widthxheight\", or \"auto\" (was: $size)</font>";
480 }
481
482 unless ( $engine =~ m/^(dot|neato|twopi|circo|fdp)$/ ) {
483 return
484"<font color=\"red\"><nop>DirectedGraph Error: engine parameter must be one of the following: dot, neato, twopi, circo or fdp (was: $engine)</font>";
485 }
486
487 unless ( $dotHash =~ m/^(on|off)$/ ) {
488 return
489"<font color=\"red\"><nop>DirectedGraph Error: dothash must be either \"off\" or \"on\" (was: $dotHash)</font>";
490 }
491
492 unless ( $hideAttach =~ m/^(on|off)$/ ) {
493 return
494"<font color=\"red\"><nop>DirectedGraph Error: hideattachments must be either \"off\" or \"on\" (was: $hideAttach)</font>";
495 }
496
497 unless ( $linkAttachments =~ m/^(on|off)$/ ) {
498 return
499"<font color=\"red\"><nop>DirectedGraph Error: links must be either \"off\" or \"on\" (was: $linkAttachments)</font>";
500 }
501
502 unless ( $inlineAttach =~ m/^(png|jpg|svg)$/ ) {
503 return
504"<font color=\"red\"><nop>DirectedGraph Error: inline must be either \"jpg\", \"png\" or \"svg\" (was: $inlineAttach)</font>";
505 }
506
507 unless ( $svgFallback =~ m/^(png|jpg|none)$/ ) {
508 return
509"<font color=\"red\"><nop>DirectedGraph Error: svg fallback must be either \"png\" or \"jpg\", or set to \"none\" to disable (was: $svgFallback)</font>";
510 }
511
512 unless ( $svgLinkTarget =~ m/^(on|off)$/ ) {
513 return
514"<font color=\"red\"><nop>DirectedGraph Error: svg Link Target must either be \"on\" or \"off\" (was: $svgLinkTarget)</font>";
515 }
516
517 unless ( $deleteAttachDefault =~ m/^(on|off)$/ ) {
518 return
519"<font color=\"red\"><nop>DirectedGraph Error in defaults: DELETEATTACHMENTS must be either \"off\" or \"on\" (was: $deleteAttachDefault)</font>";
520 }
521
522 unless ( $forceAttachAPI =~ m/^(on|off)$/ ) {
523 return
524"<font color=\"red\"><nop>DirectedGraph Error in defaults: FORCEATTACHAPI must be either \"off\" or \"on\" (was: $forceAttachAPI)</font>";
525 }
526
527 my $hide = undef;
528 if ( $hideAttach =~ m/off/ ) {
529 $hide = 0;
530 }
531 else {
532 $hide = 1;
533 }
534
535 my $chkHash = undef;
536 if ( $dotHash =~ m/off/ ) {
537 $chkHash = 0;
538 }
539 else {
540 $chkHash = 1;
541 }
542
543# SMELL: This is not safe for the Store rewrite.
544# Need to copy library attachments to a temporary directory for access by graphViz!
545
546 $library =~ s/\./\//;
547 my $workingDir = Foswiki::Func::getPubDir() . "/$library";
548 unless ( -e "$workingDir" ) {
549 return
550"<font color=\"red\"><nop>DirectedGraph Error: library parameter should point to topic with attachments to use: <br /> <nop>Web.TopicName (was: $library) <br /> pub dir is $workingDir </font>";
551 }
552
553 # compatibility: check for old map indicator format (map=1 without quotes)
554 if ( $attr =~ m/map=1/ ) {
555 $doMap = 1;
556 }
557
558 _writeDebug(
559"incoming: $desc, $attr , antialias = $antialias, density = $density, size = $size, vectorformats = $vectorFormats, engine = $engine, library = $library, doMap = $doMap, hash = $dotHash \n"
560 );
561
562 foreach my $prm ( keys(%params) ) {
563 _writeDebug("PARAMETER $prm value is $params{$prm}");
564 }
565
566 # compute the MD5 hash of this string. This used to detect
567 # if any parameters or input change from run to run
568 # Attachments recreated if the hash changes
569
570# Hash is calculated against the <dot> command parameters and input,
571# along with any parameters that are set in the Default topic which would modify the results.
572# Parameters that are only set as part of the <dot> command do not need to be explicitly coded,
573# as they are include in $attr.
574
575 my $hashCode =
576 md5_hex( 'DOT'
577 . $desc
578 . $attr
579 . $antialias
580 . $density
581 . $size
582 . $vectorFormats
583 . $engine
584 . $library
585 . $hideAttach
586 . $inlineAttach );
587
588 # If a filename is not provided, set it to a name, with incrementing number.
589 if ( $outFilename eq '' ) { #no filename? Create a new name
590 $grNum++; # increment graph number.
591 $outFilename = 'DirectedGraphPlugin_' . "$grNum";
592 $outFilename = Foswiki::Sandbox::untaintUnchecked($outFilename);
593 }
594
595 # Make sure vectorFormats includes all required file types
596 $vectorFormats =~ s/,/ /g; #Replace any comma's in the list with spaces.
597 $vectorFormats .= ' ' . $inlineAttach
598 if !( $vectorFormats =~ m/$inlineAttach/ )
599 ; # whatever specified inline is mandatory
600 $vectorFormats .= ' ' . "$svgFallback"
601 if ( $inlineAttach =~ m/svg/ && $svgFallback ne 'none' )
602 ; # Generate png if SVG is inline - for browser fallback
603
604 $vectorFormats .= ' ps'
605 if ( ($antialias)
606 && !( $vectorFormats =~ m/ps/ )
607 && !( $inlineAttach =~ m/svg/ ) )
608 ; # postscript for antialias or as requested
609 $vectorFormats .= ' cmapx'
610 if ( ($doMap) && !( $vectorFormats =~ m/cmapx/ ) ); # client side map
611 $vectorFormats =~ s/none//g; # remove the "none" if set by default
612
613 my %attachFile
614 ; # Hash to store attachment file names - key is the file type.
615
616 my $oldHashCode = $oldHashArray{MD5HASH}{$outFilename}
617 || ' '; # retrieve hash code for filename
618
619 $newHashArray{MD5HASH}{$outFilename} = $hashCode; # hash indexed by filename
620 $newHashArray{FORMATS}{$outFilename} =
621 $vectorFormats; # output formats for eventual cleanup
622
623 _writeDebug("$outFilename: oldhash = $oldHashCode newhash = $hashCode");
624
625# Initialize the attachment filenames and copy over image sizes from the old hash.
626 foreach my $key ( split( ' ', $vectorFormats ) ) {
627 if ( $key ne 'none' ) { # skip the bogus default
628 $attachFile{$key} = "$outFilename.$key";
629 $newHashArray{IMAGESIZE}{ $outFilename . $key } =
630 $oldHashArray{IMAGESIZE}{ $outFilename . $key }
631 || ''; # Copy the image size
632 } ### if ($key ne 'none'
633 } ### foreach my $key
634
635 # #######################################################
636 # Generate the attachments if hash or missing files require
637 # ########################################################
638
639 # If the hash codes don't match, the graph needs to be recreated
640 # otherwise just use the previous graph already attached.
641 # Also check if the inline attachment is missing and recreate if needed
642
643 if ( ( ( $oldHashCode ne $hashCode ) && $chkHash ) |
644 not _attachmentExists( $web, $topic, "$outFilename.$inlineAttach" ) )
645 {
646
647 _writeDebug(
648" >>> Processing changed dot tag or missing file $outFilename.$inlineAttach <<< "
649 );
650
651 my $outString = '';
652 my %tempFile;
653
654 foreach my $key ( keys(%attachFile) ) {
655 if ( !exists( $tempFile{$key} ) ) {
656 $tempFile{$key} = new File::Temp(
657 TEMPLATE => 'DGPXXXXXXXXXX',
658 DIR => $tempdir,
659 UNLINK =>
660 0, # Manually unlink later if debug not specified.
661 SUFFIX => ".$key"
662 );
663 $outString .= "-T$key -o$tempFile{$key} ";
664 } ### if (!exists ($tempFile
665 } ### foreach my $key
666
667 # Create a new temporary file to pass to GraphViz
668 my $dotFile = new File::Temp(
669 TEMPLATE => 'DiGraphPluginXXXXXXXXXX',
670 DIR => $tempdir,
671 UNLINK => 0, # Manually unlink later if debug not specified
672 SUFFIX => '.dot'
673 );
674 Foswiki::Func::saveFile( "$dotFile", $desc );
675
676 my $debugFile = '';
677 if ($debugDefault) {
678 $debugFile = new File::Temp(
679 TEMPLATE => 'DiGraphPluginRunXXXXXXXXXX',
680 DIR => $tempdir,
681 UNLINK => 0, # Manually unlink later if debug not specified
682 SUFFIX => '.log'
683 );
684 }
685
686 # Execute dot - generating all output into the Foswiki temp directory
687 my ( $output, $status ) = Foswiki::Sandbox->sysCommand(
688 $perlCmd . $engineCmd,
689 HELPERSCRIPT => $toolsPath . $dotHelper,
690 DOT => $enginePath . $engine,
691 WORKDIR => $workingDir,
692 INFILE => "$dotFile",
693 IOSTRING => $outString,
694 ERRFILE => "$dotFile" . '.err',
695 DEBUGFILE => "$debugFile"
696 );
697
698 if ($status) {
699 $dotFile =~ tr!\\!/!;
700 unlink $dotFile unless $debugDefault;
701 return _showError(
702 $status,
703 $output,
704 "Processing $toolsPath$dotHelper - $enginePath$engine: <br />"
705 . $desc,
706 $dotFile . '.err'
707 );
708 } ### if ($status)
709 $dotFile =~ tr!\\!/!;
710 unlink "$dotFile.err" unless $debugDefault;
711 unlink $dotFile unless $debugDefault;
712
713 if (
714 ($antialias)
715 && ( ( $inlineAttach eq 'svg' && $svgFallback ne 'none' )
716 || ( $inlineAttach ne 'svg' ) )
717 )
718 { # Convert the postscript image to the inline format
719
720 my $inlineType = "$tempFile{$inlineAttach}";
721 if ( $inlineAttach eq 'svg' ) {
722 my $inlineType = "$tempFile{$svgFallback}";
723 }
724
725 my ( $output, $status ) =
726 Foswiki::Sandbox->sysCommand( $magickPath . $identifyCmd,
727 INFILE => "$inlineType", );
728
729 _writeDebug(" _____IDENTIFY_____ $output");
730
731 #$size = "auto";
732 if ( $size eq "auto"
733 && $output =~ m/.*\s([[:digit:]]+x[[:digit:]]+)\s.*/i )
734 {
735 _writeDebug(" ______________ size $1");
736 $size = $1;
737 }
738
739 ( $output, $status ) = Foswiki::Sandbox->sysCommand(
740 $magickPath . $antialiasCmd,
741 DENSITY => $density,
742 GEOMETRY => $size,
743 INFILE => "$tempFile{'ps'}",
744 OUTFILE => "$inlineType"
745 );
746 _writeDebug("dgp-antialias: output: $output \n status: $status");
747 if ($status) {
748 return &_showError( $status, $output,
749 "Processing $magickPath.$antialiasCmd <br />" . $desc );
750 } ### if ($status)
751 } ### if ($antialias)
752
753 ### Attach all of the files to the topic. If a hard path is specified,
754 ### then use perl file I/O, otherwise use Foswiki API.
755 _writeDebug(
756"### forceAttachAPI = |$forceAttachAPI| attachPath = |$attachPath| "
757 );
758
759 #
760 foreach my $key ( keys(%attachFile) ) {
761
762# Get the images size for inline images so that the <object> or <img> tag can be built with the
763# correct size. Reserve # space in the browser to speed rendering a bit, and ensure SVG displays correctly.
764
765 if ( ( $key eq $inlineAttach ) || ( $key eq $svgFallback ) ) {
766 my ( $imgSize, $status ) = Foswiki::Sandbox->sysCommand(
767 $magickPath . $identifyCmd,
768 INFILE => "$tempFile{$key}",
769 );
770
771 _writeDebug(
772" _____IDENTIFY_____ $tempFile{$key}: OBJSIZE $imgSize - STATUS $status"
773 );
774 $imgSize =~
775s/.*\s([[:digit:]]+)x([[:digit:]]+)\s.*/width="$1" height="$2"/i;
776 _writeDebug(" _____MODIFIED $imgSize");
777 chomp $imgSize;
778 $newHashArray{IMAGESIZE}{ $outFilename . $key } = $imgSize;
779 }
780
781# As of IE 7 / FF 3.0 and 3.5, the only way to get consistent link behavior for
782# embedded links is to add target="_top". Any other setting - IE opens the
783# link in the current page, FF opens the link within the svg object on the page.
784# This code overrides the target in the generated svg file for consistent operation.
785
786 if ( ( $key eq 'svg' ) && ( $svgLinkTarget eq 'on' ) ) {
787 my $svgfile = Foswiki::Func::readFile("$tempFile{$key}");
788 $svgfile =~ s/xlink\:href/target=\"_top\" xlink:href/g;
789 Foswiki::Func::saveFile( "$tempFile{$key}", "$svgfile" );
790 }
791
792# the "dot" suffix use for the directed graph input will be detected as a msword template
793# by most servers/browsers. Rename the dot file to dot.txt suffix.
794 my $fname = "$attachFile{$key}";
795 $fname .= '.txt' if ( $key eq 'dot' );
796
797 if ( ($attachPath) && !( $forceAttachAPI eq 'on' ) ) {
798 _writeDebug(
799 "attaching $attachFile{$key} using direct file I/O ");
800 _make_path( $topic, $web );
801 my $tempoutfile = "$attachPath/$web/$topic/$fname";
802 $tempoutfile = Foswiki::Sandbox::untaintUnchecked($tempoutfile)
803 ; #untaint - fails in Perl 5.10
804 my $oldmask =
805 umask( oct(777) - $Foswiki::cfg{RCS}{filePermission} );
806 my $success;
807 eval {
808 $success =
809 File::Copy::copy( "$tempFile{$key}", $tempoutfile );
810 };
811
812 if ( not $success ) {
813 my $message =
814"Plugins:DirectedGraphPlugin: failed to create $tempoutfile: $!";
815 umask($oldmask);
816 throw Error::Simple($message);
817 }
818 umask($oldmask);
819 }
820 else {
821 my @stats = stat $tempFile{$key};
822 my $fileSize = $stats[7];
823 my $fileDate = $stats[9];
824 _writeDebug(
825"attaching $fname using Foswiki API - Web = $web, Topic = $topic, File=$tempFile{$key} Type = $key Size $fileSize date $fileDate"
826 );
827 $fname = Foswiki::Sandbox::untaintUnchecked($fname)
828 ; #untaint - fails on trunk
829
830 Foswiki::Func::saveAttachment(
831 $web, $topic, "$fname",
832 {
833 file => "$tempFile{$key}",
834 filedate => $fileDate,
835 filesize => $fileSize,
836 comment => '<nop>DirectedGraphPlugin: DOT graph',
837 hide => $hide
838 }
839 );
840 } # else if ($attachPath)
841 $tempFile{$key} =~ tr!\\!/!;
842 unlink $tempFile{$key} unless $debugDefault;
843 } ### foreach my $key (keys...
844
845 _writeDebug('attaching Files completed ');
846
847 } ### else [ if ($oldHashCode ne $hashCode) |
848
849 $newHashArray{GRNUM} = $grNum;
850 Foswiki::Func::setSessionValue( 'DGP_newhash',
851 Storable::nfreeze \%newHashArray );
852
853 # ##############################
854 # End Generation of attachments
855 # ###############################
856
857 # Build the path to use for attachment URL's
858 # $attachUrlPath is used only if attachments are stored in an explicit path
859 # and $attachUrlPath is provided, and use of the API is not forced.
860
861 my $urlPath = undef;
862 if ( ($attachPath) && ($attachUrlPath) && !( $forceAttachAPI eq 'on' ) ) {
863 $urlPath = $attachUrlPath;
864 }
865 else {
866 $urlPath = Foswiki::Func::getPubUrlPath();
867 }
868
869 # Build a manual link for each specified file type except for
870 # The "inline" file format, and any image map file
871
872 my $fileLinks = '';
873 if ( Foswiki::Func::isTrue($linkAttachments) ) {
874 $fileLinks = '<br />';
875 foreach my $key ( keys(%attachFile) ) {
876 if ( ( $key ne $inlineAttach ) && ( $key ne 'cmapx' ) ) {
877 my $fname = $attachFile{$key};
878 $fname .= '.txt' if ( $key eq 'dot' );
879 $fileLinks .=
880 '<a href='
881 . $urlPath
882 . Foswiki::urlEncode("/$web/$topic/$fname")
883 . ">[$key]</a> ";
884 } # if (($key ne
885 } # foreach my $key
886 } # if ($linkAttachments
887
888 my $mapfile = '';
889 if ($doMap) {
890
891 # read and format map
892 if ( ($attachPath) && ($attachUrlPath) && !( $forceAttachAPI eq 'on' ) )
893 {
894 $mapfile = Foswiki::Func::readFile(
895 "$attachPath/$web/$topic/$outFilename.cmapx");
896 _writeDebug("MAPFILE $outFilename.cmapx is $mapfile");
897 }
898 else {
899 if (
900 Foswiki::Func::attachmentExists(
901 $web, $topic, "$outFilename.cmapx"
902 )
903 )
904 {
905 $mapfile = Foswiki::Func::readAttachment( $web, $topic,
906 "$outFilename.cmapx" );
907 }
908 }
909 if ($mapfile) { #If mapfile is empty for some reason, these will fail
910 $mapfile =~
911s/(<map\ id\=\")(.*?)(\"\ name\=\")(.*?)(\">)/$1$hashCode$3$hashCode$5/g;
912 $mapfile =~ s/[\n\r]/ /g;
913 $mapfile =~ s/&#45;/-/g;
914 }
915 else {
916 $mapfile = '';
917 }
918 }
919
920 my $loc = $urlPath . "/$web/$topic";
921 my $src = Foswiki::urlEncode("$loc/$outFilename.$inlineAttach");
922 my $returnData = '';
923
924 my $fbtype =
925 $inlineAttach; # If not a SVG, fallback image becomes the primary image.
926
927 $returnData = "<noautolink>\n";
928
929 $returnData .= "$mapfile\n" if ($doMap);
930
931 if ( $inlineAttach eq 'svg' ) {
932 $fbtype = "$svgFallback";
933 $returnData .=
934 "<object data=\"$src\" type=\"image/svg+xml\" border=\"0\" "
935 . $newHashArray{IMAGESIZE}{ $outFilename . $inlineAttach };
936 $returnData .= " alt=\"$outFilename.$inlineAttach diagram\"";
937 $returnData .= "> \n";
938 }
939
940# This is either the fallback image, or the primary image if not generating an inline SVG
941
942 if ( ( $inlineAttach eq 'svg' && $svgFallback ne 'none' )
943 || ( $inlineAttach ne 'svg' ) )
944 {
945 my $srcfb = Foswiki::urlEncode("$loc/$outFilename.$fbtype");
946 $returnData .=
947 "<img src=\"$srcfb\" type=\"image/$fbtype\" "
948 . $newHashArray{IMAGESIZE}{ $outFilename . $fbtype }
949 ; #Embedded img tag for fallback
950 $returnData .= " usemap=\"#$hashCode\""
951 if ($doMap); #Include the image map if required
952 $returnData .= " alt=\"$outFilename.$inlineAttach diagram\"";
953 $returnData .= "> \n";
954 }
955
956 $returnData .= "</object>\n" if ( $inlineAttach eq "svg" );
957
958 $returnData .= "</noautolink>";
959 $returnData .= $fileLinks;
960
961 return $returnData;
962
963} ### sub _handleDot
964
965### sub _showError
966#
967# Display any GraphViz reported errors inline into the file
968# For easier debuggin of malformed <dot> tags.
969
970sub _showError {
971 my ( $status, $output, $text, $errFile ) = @_;
972
973 # Check error file for detailed report from graphviz binary
974 if ( defined $errFile && $errFile && -s $errFile ) {
975 open( ERRFILE, $errFile );
976 my @errLines = <ERRFILE>;
977 $text =
978 "*DirectedGraphPlugin error:* <verbatim>"
979 . join( "", @errLines )
980 . "</verbatim>";
981 $errFile =~ tr!\\!/!;
982 ($errFile) = ( $errFile =~ /(.*)/ ); # untaint $errFile for unlink
983 unlink $errFile unless $debugDefault;
984 }
985
986 my $line = 1;
987 $text =~ s/\n/sprintf("\n%02d: ", $line++)/ges if ($text);
988 $output .= "<pre>$text\n</pre>";
989 return
990 "<font color=\"red\"><nop>DirectedGraph Error ($status): $output</font>";
991} ### sub _showError
992
993### sub _writeDebug
994#
995# Writes a common format debug message if debug is enabled
996
997
# spent 36µs within Foswiki::Plugins::DirectedGraphPlugin::_writeDebug which was called 17 times, avg 2µs/call: # 7 times (18µs+0s) by Foswiki::Plugins::DirectedGraphPlugin::commonTagsHandler at line 353, avg 3µs/call # 7 times (10µs+0s) by Foswiki::Plugins::DirectedGraphPlugin::commonTagsHandler at line 365, avg 1µs/call # once (6µs+0s) by Foswiki::Plugins::DirectedGraphPlugin::initPlugin at line 136 # once (2µs+0s) by Foswiki::Plugins::DirectedGraphPlugin::initPlugin at line 322 # once (2µs+0s) by Foswiki::Plugins::DirectedGraphPlugin::initPlugin at line 331
sub _writeDebug {
9981761µs &Foswiki::Func::writeDebug( 'DirectedGraphPlugin - ' . $_[0] )
999 if $debugDefault;
1000} ### SUB _writeDebug
1001
1002### sub afterRenameHandler
1003#
1004# This routine will rename or delete any workarea files. If topic is renamed
1005# to the Trash web, then the workarea files are simply removed, otherwise they
1006# are renamed to the new Web and topic name.
1007
1008sub afterRenameHandler {
1009
1010 # do not uncomment, use $_[0], $_[1]... instead
1011 ### my ( $oldWeb, $oldTopic, $oldAttachment, $newWeb, $newTopic, $newAttachment ) = @_;
1012
1013 my $oldweb = $_[0];
1014 $oldweb =~ s/\//_/g; # convert subweb separators to underscore
1015 my $oldtopic = $_[1];
1016 my $newweb = $_[3];
1017 $newweb =~ s/\//_/g; # convert subweb separators to underscore
1018 my $newtopic = $_[4];
1019 my $workAreaDir = Foswiki::Func::getWorkArea('DirectedGraphPlugin');
1020
1021 _writeDebug( "- DirectedGraphPlugin::afterRenameHandler( "
1022 . "$_[0].$_[1] $_[2] -> $_[3].$_[4] $_[5] )" );
1023
1024 # Find all files in the workarea directory for the old topic
1025 # rename them unless new web is Trash, otherwise delete them.
1026 #
1027 # files are named any of $web_$topic_DirectedGraphPlugin_n
1028 # or $web_$topic_<user specified name>
1029 # or $web_$topic-filehash
1030 #
1031
1032 opendir( DIR, $workAreaDir )
1033 || die "<ERR> Can't find directory --> $workAreaDir !";
1034
1035 my @wfiles = grep { /^${oldweb}_${oldtopic}-/ } readdir(DIR);
1036 foreach my $f (@wfiles) {
1037 my $prefix = "${oldweb}_${oldtopic}-";
1038 my ($suffix) = ( $f =~ "^$prefix(.*)" );
1039 $f = Foswiki::Sandbox::untaintUnchecked($f);
1040 if ( $newweb eq 'Trash' ) {
1041 unlink "$workAreaDir/$f";
1042 }
1043 else {
1044 my $newname = "${newweb}_${newtopic}-${suffix}";
1045 $newname = Foswiki::Sandbox::untaintUnchecked($newname);
1046 _writeDebug(" Renaming $workAreaDir/$f to $workAreaDir/$newname ");
1047 rename( "$workAreaDir/$f", "$workAreaDir/$newname" );
1048 }
1049 }
1050} ### sub afterRenameHandler
1051
1052### sub _loadHashCodes
1053#
1054# This routine loads the hash array from the stored file in the workarea directory
1055# It also will convert any older style hash files into the new single file written
1056# by the Storable routines.
1057
1058
# spent 189µs (55+134) within Foswiki::Plugins::DirectedGraphPlugin::_loadHashCodes which was called: # once (55µs+134µs) by Foswiki::Plugins::DirectedGraphPlugin::initPlugin at line 310
sub _loadHashCodes {
1059
106016µs1134µs my $workAreaDir = Foswiki::Func::getWorkArea('DirectedGraphPlugin');
# spent 134µs making 1 call to Foswiki::Func::getWorkArea
1061
1062131µs opendir( DIR, $workAreaDir )
1063 || die "<ERR> Can't find directory --> $workAreaDir !";
1064
10651600ns my %tempHash;
1066
106718µs if ( -e "$workAreaDir/${usWeb}_${topic}-filehash" ) {
1068 _writeDebug(' loading filehash ');
1069 my $hashref = retrieve("$workAreaDir/${usWeb}_${topic}-filehash");
1070 %tempHash = %$hashref;
1071 return %tempHash;
1072 }
1073
1074112µs return %tempHash unless ( $legacyCleanup eq 'on' );
1075
1076 ### Temporary Code - Convert file hash codes
1077 ### and delete the old files from the workarea
1078 ### Also insert any old format attachments into the table
1079 ### for later cleanup.
1080
1081 my %typeHash;
1082
1083 # Get all the attachments filenames and extract their types
1084 _writeDebug(' entering legacy cleanup routine ');
1085
1086 my ( $met, $tex ) = Foswiki::Func::readTopic( $web, $topic );
1087 my @attachments = $met->find('FILEATTACHMENT');
1088 _writeDebug(' converting old filehash ');
1089 foreach my $a (@attachments) {
1090 my $aname = $a->{name};
1091 my ( $n, $t ) = $aname =~ m/^(.*)\.(.*)$/; # Split file name and type
1092 next unless $t; # If no type, skip it, it's not ours.
1093 _writeDebug(" - Attach = |$aname| Name = |$n| Type = |$t| ");
1094 $typeHash{$n} .= ' ' . $t;
1095 my ($on) = $n =~
1096 m/^graph([0-9a-f]{32})$/; # old style attachment graph<hashcode>.xxx
1097 if ($on) {
1098 $tempHash{MD5HASH}{$n} = $on;
1099 $tempHash{FORMATS}{$n} .= ' ' . $t;
1100 } # if ($on)
1101 } # foreach my $a
1102
1103 # Read in all of the hash files for the generated attachments
1104 # and build a new format hash table.
1105
1106 my $fPrefix = $usWeb . '_' . $topic . '_';
1107 my @wfiles = grep { /^$fPrefix/ } readdir(DIR);
1108 _writeDebug(" unlinking old hash files for $fPrefix");
1109 foreach my $f (@wfiles) {
1110 my $key = Foswiki::readFile("$workAreaDir/$f");
1111 $f = Foswiki::Sandbox::untaintUnchecked($f);
1112 unlink "$workAreaDir/$f"; # delete the old style hash file
1113 _writeDebug(" unlinking old filehash $workAreaDir/$f ");
1114 $f =~ s/^${usWeb}_${topic}_(.*)/$1/g
1115 ; # recover the original attachment filename
1116 $tempHash{FORMATS}{$f} =
1117 $typeHash{$f}; # insert hash of types found in attachment table
1118 $tempHash{MD5HASH}{$f} = $key; # insert hash indexed by filename
1119 _writeDebug(
1120 "$f = |$tempHash{MD5HASH}{$f}| types |$tempHash{FORMATS}{$f}| ");
1121 }
1122
1123 # Write out new hashfile
1124 if ( keys %tempHash ) {
1125 _writeDebug(" - Writing hashfile ");
1126 Storable::nstore \%tempHash, "$workAreaDir/${usWeb}_${topic}-filehash";
1127 }
1128 return %tempHash;
1129
1130} ### sub _loadHashCodes
1131
1132#
1133# sub wrapupTagsHandler
1134# - Find any files or file types that are no longer needed
1135# and move to Trash with a unique name.
1136#
1137sub wrapupTagsHandler {
1138
1139 _writeDebug(' >>> wrapupTagsHandler entered ');
1140
1141 my %newHash = ();
1142 my $newHashRef = thaw( Foswiki::Func::getSessionValue('DGP_newhash') );
1143
1144 if ($newHashRef) { # DGP_newhash existed
1145 _writeDebug(' -- newHashRef existed in session - writing out ');
1146 %newHash = %{$newHashRef};
1147 my $workAreaDir = Foswiki::Func::getWorkArea('DirectedGraphPlugin');
1148
1149 Storable::nstore \%newHash, "$workAreaDir/${usWeb}_${topic}-filehash";
1150
1151 if ( $newHash{SET} ) { # dot tags have been processed
1152 my %oldHash = ();
1153 my $oldHashRef = thaw( Foswiki::Func::getSessionValue('DGP_hash') );
1154 if ($oldHashRef) { %oldHash = %{$oldHashRef}; }
1155
1156 _writeDebug(" afterCommon - Value of SET s $newHash{SET} ");
1157 _writeDebug(" delete = $deleteAttachDefault");
1158 _writeDebug( ' keys = ' . ( keys %oldHash ) );
1159
1160 if ( ($deleteAttachDefault) && ( keys %oldHash ) )
1161 { # If there are any old files to deal with
1162 foreach my $filename ( keys %{ $oldHash{FORMATS} } )
1163 { # Extract filename
1164 my $oldTypes = $oldHash{FORMATS}{$filename} || '';
1165 if ($debugDefault) {
1166 _writeDebug("old $filename ... types= $oldTypes ");
1167 _writeDebug(
1168"new $filename ... types= $newHash{FORMATS}{$filename} "
1169 );
1170 } ### if ($debugDefault
1171 if ($oldTypes) {
1172 foreach my $oldsuffix ( split( ' ', $oldTypes ) ) {
1173 if (
1174 ( !defined $newHash{FORMATS}{$filename} )
1175 || ( !$newHash{FORMATS}{$filename} =~
1176 (/$oldsuffix/) )
1177 )
1178 {
1179 _deleteAttach("$filename.$oldsuffix");
1180 _deleteAttach("$filename.$oldsuffix.txt")
1181 if ( $oldsuffix eq 'dot' );
1182 } ### if (%newHash
1183 } ### foreach my $olsduffix
1184 } ### if ($oldTypes)
1185 } ### foreach my $filename
1186 } ### if (keys %{$oldHash
1187
1188 # Clear the session values
1189 Foswiki::Func::clearSessionValue('DGP_hash');
1190 Foswiki::Func::clearSessionValue('DGP_newhash');
1191 } ### if ($newHash{SET}
1192 } ### if ($newHashRef)
1193
1194} ### sub wrapupTagsHandler
1195
1196### sub _deleteAttach
1197#
1198# Handles moving unneeded attachments to the Trash web with a new name which includes
1199# the Web name and Topic name. On older versions of Foswiki, it simply deleted the files
1200# with perl's unlink. Also use unlink if direct file I/O requested.
1201
1202sub _deleteAttach {
1203
1204 my $fn = Foswiki::Sandbox::normalizeFileName( $_[0] );
1205
1206 _writeDebug(" DELETE ATTACHMENT entered for $fn");
1207
1208 if ( _attachmentExists( $web, $topic, $fn ) ) {
1209
1210 if ( ($attachPath) && !( $forceAttachAPI eq 'on' ) )
1211 { # Direct file I/O requested
1212 unlink "$attachPath/$web/$topic/$fn";
1213 _writeDebug(" ### Unlinked $attachPath/$web/$topic/$fn ");
1214
1215 }
1216 else { # Foswiki attach API used
1217 # If the TrashAttachment topic is missing, create it.
1218 if (
1219 !Foswiki::Func::topicExists(
1220 $Foswiki::cfg{TrashWebName},
1221 'TrashAttachment'
1222 )
1223 )
1224 {
1225 _writeDebug(' ### Creating missing TrashAttachment topic ');
1226 my $text =
1227 "---+ %MAKETEXT{\"Placeholder for trashed attachments\"}%\n";
1228 Foswiki::Func::saveTopic( "$Foswiki::cfg{TrashWebName}",
1229 "TrashAttachment", undef, $text, undef );
1230 } # if (! Foswiki::Func::topicExists
1231
1232 _writeDebug(" >>> Trashing $web . $topic . $fn");
1233
1234 my $i = 0;
1235 my $of = $fn;
1236 while (
1237 Foswiki::Func::attachmentExists(
1238 $Foswiki::cfg{TrashWebName}, 'TrashAttachment',
1239 "$web.$topic.$of"
1240 )
1241 )
1242 {
1243 _writeDebug(" ------ duplicate in trash $of");
1244 $i++;
1245 $of .= "$i";
1246 } # while (Foswiki::Func
1247
1248 Foswiki::Func::moveAttachment( $web, $topic, $fn,
1249 $Foswiki::cfg{TrashWebName},
1250 'TrashAttachment', "$web.$topic.$of" );
1251 } # else if ($attachPath)
1252 } # _attachmentExists
1253} ### sub _deleteFile
1254
1255#
1256# _make_path
1257# For direct file i/o, make sure the target directory exists
1258# returns the target directory for the attachments.
1259#
1260sub _make_path {
1261 my ( $topic, $web ) = @_;
1262
1263 my @webs = split( '/', $web ); # Split web in case subwebs are present
1264 my $dir = $attachPath || Foswiki::Func::getPubDir();
1265
1266 foreach my $val (@webs) { # Process each subweb in the web path
1267 $dir .= '/' . $val;
1268 if ( !-e $dir ) {
1269 my $oldmask = umask( oct(777) - $Foswiki::cfg{RCS}{dirPermission} );
1270 eval {
1271 File::Path::mkpath( $dir, 0,
1272 $Foswiki::cfg{RCS}{dirPermission} );
1273 };
1274 if ($@) {
1275 my $message =
1276 "Plugins:DirectedGraphPlugin: failed to create ${dir}: $!";
1277 umask($oldmask);
1278 throw Error::Simple($message);
1279 }
1280 umask($oldmask);
1281 } # if (! -e $dir
1282 } # foreach
1283
1284 # If the top level "pub/$web/$topic" directory doesn't exist, create
1285 # it.
1286 $dir .= '/' . $topic;
1287 if ( !-e "$dir" ) {
1288 my $oldmask = umask( oct(777) - $Foswiki::cfg{RCS}{dirPermission} );
1289 eval {
1290 File::Path::mkpath( $dir, 0, $Foswiki::cfg{RCS}{dirPermission} );
1291 };
1292 if ($@) {
1293 my $message =
1294 "Plugins:DirectedGraphPlugin: failed to create ${dir}: $!";
1295 umask($oldmask);
1296 throw Error::Simple($message);
1297 }
1298 umask($oldmask);
1299 }
1300
1301 # Return the complete path to target directory
1302 return ($dir);
1303} ### sub _make_path
1304
1305#
1306# _attachmentExists
1307# Check if attachment exists - use Foswiki API or direct file I/O
1308#
1309sub _attachmentExists {
1310 my ( $web, $topic, $fn ) = @_;
1311
1312 if ( ($attachPath) && !( $forceAttachAPI eq 'on' ) ) {
1313 return ( -e "$attachPath/$web/$topic/$fn" );
1314 }
1315 else {
1316 return Foswiki::Func::attachmentExists( $web, $topic, $fn );
1317 }
1318}
1319
132018µs1;
1321