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

Filename/var/www/foswikidev/core/lib/Foswiki/Plugins/WysiwygPlugin/Handlers.pm
StatementsExecuted 43 statements in 3.53ms
Subroutines
Calls P F Exclusive
Time
Inclusive
Time
Subroutine
111284µs405µsFoswiki::Plugins::WysiwygPlugin::Handlers::::BEGIN@16Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@16
21118µs18µsFoswiki::Plugins::WysiwygPlugin::Handlers::::addXMLTagFoswiki::Plugins::WysiwygPlugin::Handlers::addXMLTag
11118µs22µsFoswiki::Plugins::WysiwygPlugin::Handlers::::BEGIN@8Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@8
11116µs29µsFoswiki::Plugins::WysiwygPlugin::Handlers::::BEGIN@7Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@7
11114µs41µsFoswiki::Plugins::WysiwygPlugin::Handlers::::BEGIN@286Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@286
11114µs18µsFoswiki::Plugins::WysiwygPlugin::Handlers::::BEGIN@642Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@642
11111µs662µsFoswiki::Plugins::WysiwygPlugin::Handlers::::BEGIN@12Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@12
11110µs40µsFoswiki::Plugins::WysiwygPlugin::Handlers::::BEGIN@9Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@9
11110µs108µsFoswiki::Plugins::WysiwygPlugin::Handlers::::BEGIN@10Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@10
1115µs5µsFoswiki::Plugins::WysiwygPlugin::Handlers::::BEGIN@14Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@14
1114µs4µsFoswiki::Plugins::WysiwygPlugin::Handlers::::BEGIN@15Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@15
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::REST_HTML2TMLFoswiki::Plugins::WysiwygPlugin::Handlers::REST_HTML2TML
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::REST_TML2HTMLFoswiki::Plugins::WysiwygPlugin::Handlers::REST_TML2HTML
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::REST_attachmentsFoswiki::Plugins::WysiwygPlugin::Handlers::REST_attachments
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::TranslateHTML2TMLFoswiki::Plugins::WysiwygPlugin::Handlers::TranslateHTML2TML
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::TranslateTML2HTMLFoswiki::Plugins::WysiwygPlugin::Handlers::TranslateTML2HTML
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::_JAVASCRIPT_TEXTFoswiki::Plugins::WysiwygPlugin::Handlers::_JAVASCRIPT_TEXT
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::_OTOPICTAGFoswiki::Plugins::WysiwygPlugin::Handlers::_OTOPICTAG
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::_OWEBTAGFoswiki::Plugins::WysiwygPlugin::Handlers::_OWEBTAG
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::_SECRET_IDFoswiki::Plugins::WysiwygPlugin::Handlers::_SECRET_ID
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::_WYSIWYG_TEXTFoswiki::Plugins::WysiwygPlugin::Handlers::_WYSIWYG_TEXT
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::__ANON__[:518]Foswiki::Plugins::WysiwygPlugin::Handlers::__ANON__[:518]
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::_convertImageFoswiki::Plugins::WysiwygPlugin::Handlers::_convertImage
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::_dropBackFoswiki::Plugins::WysiwygPlugin::Handlers::_dropBack
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::_isKnownColourFoswiki::Plugins::WysiwygPlugin::Handlers::_isKnownColour
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::_liftOutFoswiki::Plugins::WysiwygPlugin::Handlers::_liftOut
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::_populateVarsFoswiki::Plugins::WysiwygPlugin::Handlers::_populateVars
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::_unquoteFoswiki::Plugins::WysiwygPlugin::Handlers::_unquote
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::afterEditHandlerFoswiki::Plugins::WysiwygPlugin::Handlers::afterEditHandler
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::beforeCommonTagsHandlerFoswiki::Plugins::WysiwygPlugin::Handlers::beforeCommonTagsHandler
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::beforeEditHandlerFoswiki::Plugins::WysiwygPlugin::Handlers::beforeEditHandler
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::beforeMergeHandlerFoswiki::Plugins::WysiwygPlugin::Handlers::beforeMergeHandler
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::beforeSaveHandlerFoswiki::Plugins::WysiwygPlugin::Handlers::beforeSaveHandler
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::expandVarsInURLFoswiki::Plugins::WysiwygPlugin::Handlers::expandVarsInURL
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::modifyHeaderHandlerFoswiki::Plugins::WysiwygPlugin::Handlers::modifyHeaderHandler
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::postConvertURLFoswiki::Plugins::WysiwygPlugin::Handlers::postConvertURL
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::postRenderingHandlerFoswiki::Plugins::WysiwygPlugin::Handlers::postRenderingHandler
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::returnRESTResultFoswiki::Plugins::WysiwygPlugin::Handlers::returnRESTResult
0000s0sFoswiki::Plugins::WysiwygPlugin::Handlers::::toSiteCharSetFoswiki::Plugins::WysiwygPlugin::Handlers::toSiteCharSet
Call graph for these subroutines as a Graphviz dot language file.
Line State
ments
Time
on line
Calls Time
in subs
Code
1# See bottom of file for license and copyright information
2package Foswiki::Plugins::WysiwygPlugin::Handlers;
3
4# This package contains the handler functions used to implement the
5# WysiwygPlugin. They are implemented here so we can 'lazy-load' this
6# module only when it is actually required.
7233µs242µs
# spent 29µs (16+13) within Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@7 which was called: # once (16µs+13µs) by Foswiki::Plugins::WysiwygPlugin::addXMLTag at line 7
use strict;
# spent 29µs making 1 call to Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@7 # spent 13µs making 1 call to strict::import
8227µs227µs
# spent 22µs (18+4) within Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@8 which was called: # once (18µs+4µs) by Foswiki::Plugins::WysiwygPlugin::addXMLTag at line 8
use warnings;
# spent 22µs making 1 call to Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@8 # spent 4µs making 1 call to warnings::import
9231µs270µs
# spent 40µs (10+30) within Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@9 which was called: # once (10µs+30µs) by Foswiki::Plugins::WysiwygPlugin::addXMLTag at line 9
use Assert;
# spent 40µs making 1 call to Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@9 # spent 30µs making 1 call to Exporter::import
10232µs2206µs
# spent 108µs (10+98) within Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@10 which was called: # once (10µs+98µs) by Foswiki::Plugins::WysiwygPlugin::addXMLTag at line 10
use Error (':try');
# spent 108µs making 1 call to Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@10 # spent 98µs making 1 call to Error::import
11
12232µs21.31ms
# spent 662µs (11+651) within Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@12 which was called: # once (11µs+651µs) by Foswiki::Plugins::WysiwygPlugin::addXMLTag at line 12
use CGI qw( :cgi );
# spent 662µs making 1 call to Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@12 # spent 651µs making 1 call to CGI::import
13
14221µs15µs
# spent 5µs within Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@14 which was called: # once (5µs+0s) by Foswiki::Plugins::WysiwygPlugin::addXMLTag at line 14
use Foswiki::Func (); # The plugins API
15223µs14µs
# spent 4µs within Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@15 which was called: # once (4µs+0s) by Foswiki::Plugins::WysiwygPlugin::addXMLTag at line 15
use Foswiki::Plugins (); # For the API version
1621.31ms1405µs
# spent 405µs (284+121) within Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@16 which was called: # once (284µs+121µs) by Foswiki::Plugins::WysiwygPlugin::addXMLTag at line 16
use Foswiki::Plugins::WysiwygPlugin::Constants ();
# spent 405µs making 1 call to Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@16
17
181100nsour $html2tml;
1910sour $imgMap;
201300nsour @refs;
211100nsour %xmltagPlugin;
22
231900nsour $SECRET_ID =
24'WYSIWYG content - do not remove this comment, and never use this identical text in your topics';
25
26sub toSiteCharSet {
27 my $string = shift;
28
29 return $string unless defined $string;
30
31 return $string if $Foswiki::UNICODE;
32
33 return $string
34 if ( $Foswiki::cfg{Site}{CharSet} =~ /^utf-?8/i );
35
36 # If the site charset is not utf-8, need to convert it
37 return Encode::encode(
38 $Foswiki::cfg{Site}{CharSet},
39 Encode::decode_utf8($string),
40 Encode::FB_PERLQQ
41 );
42}
43
44sub _SECRET_ID {
45 $SECRET_ID;
46}
47
48sub _OWEBTAG {
49 my ( $session, $params, $topic, $web ) = @_;
50
51 my $query = Foswiki::Func::getCgiQuery();
52
53 return $web unless $query;
54
55 my $tt = $query->param('templatetopic');
56 if ( defined($tt) ) {
57 my @split =
58 split( /\./, toSiteCharSet($tt) );
59
60 if ( $#split == 0 ) {
61 return $web;
62 }
63 else {
64 return $split[0];
65 }
66 }
67
68 return $web;
69}
70
71sub _OTOPICTAG {
72 my ( $session, $params, $topic, $web ) = @_;
73
74 my $query = Foswiki::Func::getCgiQuery();
75
76 return $topic unless $query;
77
78 my $tt = $query->param('templatetopic');
79 if ( defined($tt) ) {
80 my @split =
81 split( /\./, toSiteCharSet($tt) );
82
83 return $split[$#split];
84 }
85
86 return $topic;
87}
88
89# This handler is used to determine whether the topic is editable by
90# a WYSIWYG editor or not. The only thing it does is to redirect to a
91# normal edit url if the skin is set to WYSIWYGPLUGIN_WYSIWYGSKIN and
92# nasty content is found.
93sub beforeEditHandler {
94
95 #my( $text, $topic, $web, $meta ) = @_;
96
97 my $skin = Foswiki::Func::getPreferencesValue('WYSIWYGPLUGIN_WYSIWYGSKIN');
98
99 if ( $skin && Foswiki::Func::getSkin() =~ /\b$skin\b/o ) {
100 if ( notWysiwygEditable( $_[0] ) ) {
101
102 # redirect
103 my $query = Foswiki::Func::getCgiQuery();
104 foreach my $p (qw( skin cover )) {
105 my $arg = toSiteCharSet( $query->param($p) );
106 if ( $arg && $arg =~ s/\b$skin\b// ) {
107 if ( $arg =~ /^[\s,]*$/ ) {
108 $query->delete($p);
109 }
110 else {
111 $query->param( -name => $p, -value => $arg );
112 }
113 }
114 }
115 my $url = $query->url( -full => 1, -path => 1, -query => 1 );
116 Foswiki::Func::redirectCgiQuery( $query, $url );
117
118 # Bring this session to an untimely end
119 exit 0;
120 }
121 }
122}
123
124# This handler is only invoked *after* merging is complete
125sub beforeSaveHandler {
126
127 #my( $text, $topic, $web ) = @_;
128 my $query = Foswiki::Func::getCgiQuery();
129 return unless $query;
130
131 return unless defined( $query->param('wysiwyg_edit') );
132
133 $_[0] = TranslateHTML2TML( $_[0], $_[1], $_[2] );
134}
135
136# This handler is invoked before a merge. Merges are done before the
137# afterEditHandler is called, so we need to translate here.
138sub beforeMergeHandler {
139
140 #my( $text, $currRev, $currText, $origRev, $origText, $web, $topic ) = @_;
141 afterEditHandler( $_[0], $_[6], $_[5] );
142}
143
144# This handler is invoked *after* a merge, and only from the edit
145# script (so it's useless for a REST save)
146sub afterEditHandler {
147 my ( $text, $topic, $web ) = @_;
148 my $query = Foswiki::Func::getCgiQuery();
149 return unless $query;
150
151 return
152 unless defined( $query->param('wysiwyg_edit') )
153 || $text =~ s/<!--$SECRET_ID-->//go;
154
155 # Switch off wysiwyg_edit so it doesn't try to transform again in
156 # the beforeSaveHandler
157 $query->delete('wysiwyg_edit');
158
159 $text = TranslateHTML2TML( $text, $_[1], $_[2] );
160
161 $_[0] = $text;
162}
163
164# Invoked to convert HTML to TML
165# $text is a foswiki string, i.e. octets encoded in utf8, and so is the result.
166sub TranslateHTML2TML {
167 my ( $text, %opts ) = @_;
168
169 unless ($html2tml) {
170 require Foswiki::Plugins::WysiwygPlugin::HTML2TML;
171
172 $html2tml = new Foswiki::Plugins::WysiwygPlugin::HTML2TML();
173 }
174
175 # SMELL: really, really bad smell; bloody core should NOT pass text
176 # with embedded meta to plugins! It is VERY BAD DESIGN!!!
177 my $top = '';
178 if ( $text =~ s/^(%META:[A-Z]+{.*?}%\r?\n)//s ) {
179 $top = $1;
180 }
181 my $bottom = '';
182 $text =~ s/^(%META:[A-Z]+{.*?}%\r?\n)/$bottom = "$1$bottom";''/gem;
183
184 # Apply defaults
185 $opts{convertImage} ||= \&_convertImage;
186 $opts{rewriteURL} ||= \&postConvertURL;
187
188 # used by above callbacks
189 $opts{web} ||= $Foswiki::Plugins::SESSION->{webName};
190 $opts{topic} ||= $Foswiki::Plugins::SESSION->{topicName};
191
192 $opts{very_clean} = 1; # aggressively polish TML
193 $opts{stickybits} =
194 Foswiki::Func::getPreferencesValue('WYSIWYGPLUGIN_STICKYBITS');
195 $opts{ignoreattrs} =
196 Foswiki::Func::getPreferencesValue('WYSIWYGPLUGIN_IGNOREATTRS');
197
198 $text = $html2tml->convert( $text, \%opts );
199
200 return $top . $text . $bottom;
201}
202
203# Handler used to process text in a =view= URL to generate text/html
204# containing the HTML of the topic to be edited.
205#
206# Invoked when the selected skin is in use to convert the text to HTML
207# We can't use the beforeEditHandler, because the editor loads up and then
208# uses a URL to fetch the text to be edited. This handler is designed to
209# provide the text for that request. It's a real struggle, because the
210# commonTagsHandler is called so many times that getting the right
211# call is hard, and then preventing a repeat call is harder!
212sub beforeCommonTagsHandler {
213
214 #my ( $text, $topic, $web, $meta )
215 my $query = Foswiki::Func::getCgiQuery();
216
217 # stop it from processing the template without expanded
218 # %TEXT% (grr; we need a better way to tell where we
219 # are in the processing pipeline)
220 return if ( $_[0] =~ /^<!-- WysiwygPlugin Template/ );
221
222 # Have to re-read the topic because verbatim blocks have already been
223 # lifted out, and we need them.
224 my $topic = $_[1];
225 my $web = $_[2];
226 my ( $meta, $text );
227 my $altText = $query->param('templatetopic');
228 if ($altText) {
229 $altText = toSiteCharSet($altText);
230 if ( Foswiki::Func::topicExists( $web, $altText ) ) {
231 ( $web, $topic ) =
232 Foswiki::Func::normalizeWebTopicName( $web, $altText );
233 }
234 }
235
236 $_[0] = _WYSIWYG_TEXT( $Foswiki::Plugins::SESSION, {}, $topic, $web );
237}
238
239# Handler used by editors that require pre-prepared HTML embedded in the
240# edit template.
241sub _WYSIWYG_TEXT {
242 my ( $session, $params, $topic, $web ) = @_;
243
244 # Have to re-read the topic because content has already been munged
245 # by other plugins, or by the extraction of verbatim blocks.
246 my ( $meta, $text ) = Foswiki::Func::readTopic( $web, $topic );
247
248 $text = TranslateTML2HTML( $text, web => $web, topic => $topic );
249
250 # Lift out the text to protect it from further Foswiki rendering. It will be
251 # put back in the postRenderingHandler.
252 return _liftOut($text);
253}
254
255# Handler used to present the editable text in a javascript constant string
256sub _JAVASCRIPT_TEXT {
257 my ( $session, $params, $topic, $web ) = @_;
258
259 my $html = _dropBack( _WYSIWYG_TEXT(@_) );
260
261 $html =~ s/([\\'])/\\$1/sg;
262 $html =~ s/\r/\\r/sg;
263 $html =~ s/\n/\\n/sg;
264 $html =~ s/script/scri'+'pt/g;
265
266 return _liftOut("'$html'");
267}
268
269sub postRenderingHandler {
270
271 # Replace protected content.
272 $_[0] = _dropBack( $_[0] );
273}
274
275sub modifyHeaderHandler {
276 my ( $headers, $query ) = @_;
277
278 if ( $query->param('wysiwyg_edit') ) {
279 $headers->{Expires} = 0;
280 $headers->{'Cache-control'} = 'max-age=0, must-revalidate';
281 }
282}
283
284# The subset of vars for which bidirection transformation is supported
285# in URLs only
28621.40ms267µs
# spent 41µs (14+26) within Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@286 which was called: # once (14µs+26µs) by Foswiki::Plugins::WysiwygPlugin::addXMLTag at line 286
use vars qw( @VARS );
# spent 41µs making 1 call to Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@286 # spent 26µs making 1 call to vars::import
287
288# The set of macros that get "special treatment" in URLs, They have to end up
289# sorted based on their expanded length. To convert from URL to MACRO it has to
290# be based upon longest match. So _populateVars replaces this with the appropriately
291# sorted array.
29212µs@VARS = (
293 '%ATTACHURL%',
294 '%ATTACHURLPATH%',
295 '%PUBURL%',
296 '%PUBURLPATH%',
297 '%SCRIPTURLPATH{"view"}%',
298 '%SCRIPTURLPATH{"viewfile"}%',
299 '%SCRIPTURLPATH%',
300 '%SCRIPTURL{"view"}%',
301 '%SCRIPTURL{"viewfile"}%',
302 '%SCRIPTURL%',
303 '%SCRIPTSUFFIX%', # bit dodgy, this one
304);
305
306# Initialises the mapping from var to URL and back
307sub _populateVars {
308 my $opts = shift;
309
310 return if ( $opts->{exp} );
311
312 local $Foswiki::Plugins::WysiwygPlugin::recursionBlock =
313 1; # block calls to beforeCommonTagshandler
314
315 my @exp = split(
316 /\0/,
317 Foswiki::Func::expandCommonVariables(
318 join( "\0", @VARS ),
319 $opts->{topic}, $opts->{web}
320 )
321 );
322
323 # Item13178: The mapping between URL and vars needs to be longest match
324 # so the list must be sorted by length of the value. Also, null entries
325 # should be omitted from the mapping, as they cannot be reversed.
326 my %varh;
327 my @exph = @exp;
328 foreach my $k (@VARS) {
329 my $val = shift @exph;
330 $varh{$k} = $val if ( defined $val );
331 }
332
333 my @nvars;
334 my @nexp;
335
336 # Do the sort by length.
337 foreach
338 my $k ( sort { length( $varh{$b} ) <=> length( $varh{$a} ) } keys %varh )
339 {
340 next unless $varh{$k}; # Omit empty variables, can't be reversed.
341 push @nvars, $k;
342 push @nexp, $varh{$k};
343 }
344
345 @VARS = @nvars; # Replace the vars list with the length sorted list.
346
347 # and build the list of values in order of @nvars.
348 for my $i ( 0 .. $#VARS ) {
349 my $nvar = $VARS[$i];
350 $opts->{match}[$i] = "\Q$nvar\E";
351 $nexp[$i] ||= ''; # Avoid undefined issues.
352 }
353 $opts->{exp} = \@nexp;
354
355}
356
357# callback passed to the TML2HTML convertor on each
358# variable in a URL used in a square bracketed link
359sub expandVarsInURL {
360 my ( $url, $opts ) = @_;
361
362 return '' unless $url;
363
364 _populateVars($opts);
365 for my $i ( 0 .. $#VARS ) {
366 $url =~ s/$opts->{match}[$i]/$opts->{exp}->[$i]/g;
367 }
368 return $url;
369}
370
371# callback passed to the HTML2TML convertor
372# See also foswiki_tiny.js in TinyMCEPlugin, which performs similar functions.
373sub postConvertURL {
374 my ( $url, $opts ) = @_;
375
376 #my $orig = $url; #debug
377
378 local $Foswiki::Plugins::WysiwygPlugin::recursionBlock =
379 1; # block calls to beforeCommonTagshandler
380
381 my $anchor = '';
382 if ( $url =~ s/(#.*)$// ) {
383 $anchor = $1;
384 }
385 my $parameters = '';
386 if ( $url =~ s/(\?.*)$// ) {
387 $parameters = $1;
388 }
389
390 _populateVars($opts);
391
392 for my $i ( 0 .. $#VARS ) {
393 next unless $opts->{exp}->[$i];
394
395 # URLs passed here will be URL-encoded, so
396 # we have to url-encode the test expression.
397 my $test = quotemeta( Foswiki::urlEncode( $opts->{exp}->[$i] ) );
398 $url =~ s/^$test/$VARS[$i]/g;
399 }
400
401 if ( $url =~ m#^%SCRIPTURL(?:PATH)?(?:{"view"}%|%/+view[^/]*)/+([/\w.]+)$#
402 && !$parameters )
403 {
404 my $orig = $1;
405 my ( $web, $topic ) =
406 Foswiki::Func::normalizeWebTopicName( $opts->{web}, $orig );
407
408 if ( $web && $web ne $opts->{web} ) {
409
410 return $web . '.' . $topic . $anchor;
411 }
412
413 return $topic . $anchor;
414 }
415
416 return $url . $parameters . $anchor;
417}
418
419# Callback used to convert an IMG reference into a Foswiki variable,
420# given the src= URL
421sub _convertImage {
422 my ( $src, $opts ) = @_;
423
424 return unless $src;
425
426 # block calls to beforeCommonTagshandler
427 local $Foswiki::Plugins::WysiwygPlugin::recursionBlock = 1;
428
429 # SMELL: this is not documented anywhere; is it still useful?
430 unless ($imgMap) {
431 $imgMap = {};
432 my $imgs = Foswiki::Func::getPreferencesValue('WYSIWYGPLUGIN_ICONS');
433 if ($imgs) {
434 while ( $imgs =~ s/src="(.*?)" alt="(.*?)"// ) {
435 my ( $src, $alt ) = ( $1, $2 );
436 $src =
437 Foswiki::Func::expandCommonVariables( $src, $opts->{topic},
438 $opts->{web} );
439 $alt .= '%' if $alt =~ /^%/;
440 $imgMap->{$src} = $alt;
441 }
442 }
443 }
444
445 return $imgMap->{$src};
446}
447
448# Replace content with a marker to prevent it being munged by Foswiki
449sub _liftOut {
450 my ($text) = @_;
451 my $n = scalar(@refs);
452 push( @refs, $text );
453 return "\05$n\05";
454}
455
456# Substitute marker
457sub _dropBack {
458 my ($text) = @_;
459
460 # Restore everything that was lifted out
461 while ( $text =~ s/\05([0-9]+)\05/$refs[$1]/gi ) {
462 }
463 return $text;
464}
465
466=begin TML
467
468---++ StaticMethod addXMLTag($tag, \&fn)
469
470Instruct WysiwygPlugin to "lift out" the named tag
471and pass it to &fn for processing.
472&fn may modify the text of the tag.
473&fn should return 0 if the tag is to be re-embedded immediately,
474or 1 if it is to be re-embedded after all processing is complete.
475The text passed (by reference) to &fn includes the
476=<tag> ... </tag>= brackets.
477
478The simplest use of this function is something like this:
479=Foswiki::Plugins::WysiwygPlugin::addXMLTag( 'mytag', sub { 1 } );=
480
481A plugin may call this function more than once
482e.g. to change the processing function for a tag.
483However, only the *original plugin* may change the processing
484for a tag.
485
486Plugins should call this function from their =initPlugin=
487handlers so that WysiwygPlugin will protect the XML-like tags
488for all conversions, including REST conversions.
489Plugins that are intended to be used with older versions of Foswiki
490(e.g. 1.0.6) should check that this function is defined before calling it,
491so that they degrade gracefully if an older version of WysiwygPlugin
492(e.g. that shipped with 1.0.6) is installed.
493
494=cut
495
496
# spent 18µs within Foswiki::Plugins::WysiwygPlugin::Handlers::addXMLTag which was called 2 times, avg 9µs/call: # 2 times (18µs+0s) by Foswiki::Plugins::WysiwygPlugin::addXMLTag at line 316 of /var/www/foswikidev/core/lib/Foswiki/Plugins/WysiwygPlugin.pm, avg 9µs/call
sub addXMLTag {
49721µs my ( $tag, $fn ) = @_;
498
49926µs my $plugin = caller;
50023µs $plugin =~ s/^Foswiki::Plugins:://;
501
5022300ns return if not defined $tag;
503
50429µs if (
505 (
506 not exists $Foswiki::Plugins::WysiwygPlugin::xmltag{$tag}
507 and not exists $xmltagPlugin{$tag}
508 )
509 or ( $xmltagPlugin{$tag} eq $plugin )
510 )
511 {
512
513 # This is either a plugin adding a new tag
514 # or a plugin adding a tag it had previously added before.
515 # A plugin is allowed to add a tag that it had added before
516 # and the new function replaces the old.
517 #
518 $fn = sub { 1 }
5192400ns unless $fn; # Default function
520
52122µs $Foswiki::Plugins::WysiwygPlugin::xmltag{$tag} = $fn;
52221µs $xmltagPlugin{$tag} = $plugin;
523 }
524 else {
525
526 # DON'T replace the existing processing for this tag
527 printf STDERR "WysiwygPlugin::addXMLTag: "
528 . "$plugin cannot add XML tag $tag, "
529 . "that tag was already registered by $xmltagPlugin{$tag}\n";
530 }
531}
532
533# Invoked to convert TML to HTML
534# $text is a foswiki string, i.e. octets encoded in utf8, and so is the result.
535sub TranslateTML2HTML {
536 my ( $text, %opts ) = @_;
537
538 unless ($Foswiki::Plugins::WysiwygPlugin::tml2html) {
539 require Foswiki::Plugins::WysiwygPlugin::TML2HTML;
540 $Foswiki::Plugins::WysiwygPlugin::tml2html =
541 new Foswiki::Plugins::WysiwygPlugin::TML2HTML();
542 }
543
544 # Apply defaults
545 $opts{web} ||= $Foswiki::Plugins::SESSION->{webName};
546 $opts{topic} ||= $Foswiki::Plugins::SESSION->{topicName};
547 $opts{expandVarsInURL} ||= \&expandVarsInURL;
548 $opts{xmltag} ||= \%Foswiki::Plugins::WysiwygPlugin::xmltag;
549 my $keepblocks =
550 Foswiki::Func::getPreferencesValue('WYSIWYGPLUGIN_PROTECT_TAG_BLOCKS');
551 if ( defined $keepblocks && $keepblocks ne 'NONE' ) {
552 $opts{keepblocks} = [];
553 foreach my $tag ( split /[,\s]+/, $keepblocks ) {
554 push( @{ $opts{keepblocks} }, $tag );
555 }
556 }
557 my $keeptags =
558 Foswiki::Func::getPreferencesValue('WYSIWYGPLUGIN_PROTECT_EXISTING_TAGS');
559 if ( defined $keeptags && $keeptags ne 'NONE' ) {
560 $opts{keeptags} = [];
561 foreach ( split( /[,\s]+/, $keeptags ) ) {
562 push( @{ $opts{keeptags} }, $_ );
563 }
564 }
565 $opts{forcenoautolink} =
566 Foswiki::isTrue( Foswiki::Func::getPreferencesValue('NOAUTOLINK') );
567 $opts{isKnownColour} = \&_isKnownColour;
568
569 # SMELL: WTF is this? - CDot
570 $opts{supportsparaindent} =
571 Foswiki::Func::getContext()->{SUPPORTS_PARA_INDENT};
572 my $disabled =
573 Foswiki::Plugins::WysiwygPlugin::wysiwygEditingDisabledForThisContent(
574 $_[0] );
575 $opts{protectall} = $disabled ? 1 : 0;
576
577 my $html =
578 $Foswiki::Plugins::WysiwygPlugin::tml2html->convert( $_[0], \%opts );
579
580 if ( $opts{protectall} ) {
581 $html = CGI::div(
582 { class => 'WYSIWYG_WARNING foswikiBroadcastMessage' },
583 Foswiki::Func::renderText(
584 Foswiki::Func::expandCommonVariables( <<"WARNING" ) ) )
585*%MAKETEXT{"Conversion to HTML for WYSIWYG editing is disabled because of the topic content."}%*
586
587%MAKETEXT{"This is why the conversion is disabled:"}% $disabled
588
589%MAKETEXT{"(This message will be removed automatically)"}%
590WARNING
591 . CGI::div( { class => 'WYSIWYG_PROTECTED' }, $html );
592 }
593
594 return $html;
595}
596
597# Look in the Foswiki preferences to see if the named colour is
598# a preference mapped to an HTML colour
599sub _isKnownColour {
600 my $name = shift;
601
602 my $epr = Foswiki::Func::getPreferencesValue($name);
603
604 # Match <font color="x" and style="color:x"
605 if (
606 defined $epr
607 && ( $epr =~ /color=["'](#?\w+)['"]/
608 || $epr =~ /color\s*:\s*(#?\w+)/
609 || $epr =~ /class=["']foswiki(${name})FG['"]/i )
610 )
611 {
612 return $1;
613 }
614 return undef;
615}
616
617# Text that is taken from a web page and added to the parameters of an XHR
618# by JavaScript is UTF-8 encoded. This is because UTF-8 is the default encoding
619# for XML, which XHR was designed to transport. For usefulness in Javascript
620# the response to an XHR should also be UTF-8 encoded.
621# This function generates such a response.
622sub returnRESTResult {
623 my ( $response, $status, $text ) = @_;
624
625 # Foswiki 1.0 introduces the Foswiki::Response object, which handles all
626 # responses.
627 if ( UNIVERSAL::isa( $response, 'Foswiki::Response' ) ) {
628 $response->header(
629 -status => $status,
630 -type => 'text/plain',
631 -charset => 'UTF-8'
632 );
633 $response->print($text);
634 }
635 else { # Pre-Foswiki-1.0.
636 # Turn off AUTOFLUSH
637 # See http://perl.apache.org/docs/2.0/user/coding/coding.html
638 local $| = 0;
639 my $query = Foswiki::Func::getCgiQuery();
640 if ( defined($query) ) {
641 my $len;
6422585µs221µs
# spent 18µs (14+4) within Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@642 which was called: # once (14µs+4µs) by Foswiki::Plugins::WysiwygPlugin::addXMLTag at line 642
{ use bytes; $len = length($text); };
# spent 18µs making 1 call to Foswiki::Plugins::WysiwygPlugin::Handlers::BEGIN@642 # spent 4µs making 1 call to bytes::import
643 print $query->header(
644 -status => $status,
645 -type => 'text/plain',
646 -charset => 'UTF-8',
647 -Content_length => $len
648 );
649 print $text;
650 }
651 }
652 print STDERR $text if ( $status >= 400 );
653}
654
655# Rest handler for use from Javascript. The 'text' parameter is used to
656# pass the text for conversion. The text must be URI-encoded (this is
657# to support use of this handler from XMLHttpRequest, which gets it
658# wrong). Example:
659#
660# var req = new XMLHttpRequest();
661# req.open("POST", url, true);
662# req.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
663# var params = "text=" + encodeURIComponent(escape(text));
664# request.req.setRequestHeader("Content-length", params.length);
665# request.req.setRequestHeader("Connection", "close");
666# request.req.onreadystatechange = ...;
667# req.send(params);
668#
669sub REST_TML2HTML {
670 my ( $session, $plugin, $verb, $response ) = @_;
671
672 my $tml = Foswiki::Func::getCgiQuery()->param('text');
673 $tml = toSiteCharSet($tml);
674
675 return '' unless $tml;
676
677 # if the secret ID is present, don't convert again. We are probably
678 # going 'back' to this page (doesn't work on IE :-( )
679 if ( $tml =~ /<!--$SECRET_ID-->/ ) {
680 return $tml;
681 }
682
683 my $html = TranslateTML2HTML( toSiteCharSet($tml) );
684
685 # Add the secret id to trigger reconversion. Doesn't work if the
686 # editor eats HTML comments, so the editor may need to put it back
687 # in during final cleanup.
688 $html = '<!--' . $SECRET_ID . '-->' . $html;
689
690 returnRESTResult( $response, 200, $html );
691
692 return; # to prevent further processing
693}
694
695# Rest handler for use from Javascript
696sub REST_HTML2TML {
697 my ( $session, $plugin, $verb, $response ) = @_;
698
699 my $html = Foswiki::Func::getCgiQuery()->param('text');
700
701 return '' unless $html;
702
703 $html = toSiteCharSet($html);
704
705 $html =~ s/<!--$SECRET_ID-->//go;
706 unless ($html2tml) {
707 require Foswiki::Plugins::WysiwygPlugin::HTML2TML;
708
709 $html2tml = new Foswiki::Plugins::WysiwygPlugin::HTML2TML();
710 }
711
712 my $tml = $html2tml->convert(
713 $html,
714 {
715 very_clean => 1,
716 stickybits =>
717 Foswiki::Func::getPreferencesValue('WYSIWYGPLUGIN_STICKYBITS'),
718 ignoreattrs =>
719 Foswiki::Func::getPreferencesValue('WYSIWYGPLUGIN_IGNOREATTRS'),
720 convertImage => \&_convertImage,
721 rewriteURL => \&postConvertURL,
722 web => $session->{webName}, # used by callbacks
723 topic => $session->{topicName}, # used by callbacks
724 }
725 );
726
727 returnRESTResult( $response, 200, $tml );
728
729 return; # to prevent further processing
730}
731
732sub _unquote {
733 my $text = shift;
734 $text =~ s/\\/\\\\/g;
735 $text =~ s/\n/\\n/g;
736 $text =~ s/\r/\\r/g;
737 $text =~ s/\t/\\t/g;
738 $text =~ s/"/\\"/g;
739 $text =~ s/'/\\'/g;
740 return $text;
741}
742
743# Get, and return, a list of attachments using JSON
744sub REST_attachments {
745 my ( $session, $plugin, $verb, $response ) = @_;
746 my ( $web, $topic ) = ( $session->{webName}, $session->{topicName} );
747 my ( $meta, $text ) = Foswiki::Func::readTopic( $web, $topic );
748
749 unless (
750 Foswiki::Func::checkAccessPermission(
751 'VIEW', Foswiki::Func::getWikiName(),
752 $text, $topic, $web, $meta
753 )
754 )
755 {
756 returnRESTResult( $response, 401, "Access denied" );
757 return; # to prevent further processing
758 }
759
760 # Create a JSON list of attachment data, sorted by name
761 my @atts;
762 foreach my $att ( sort { $a->{name} cmp $b->{name} }
763 $meta->find('FILEATTACHMENT') )
764 {
765 push(
766 @atts,
767 '{' . join(
768 ',',
769 map {
770 '"'
771 . _unquote($_) . '":"'
772 . _unquote( $att->{$_} ) . '"'
773 } keys %$att
774 )
775 . '}'
776 );
777
778 }
779 return '[' . join( ',', @atts ) . ']';
780}
781
78215µs1;
783__END__