← Index
NYTProf Performance Profile   « line view »
For ./view
  Run on Fri Jul 31 18:42:36 2015
Reported on Fri Jul 31 18:48:14 2015

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