Filename | /var/www/foswikidev/core/lib/Foswiki/Plugins/DirectedGraphPlugin.pm |
Statements | Executed 1526 statements in 8.02ms |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
108 | 1 | 1 | 2.32ms | 3.13ms | commonTagsHandler | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 2.14ms | 2.24ms | BEGIN@14 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 1.93ms | 2.34ms | BEGIN@9 | Foswiki::Plugins::DirectedGraphPlugin::
203 | 5 | 1 | 182µs | 182µs | _writeDebug | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 173µs | 5.49ms | initPlugin | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 32µs | 100µs | _loadHashCodes | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 15µs | 28µs | BEGIN@6 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 13µs | 44µs | BEGIN@179 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 11µs | 39µs | BEGIN@8 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 5µs | 5µs | BEGIN@11 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 4µs | 4µs | BEGIN@16 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 3µs | 3µs | BEGIN@12 | Foswiki::Plugins::DirectedGraphPlugin::
1 | 1 | 1 | 3µs | 3µs | BEGIN@13 | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | __ANON__[:305] | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | __ANON__[:308] | Foswiki::Plugins::DirectedGraphPlugin::
0 | 0 | 0 | 0s | 0s | __ANON__[:442] | 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 | # See bottom of file for default license and copyright information | ||||
2 | |||||
3 | package Foswiki::Plugins::DirectedGraphPlugin; | ||||
4 | |||||
5 | # ========================= | ||||
6 | 2 | 31µs | 2 | 41µ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 # spent 28µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@6
# spent 13µs making 1 call to strict::import |
7 | |||||
8 | 2 | 31µs | 2 | 66µ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 # spent 39µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@8
# spent 28µs making 1 call to Exporter::import |
9 | 2 | 161µs | 2 | 2.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 # spent 2.34ms making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@9
# spent 40µs making 1 call to Exporter::import |
10 | |||||
11 | 2 | 21µs | 1 | 5µ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 # spent 5µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@11 |
12 | 2 | 19µs | 1 | 3µ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 # spent 3µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@12 |
13 | 2 | 19µs | 1 | 3µ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 # spent 3µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@13 |
14 | 2 | 110µs | 1 | 2.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 # spent 2.24ms making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@14 |
15 | |||||
16 | 2 | 391µs | 1 | 4µ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 # spent 4µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::BEGIN@16 |
17 | |||||
18 | 1 | 1µs | our $VERSION = '1.14'; | ||
19 | 1 | 300ns | our $RELEASE = '1.14'; | ||
20 | |||||
21 | # | ||||
22 | # # Short description of this plugin | ||||
23 | # # One line description, is shown in the %SYSTEMWEB%.TextFormattingRules topic: | ||||
24 | 1 | 300ns | our $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. | ||||
35 | 1 | 100ns | our $NO_PREFS_IN_TOPIC = 1; | ||
36 | |||||
37 | # | ||||
38 | # General plugin information | ||||
39 | # | ||||
40 | 1 | 200ns | my $web; # Current web being processed | ||
41 | 1 | 100ns | my $usWeb; # Web name with subwebs delimiter changed to underscore | ||
42 | 1 | 100ns | my $topic; # Current topic | ||
43 | 1 | 100ns | my $user; # Current user | ||
44 | 1 | 100ns | my $installWeb; # Web where plugin topic is installed | ||
45 | |||||
46 | # | ||||
47 | # Plugin settings passed in URL or by preferences | ||||
48 | # | ||||
49 | 1 | 100ns | my $debugDefault; # Debug mode | ||
50 | 1 | 100ns | my $antialiasDefault; # Anti-ailas setting | ||
51 | 1 | 100ns | my $densityDefault; # Density for Postscript document | ||
52 | 1 | 100ns | my $sizeDefault; # Size of graph | ||
53 | 1 | 0s | my $vectorFormatsDefault; # Types of images to be generated | ||
54 | 1 | 100ns | my $hideAttachDefault; # Should attachments be shown in the attachment table | ||
55 | 1 | 100ns | my $inlineAttachDefault; # Image type that will be shown inline in the topic | ||
56 | 1 | 100ns | my $linkAttachmentsDefault | ||
57 | ; # Should other file types have links shown under the inline image | ||||
58 | 1 | 100ns | my $engineDefault | ||
59 | ; # Which GraphVize engine should generate the image (default is "dot") | ||||
60 | 1 | 100ns | my $libraryDefault; # Topic for images | ||
61 | 1 | 100ns | my $deleteAttachDefault; # Should old attachments be trashed | ||
62 | 1 | 100ns | my $legacyCleanup; # Backwards cleanup from TWiki implementation | ||
63 | 1 | 100ns | my $forceAttachAPI; # Force attachment processing using Foswiki API | ||
64 | 1 | 100ns | my $forceAttachAPIDefault; # | ||
65 | 1 | 100ns | my $svgFallbackDefault | ||
66 | ; # File graphics type attached as fallback for browsers without svg support | ||||
67 | 1 | 100ns | my $svgLinkTargetDefault; # | ||
68 | |||||
69 | # | ||||
70 | # Locations of the commands, etc. passed in from LocalSite.cfg | ||||
71 | # | ||||
72 | 1 | 100ns | my $enginePath; # Location of the "dot" command | ||
73 | 1 | 100ns | my $magickPath; # Location of ImageMagick | ||
74 | 1 | 100ns | my $toolsPath; # Location of the Tools directory for helper script | ||
75 | 1 | 100ns | my $attachPath; # Location of attachments if not using Foswiki API | ||
76 | 1 | 100ns | my $attachUrlPath; # URL to find attachments | ||
77 | 1 | 100ns | my $perlCmd; # perl command | ||
78 | |||||
79 | 1 | 200ns | my $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 | |||||
92 | 1 | 300ns | my $dotHelper = 'DirectedGraphPlugin.pl'; | ||
93 | 1 | 200ns | my $engineCmd = | ||
94 | ' %HELPERSCRIPT|F% %DOT|F% %WORKDIR|F% %INFILE|F% %IOSTRING|U% %ERRFILE|F% %DEBUGFILE|F%'; | ||||
95 | 1 | 300ns | my $antialiasCmd = | ||
96 | 'convert -density %DENSITY|N% -geometry %GEOMETRY|S% %INFILE|F% %OUTFILE|F%'; | ||||
97 | 1 | 200ns | my $identifyCmd = 'identify %INFILE|F%'; | ||
98 | |||||
99 | 1 | 100ns | my $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 | ||||
110 | 1 | 3µs | ( $topic, $web, $user, $installWeb ) = @_; | ||
111 | |||||
112 | #SMELL: topic and web are tainted when using Locale's | ||||
113 | 1 | 2µs | 1 | 6µs | $topic = Foswiki::Sandbox::untaintUnchecked($topic); # spent 6µs making 1 call to Foswiki::Sandbox::untaintUnchecked |
114 | 1 | 2µs | 1 | 3µs | $web = Foswiki::Sandbox::untaintUnchecked($web); # spent 3µs making 1 call to Foswiki::Sandbox::untaintUnchecked |
115 | |||||
116 | 1 | 3µs | 1 | 2µs | _writeDebug(' >>> initPlugin Entered'); # spent 2µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::_writeDebug |
117 | |||||
118 | # Disable the plugin if a topic revision is requested in the query. | ||||
119 | 1 | 400ns | my $query; | ||
120 | 1 | 18µs | 2 | 10µ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 | |||||
127 | 1 | 3µs | 1 | 20µ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 | ||||
139 | 1 | 2µs | 1 | 2µ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 | |||||
149 | 1 | 700ns | $usWeb = $web; | ||
150 | 1 | 3µs | $usWeb =~ s/\//_/g; #Convert any subweb separators to underscore | ||
151 | |||||
152 | # check for Plugins.pm versions | ||||
153 | 1 | 10µs | 1 | 4µ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 = | ||||
161 | 1 | 3µs | $Foswiki::cfg{DirectedGraphPlugin}{enginePath} | ||
162 | || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{enginePath} | ||||
163 | || ''; | ||||
164 | |||||
165 | # path to imagemagick convert routine | ||||
166 | 1 | 2µs | $magickPath = | ||
167 | $Foswiki::cfg{DirectedGraphPlugin}{magickPath} | ||||
168 | || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{magickPath} | ||||
169 | || ''; | ||||
170 | |||||
171 | # path to Plugin helper script | ||||
172 | 1 | 2µ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. | ||||
178 | 1 | 300ns | if ( !$toolsPath ) { | ||
179 | 2 | 4.90ms | 2 | 76µ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 # 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 | |||||
186 | 1 | 1µs | $toolsPath =~ s/\s+$//; | ||
187 | 1 | 1µs | $toolsPath .= '/' unless ( substr( $toolsPath, -1 ) eq '/' ); | ||
188 | 1 | 400ns | if ($enginePath) { | ||
189 | $enginePath =~ s/\s+$//; | ||||
190 | $enginePath .= '/' unless ( substr( $enginePath, -1 ) eq '/' ); | ||||
191 | } | ||||
192 | 1 | 300ns | 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 = | ||||
199 | 1 | 2µ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. | ||||
204 | 1 | 1µs | $attachUrlPath = | ||
205 | $Foswiki::cfg{DirectedGraphPlugin}{attachUrlPath} | ||||
206 | || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{attachUrlPath} | ||||
207 | || ''; | ||||
208 | |||||
209 | # path to perl interpreter | ||||
210 | 1 | 2µs | $perlCmd = | ||
211 | $Foswiki::cfg{DirectedGraphPlugin}{perlCmd} | ||||
212 | || $Foswiki::cfg{Plugins}{DirectedGraphPlugin}{perlCmd} | ||||
213 | || 'perl'; | ||||
214 | |||||
215 | # Get plugin debug flag | ||||
216 | 1 | 2µs | 1 | 30µs | $debugDefault = # spent 30µs making 1 call to Foswiki::Func::getPreferencesFlag |
217 | Foswiki::Func::getPreferencesFlag('DIRECTEDGRAPHPLUGIN_DEBUG'); | ||||
218 | |||||
219 | # Get plugin antialias default | ||||
220 | 1 | 2µs | 1 | 19µ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 | ||||
225 | 1 | 2µs | 1 | 18µ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 | ||||
230 | 1 | 2µs | 1 | 18µ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 | ||||
235 | 1 | 2µs | 1 | 19µ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 | ||||
240 | 1 | 2µs | 1 | 25µ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 | ||||
244 | 1 | 3µs | 1 | 18µ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 | ||||
249 | 1 | 2µs | 1 | 18µ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 | ||||
254 | 1 | 2µs | 1 | 18µ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 | ||||
259 | 1 | 2µs | 1 | 18µ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. | ||||
264 | 1 | 2µs | 1 | 19µ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 | ||||
269 | 1 | 2µs | 1 | 18µ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 | ||||
274 | 1 | 2µs | 1 | 18µ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 | ||||
279 | 1 | 2µs | 1 | 18µ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 | ||||
284 | 1 | 1µs | 1 | 18µ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 | |||||
292 | 1 | 4µs | 1 | 100µs | my %oldHashArray = # spent 100µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::_loadHashCodes |
293 | _loadHashCodes(); # Load the -filehash file into the old hash | ||||
294 | 1 | 6µs | 2 | 98µ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 ); | ||||
296 | 1 | 2µs | 1 | 17µ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 | ||||
300 | 1 | 2µ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 | ||||
304 | 1 | 1µs | 1 | 1µs | _writeDebug(" DISABLE the dot tag in WYSIWYIG "); # spent 1µs making 1 call to Foswiki::Plugins::DirectedGraphPlugin::_writeDebug |
305 | 1 | 5µs | 1 | 4.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. | ||||
308 | 1 | 5µs | 1 | 11µ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( | ||||
313 | 1 | 3µs | 1 | 900ns | "- Foswiki::Plugins::DirectedGraphPlugin::initPlugin( $web.$topic ) initialized OK" # spent 900ns making 1 call to Foswiki::Plugins::DirectedGraphPlugin::_writeDebug |
314 | ); | ||||
315 | |||||
316 | 1 | 6µ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 | ||||
321 | ### my ( $text, $topic, $web ) = @_; # do not uncomment, use $_[0], $_[1]... instead | ||||
322 | |||||
323 | 108 | 14µs | return if ($disable); # Don't render in compare or processing old revisions | ||
324 | 108 | 31µs | return if $_[3]; # Called in an include; do not process DOT macros | ||
325 | |||||
326 | 100 | 25µs | $topic = $_[1]; # Can't trust globals | ||
327 | 100 | 18µs | $web = $_[2]; | ||
328 | |||||
329 | #SMELL: topic and web are tainted when using Locale's | ||||
330 | 100 | 118µs | 100 | 418µs | $topic = Foswiki::Sandbox::untaintUnchecked($topic); # spent 418µs making 100 calls to Foswiki::Sandbox::untaintUnchecked, avg 4µs/call |
331 | 100 | 97µs | 100 | 222µs | $web = Foswiki::Sandbox::untaintUnchecked($web); # spent 222µs making 100 calls to Foswiki::Sandbox::untaintUnchecked, avg 2µs/call |
332 | |||||
333 | 100 | 18µs | $usWeb = $web; | ||
334 | 100 | 175µs | $usWeb =~ s/\//_/g; #Convert any subweb separators to underscore | ||
335 | |||||
336 | 100 | 169µs | 100 | 101µ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 | |||||
340 | 100 | 796µ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 | ||||
343 | 100 | 45µs | if ( $3 && ( $3 =~ /^dot$/i ) ) { | ||
344 | _writeDebug("DirectedGraphPlugin - FOUND MATCH - $3"); | ||||
345 | wrapupTagsHandler(); | ||||
346 | } | ||||
347 | |||||
348 | 100 | 243µs | 100 | 77µs | _writeDebug(' <<< EXIT commonTagsHandler '); # spent 77µs making 100 calls to Foswiki::Plugins::DirectedGraphPlugin::_writeDebug, avg 772ns/call |
349 | |||||
350 | } ### sub commonTagsHandler | ||||
351 | |||||
352 | # ========================= | ||||
353 | sub _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 =~ | ||||
759 | s/.*\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 =~ | ||||
895 | s/(<map\ id\=\")(.*?)(\"\ name\=\")(.*?)(\">)/$1$hashCode$3$hashCode$5/g; | ||||
896 | $mapfile =~ s/[\n\r]/ /g; | ||||
897 | $mapfile =~ s/-/-/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 | |||||
954 | sub _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 | ||||
982 | 203 | 420µ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 | |||||
992 | sub 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 | ||||
1043 | |||||
1044 | 1 | 3µs | 1 | 68µs | my $workAreaDir = Foswiki::Func::getWorkArea('DirectedGraphPlugin'); # spent 68µs making 1 call to Foswiki::Func::getWorkArea |
1045 | |||||
1046 | 1 | 19µs | opendir( DIR, $workAreaDir ) | ||
1047 | || die "<ERR> Can't find directory --> $workAreaDir !"; | ||||
1048 | |||||
1049 | 1 | 400ns | my %tempHash; | ||
1050 | |||||
1051 | 1 | 4µ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 | |||||
1058 | 1 | 7µ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 | # | ||||
1121 | sub 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 | |||||
1186 | sub _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 | # | ||||
1244 | sub _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 | # | ||||
1293 | sub _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 | |||||
1304 | 1 | 8µs | 1; | ||
1305 | |||||
1306 | # | ||||
1307 | __END__ |