Filename | /var/www/foswiki11/lib/Foswiki/Plugins/DirectedGraphPlugin.pm |
Statements | Executed 206 statements in 6.18ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
13 | 1 | 1 | 2.68ms | 2.78ms | commonTagsHandler | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 1.97ms | 2.38ms | BEGIN@31 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 267µs | 15.9ms | initPlugin | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 55µs | 189µs | _loadHashCodes | Foswiki::Plugins::DirectedGraphPlugin::
17 | 5 | 1 | 36µs | 36µs | _writeDebug | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 17µs | 39µs | BEGIN@30 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 14µs | 27µs | BEGIN@28 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 14µs | 47µs | BEGIN@197 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 5µs | 5µs | BEGIN@33 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 4µs | 4µs | BEGIN@35 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 4µs | 4µs | BEGIN@36 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 4µs | 4µs | BEGIN@34 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 3µs | 3µs | BEGIN@38 | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | __ANON__[:323] | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | __ANON__[:326] | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | __ANON__[:459] | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | _attachmentExists | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | _deleteAttach | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | _handleDot | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | _make_path | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | _showError | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | afterRenameHandler | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | wrapupTagsHandler | Foswiki::Plugins::DirectedGraphPlugin::
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 | |||||
25 | package Foswiki::Plugins::DirectedGraphPlugin; | ||||
26 | |||||
27 | # ========================= | ||||
28 | 2 | 31µs | 2 | 39µ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 # spent 27µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@28
# spent 13µs making 1 call to strict::import |
29 | |||||
30 | 2 | 36µs | 2 | 62µ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 # spent 39µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@30
# spent 22µs making 1 call to Exporter::import |
31 | 2 | 151µs | 2 | 2.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 # spent 2.38ms making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@31
# spent 43µs making 1 call to Exporter::import |
32 | |||||
33 | 2 | 20µs | 1 | 5µ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 # spent 5µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@33 |
34 | 2 | 19µs | 1 | 4µ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 # spent 4µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@34 |
35 | 2 | 19µs | 1 | 4µ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 # spent 4µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@35 |
36 | 2 | 19µs | 1 | 4µ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 # spent 4µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@36 |
37 | |||||
38 | 2 | 395µs | 1 | 3µ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 # spent 3µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@38 |
39 | |||||
40 | 1 | 900ns | our $VERSION = '1.13'; | ||
41 | 1 | 300ns | our $RELEASE = '1.13'; | ||
42 | |||||
43 | # | ||||
44 | # # Short description of this plugin | ||||
45 | # # One line description, is shown in the %SYSTEMWEB%.TextFormattingRules topic: | ||||
46 | 1 | 200ns | our $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. | ||||
57 | 1 | 200ns | our $NO_PREFS_IN_TOPIC = 1; | ||
58 | |||||
59 | # | ||||
60 | # General plugin information | ||||
61 | # | ||||
62 | 1 | 200ns | my $web; # Current web being processed | ||
63 | 1 | 100ns | my $usWeb; # Web name with subwebs delimiter changed to underscore | ||
64 | 1 | 100ns | my $topic; # Current topic | ||
65 | 1 | 0s | my $user; # Current user | ||
66 | 1 | 0s | my $installWeb; # Web where plugin topic is installed | ||
67 | |||||
68 | # | ||||
69 | # Plugin settings passed in URL or by preferences | ||||
70 | # | ||||
71 | 1 | 100ns | my $debugDefault; # Debug mode | ||
72 | 1 | 100ns | my $antialiasDefault; # Anti-ailas setting | ||
73 | 1 | 100ns | my $densityDefault; # Density for Postscript document | ||
74 | 1 | 0s | my $sizeDefault; # Size of graph | ||
75 | 1 | 0s | my $vectorFormatsDefault; # Types of images to be generated | ||
76 | 1 | 100ns | my $hideAttachDefault; # Should attachments be shown in the attachment table | ||
77 | 1 | 100ns | my $inlineAttachDefault; # Image type that will be shown inline in the topic | ||
78 | 1 | 100ns | my $linkAttachmentsDefault | ||
79 | ; # Should other file types have links shown under the inline image | ||||
80 | 1 | 0s | my $engineDefault | ||
81 | ; # Which GraphVize engine should generate the image (default is "dot") | ||||
82 | 1 | 0s | my $libraryDefault; # Topic for images | ||
83 | 1 | 100ns | my $deleteAttachDefault; # Should old attachments be trashed | ||
84 | 1 | 100ns | my $legacyCleanup; # Backwards cleanup from TWiki implementation | ||
85 | 1 | 0s | my $forceAttachAPI; # Force attachment processing using Foswiki API | ||
86 | 1 | 0s | my $forceAttachAPIDefault; # | ||
87 | 1 | 100ns | my $svgFallbackDefault | ||
88 | ; # File graphics type attached as fallback for browsers without svg support | ||||
89 | 1 | 100ns | my $svgLinkTargetDefault; # | ||
90 | |||||
91 | # | ||||
92 | # Locations of the commands, etc. passed in from LocalSite.cfg | ||||
93 | # | ||||
94 | 1 | 0s | my $enginePath; # Location of the "dot" command | ||
95 | 1 | 0s | my $magickPath; # Location of ImageMagick | ||
96 | 1 | 100ns | my $toolsPath; # Location of the Tools directory for helper script | ||
97 | 1 | 100ns | my $attachPath; # Location of attachments if not using Foswiki API | ||
98 | 1 | 0s | my $attachUrlPath; # URL to find attachments | ||
99 | 1 | 0s | my $perlCmd; # perl command | ||
100 | |||||
101 | 1 | 200ns | my $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 | |||||
114 | 1 | 300ns | my $dotHelper = 'DirectedGraphPlugin.pl'; | ||
115 | 1 | 200ns | my $engineCmd = | ||
116 | ' %HELPERSCRIPT|F% %DOT|F% %WORKDIR|F% %INFILE|F% %IOSTRING|U% %ERRFILE|F% %DEBUGFILE|F%'; | ||||
117 | 1 | 200ns | my $antialiasCmd = | ||
118 | 'convert -density %DENSITY|N% -geometry %GEOMETRY|S% %INFILE|F% %OUTFILE|F%'; | ||||
119 | 1 | 100ns | my $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 | ||||
130 | 1 | 5µs | ( $topic, $web, $user, $installWeb ) = @_; | ||
131 | |||||
132 | #SMELL: topic and web are tainted when using Locale's | ||||
133 | 1 | 7µs | 1 | 16µs | $topic = Foswiki::Sandbox::untaintUnchecked($topic); # spent 16µs making 1 call to Foswiki::Sandbox::untaintUnchecked |
134 | 1 | 2µs | 1 | 4µs | $web = Foswiki::Sandbox::untaintUnchecked($web); # spent 4µs making 1 call to Foswiki::Sandbox::untaintUnchecked |
135 | |||||
136 | 1 | 6µs | 1 | 6µs | _writeDebug(' >>> initPlugin Entered'); # spent 6µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::_writeDebug |
137 | |||||
138 | # Disable the plugin if a topic revision is requested in the query. | ||||
139 | 1 | 300ns | my $query; | ||
140 | 1 | 25µs | 2 | 25µ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 | |||||
147 | 1 | 9µs | 1 | 60µ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 | ||||
158 | 1 | 3µs | 1 | 6µ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 | |||||
167 | 1 | 1µs | $usWeb = $web; | ||
168 | 1 | 7µs | $usWeb =~ s/\//_/g; #Convert any subweb separators to underscore | ||
169 | |||||
170 | # check for Plugins.pm versions | ||||
171 | 1 | 14µs | 1 | 6µ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 = | ||||
179 | 1 | 6µs | $Foswiki::cfg{DirectedGraphPlugin}{enginePath} | ||
180 | || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{enginePath} | ||||
181 | || ''; | ||||
182 | |||||
183 | # path to imagemagick convert routine | ||||
184 | 1 | 2µs | $magickPath = | ||
185 | $Foswiki::cfg{DirectedGraphPlugin}{magickPath} | ||||
186 | || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{magickPath} | ||||
187 | || ''; | ||||
188 | |||||
189 | # path to Plugin helper script | ||||
190 | 1 | 2µ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. | ||||
196 | 1 | 500ns | if ( !$toolsPath ) { | ||
197 | 2 | 4.87ms | 2 | 81µ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 # 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 | |||||
204 | 1 | 3µs | $toolsPath =~ s/\s+$//; | ||
205 | 1 | 2µs | $toolsPath .= '/' unless ( substr( $toolsPath, -1 ) eq '/' ); | ||
206 | 1 | 400ns | if ($enginePath) { | ||
207 | $enginePath =~ s/\s+$//; | ||||
208 | $enginePath .= '/' unless ( substr( $enginePath, -1 ) eq '/' ); | ||||
209 | } | ||||
210 | 1 | 600ns | 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 = | ||||
217 | 1 | 2µ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. | ||||
222 | 1 | 2µs | $attachUrlPath = | ||
223 | $Foswiki::cfg{DirectedGraphPlugin}{attachUrlPath} | ||||
224 | || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{attachUrlPath} | ||||
225 | || ''; | ||||
226 | |||||
227 | # path to perl interpreter | ||||
228 | 1 | 1µs | $perlCmd = | ||
229 | $Foswiki::cfg{DirectedGraphPlugin}{perlCmd} | ||||
230 | || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{perlCmd} | ||||
231 | || 'perl'; | ||||
232 | |||||
233 | # Get plugin debug flag | ||||
234 | 1 | 2µs | 1 | 94µs | $debugDefault = # spent 94µs making 1 call to Foswiki::Func::getPreferencesFlag |
235 | Foswiki::Func::getPreferencesFlag('DIRECTEDGRAPHPLUGIN_DEBUG'); | ||||
236 | |||||
237 | # Get plugin antialias default | ||||
238 | 1 | 2µs | 1 | 35µ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 | ||||
243 | 1 | 2µs | 1 | 32µ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 | ||||
248 | 1 | 3µs | 1 | 31µ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 | ||||
253 | 1 | 3µs | 1 | 32µ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 | ||||
258 | 1 | 3µs | 1 | 42µ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 | ||||
262 | 1 | 4µs | 1 | 32µ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 | ||||
267 | 1 | 2µs | 1 | 31µ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 | ||||
272 | 1 | 3µs | 1 | 32µ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 | ||||
277 | 1 | 5µs | 1 | 33µ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. | ||||
282 | 1 | 3µs | 1 | 33µ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 | ||||
287 | 1 | 2µs | 1 | 31µ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 | ||||
292 | 1 | 2µs | 1 | 31µ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 | ||||
297 | 1 | 2µs | 1 | 32µ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 | ||||
302 | 1 | 2µs | 1 | 32µ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 | |||||
310 | 1 | 7µs | 1 | 189µs | my %oldHashArray = # spent 189µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::_loadHashCodes |
311 | _loadHashCodes(); # Load the -filehash file into the old hash | ||||
312 | 1 | 10µs | 2 | 156µ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 ); | ||||
314 | 1 | 3µs | 1 | 34µ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 | ||||
318 | 1 | 2µ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 | ||||
322 | 1 | 2µs | 1 | 2µs | _writeDebug(" DISABLE the dot tag in WYSIWYIG "); # spent 2µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::_writeDebug |
323 | 1 | 10µs | 1 | 14.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. | ||||
326 | 1 | 8µs | 1 | 16µ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( | ||||
331 | 1 | 4µs | 1 | 2µs | "- Foswiki::Plugins::DirectedGraphPlugin::initPlugin( $web.$topic ) initialized OK" # spent 2µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::_writeDebug |
332 | ); | ||||
333 | |||||
334 | 1 | 9µ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 | ||||
339 | ### my ( $text, $topic, $web ) = @_; # do not uncomment, use $_[0], $_[1]... instead | ||||
340 | |||||
341 | 13 | 27µs | return if $_[3]; # Called in an include; do not process DOT macros | ||
342 | |||||
343 | 7 | 4µs | $topic = $_[1]; # Can't trust globals | ||
344 | 7 | 3µs | $web = $_[2]; | ||
345 | |||||
346 | #SMELL: topic and web are tainted when using Locale's | ||||
347 | 7 | 16µs | 7 | 52µs | $topic = Foswiki::Sandbox::untaintUnchecked($topic); # spent 52µs making 7 calls to Foswiki::Sandbox::untaintUnchecked, avg 7µs/call |
348 | 7 | 12µs | 7 | 22µs | $web = Foswiki::Sandbox::untaintUnchecked($web); # spent 22µs making 7 calls to Foswiki::Sandbox::untaintUnchecked, avg 3µs/call |
349 | |||||
350 | 7 | 3µs | $usWeb = $web; | ||
351 | 7 | 24µs | $usWeb =~ s/\//_/g; #Convert any subweb separators to underscore | ||
352 | |||||
353 | 7 | 26µs | 7 | 18µ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 | |||||
357 | 7 | 131µ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 | ||||
360 | 7 | 7µs | if ( $3 && ( $3 eq 'dot' ) ) { | ||
361 | _writeDebug("DirectedGraphPlugin - FOUND MATCH - $3"); | ||||
362 | wrapupTagsHandler(); | ||||
363 | } | ||||
364 | |||||
365 | 7 | 27µs | 7 | 10µ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 | # ========================= | ||||
370 | sub _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 =~ | ||||
775 | s/.*\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 =~ | ||||
911 | s/(<map\ id\=\")(.*?)(\"\ name\=\")(.*?)(\">)/$1$hashCode$3$hashCode$5/g; | ||||
912 | $mapfile =~ s/[\n\r]/ /g; | ||||
913 | $mapfile =~ s/-/-/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 | |||||
970 | sub _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 | ||||
998 | 17 | 61µ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 | |||||
1008 | sub 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 | ||||
1059 | |||||
1060 | 1 | 6µs | 1 | 134µs | my $workAreaDir = Foswiki::Func::getWorkArea('DirectedGraphPlugin'); # spent 134µs making 1 call to Foswiki::Func::getWorkArea |
1061 | |||||
1062 | 1 | 31µs | opendir( DIR, $workAreaDir ) | ||
1063 | || die "<ERR> Can't find directory --> $workAreaDir !"; | ||||
1064 | |||||
1065 | 1 | 600ns | my %tempHash; | ||
1066 | |||||
1067 | 1 | 8µ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 | |||||
1074 | 1 | 12µ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 | # | ||||
1137 | sub 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 | |||||
1202 | sub _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 | # | ||||
1260 | sub _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 | # | ||||
1309 | sub _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 | |||||
1320 | 1 | 8µs | 1; | ||
1321 |