Filename | /var/www/foswikidev/core/lib/Foswiki/OopsException.pm |
Statements | Executed 11 statements in 749µs |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 12µs | 24µs | BEGIN@91 | Foswiki::OopsException::
1 | 1 | 1 | 11µs | 35µs | BEGIN@97 | Foswiki::OopsException::
1 | 1 | 1 | 8µs | 12µs | BEGIN@92 | Foswiki::OopsException::
1 | 1 | 1 | 4µs | 4µs | BEGIN@99 | Foswiki::OopsException::
1 | 1 | 1 | 3µs | 3µs | BEGIN@94 | Foswiki::OopsException::
0 | 0 | 0 | 0s | 0s | _prepareResponse | Foswiki::OopsException::
0 | 0 | 0 | 0s | 0s | generate | Foswiki::OopsException::
0 | 0 | 0 | 0s | 0s | new | Foswiki::OopsException::
0 | 0 | 0 | 0s | 0s | redirect | Foswiki::OopsException::
0 | 0 | 0 | 0s | 0s | stringify | Foswiki::OopsException::
Line | State ments |
Time on line |
Calls | Time in subs |
Code |
---|---|---|---|---|---|
1 | # See bottom of file for license and copyright information | ||||
2 | |||||
3 | =begin TML | ||||
4 | |||||
5 | ---+ package Foswiki::OopsException | ||||
6 | |||||
7 | Exception used to raise a request to output a preformatted page. | ||||
8 | |||||
9 | Despite the name, =oops= is not used just for errors; it is also used | ||||
10 | for one-time redirection, for example during the registration process. | ||||
11 | |||||
12 | The =Foswiki::UI::run= function, which is in the call stack for almost | ||||
13 | all cases where an =OopsException= will be thrown, traps the exception | ||||
14 | and outputs an =oops= page to the browser. This requires | ||||
15 | the name of a template file from the =templates= directory, which it | ||||
16 | expands. Parameter values passed to the exception are instantiated in | ||||
17 | the expanded template. The =oops= page is output with an HTTP status | ||||
18 | appropriate to the event that caused the exception (default 500). | ||||
19 | |||||
20 | Extensions may throw =Foswiki::OopsException=. For example: | ||||
21 | |||||
22 | <verbatim> | ||||
23 | use Error qw(:try); | ||||
24 | |||||
25 | ... | ||||
26 | |||||
27 | throw Foswiki::OopsException( 'bathplugin', | ||||
28 | status => 418, | ||||
29 | web => $web, | ||||
30 | topic => $topic, | ||||
31 | params => [ 'big toe', 'stuck in', 'hot tap' ] ); | ||||
32 | </verbatim> | ||||
33 | This will raise an exception that uses the =bathplugin.tmpl= template. If | ||||
34 | =UI::run= handles the exception it will generate a redirect to: | ||||
35 | <verbatim> | ||||
36 | oops?template=bathplugin;param1=bigtoe;param2=hot%20tap | ||||
37 | </verbatim> | ||||
38 | The =bathplugin.tmpl= might contain: | ||||
39 | (<nop> inserted to prevent translation interface from extracting these examples) | ||||
40 | <verbatim> | ||||
41 | %TMPL:INCLUDE{"oops"}% | ||||
42 | %TMPL:DEF{"titleaction"}% %<nop>MAKETEXT{"Bathing problem"}% %TMPL:END% | ||||
43 | %TMPL:DEF{"heading"}%%<nop>MAKETEXT{"Problem filling bath"}%%TMPL:END% | ||||
44 | %TMPL:DEF{"topicactionbuttons"}%%TMPL:P{"oktopicaction"}%%TMPL:END% | ||||
45 | %TMPL:DEF{"script"}%<meta http-equiv="refresh" content="0;url=%SCRIPTURL{view}%/%WEB%/%TOPIC%" />%TMPL:END% | ||||
46 | %TMPL:DEF{"pagetitle"}%%TMPL:P{"heading"}%%TMPL:END% | ||||
47 | %TMPL:DEF{"webaction"}% *%<nop>MAKETEXT{"Warning"}%* %TMPL:END% | ||||
48 | %TMPL:DEF{"message"}% | ||||
49 | %<nop>MAKETEXT{"Your bath cannot be filled because your [_1] is [_2] the [_3]" args="drain,flooding,basement"}%%TMPL:END% | ||||
50 | </verbatim> | ||||
51 | In this case the =oops= page will be rendered with a 418 ("I'm a teapot") | ||||
52 | status in the HTTP header. | ||||
53 | |||||
54 | A more practical example for plugins authors that does not require them to | ||||
55 | provide their own template file involves use of the generic message template | ||||
56 | available from =oopsattention.tmpl=: | ||||
57 | <verbatim> | ||||
58 | throw Foswiki::OopsException( 'oopsattention', def => 'generic', | ||||
59 | params => [ Operation is not allowed ] ); | ||||
60 | </verbatim> | ||||
61 | |||||
62 | Note that to protect against cross site scripting all parameter values are | ||||
63 | automatically and unconditionally entity-encoded so you cannot pass macros | ||||
64 | if you need messages to be automatically translated you either need to handle | ||||
65 | it in the perl code before throwing Foswiki::OopsException or put the %MAKETEXT | ||||
66 | in the template. You cannot pass macros through the parameters. | ||||
67 | |||||
68 | *Since* _date_ indicates where functions or parameters have been added since | ||||
69 | the baseline of the API (TWiki release 4.2.3). The _date_ indicates the | ||||
70 | earliest date of a Foswiki release that will support that function or | ||||
71 | parameter. | ||||
72 | |||||
73 | *Deprecated* _date_ indicates where a function or parameters has been | ||||
74 | [[http://en.wikipedia.org/wiki/Deprecation][deprecated]]. Deprecated | ||||
75 | functions will still work, though they should | ||||
76 | _not_ be called in new plugins and should be replaced in older plugins | ||||
77 | as soon as possible. Deprecated parameters are simply ignored in Foswiki | ||||
78 | releases after _date_. | ||||
79 | |||||
80 | *Until* _date_ indicates where a function or parameter has been removed. | ||||
81 | The _date_ indicates the latest date at which Foswiki releases still supported | ||||
82 | the function or parameter. | ||||
83 | |||||
84 | =cut | ||||
85 | |||||
86 | # THIS PACKAGE IS PART OF THE PUBLISHED API USED BY EXTENSION AUTHORS. | ||||
87 | # DO NOT CHANGE THE EXISTING APIS (well thought out extensions are OK) | ||||
88 | # AND ENSURE ALL POD DOCUMENTATION IS COMPLETE AND ACCURATE. | ||||
89 | |||||
90 | package Foswiki::OopsException; | ||||
91 | 2 | 27µs | 2 | 36µs | # spent 24µs (12+12) within Foswiki::OopsException::BEGIN@91 which was called:
# once (12µs+12µs) by Foswiki::Plugin::BEGIN@15 at line 91 # spent 24µs making 1 call to Foswiki::OopsException::BEGIN@91
# spent 12µs making 1 call to strict::import |
92 | 2 | 22µs | 2 | 16µs | # spent 12µs (8+4) within Foswiki::OopsException::BEGIN@92 which was called:
# once (8µs+4µs) by Foswiki::Plugin::BEGIN@15 at line 92 # spent 12µs making 1 call to Foswiki::OopsException::BEGIN@92
# spent 4µs making 1 call to warnings::import |
93 | |||||
94 | 2 | 39µs | 1 | 3µs | # spent 3µs within Foswiki::OopsException::BEGIN@94 which was called:
# once (3µs+0s) by Foswiki::Plugin::BEGIN@15 at line 94 # spent 3µs making 1 call to Foswiki::OopsException::BEGIN@94 |
95 | 1 | 6µs | our @ISA = ('Error'); | ||
96 | |||||
97 | 2 | 47µs | 2 | 59µs | # spent 35µs (11+24) within Foswiki::OopsException::BEGIN@97 which was called:
# once (11µs+24µs) by Foswiki::Plugin::BEGIN@15 at line 97 # spent 35µs making 1 call to Foswiki::OopsException::BEGIN@97
# spent 24µs making 1 call to Exporter::import |
98 | |||||
99 | # spent 4µs within Foswiki::OopsException::BEGIN@99 which was called:
# once (4µs+0s) by Foswiki::Plugin::BEGIN@15 at line 104 | ||||
100 | 1 | 4µs | if ( $Foswiki::cfg{UseLocale} ) { | ||
101 | require locale; | ||||
102 | import locale(); | ||||
103 | } | ||||
104 | 1 | 601µs | 1 | 4µs | } # spent 4µs making 1 call to Foswiki::OopsException::BEGIN@99 |
105 | |||||
106 | =begin TML | ||||
107 | |||||
108 | ---++ ClassMethod new( $template, ...) | ||||
109 | * =template= is the name of an oops template. e.g. 'bathplugin' refers to =templates/oopsbathplugin.tmpl= | ||||
110 | The remaining parameters are interpreted as key-value pairs. The following keys are used: | ||||
111 | * =web= will be used as the web for the oops | ||||
112 | * =topic= will be used as the topic for the oops | ||||
113 | * =def= - is the (optional) name of a TMPL:DEF within the template | ||||
114 | * =keep= - if set, the exception handler should try its damnedest to retain parameter values from the query. | ||||
115 | * =params= is a reference to an array of parameters. These will be substituted for !%PARAM1%, !%PARAM2% ... !%PARAMn% in the template. | ||||
116 | |||||
117 | For an example of how to use the =def= parameter, see the =oopsattention= | ||||
118 | template. | ||||
119 | |||||
120 | NOTE: parameter values are automatically and unconditionally entity-encoded | ||||
121 | |||||
122 | =cut | ||||
123 | |||||
124 | sub new { | ||||
125 | my $class = shift; | ||||
126 | my $template = shift; | ||||
127 | my $this = $class->SUPER::new(); | ||||
128 | $this->{template} = $template || 'generic'; | ||||
129 | $this->{status} = 500; # default server error | ||||
130 | ASSERT( scalar(@_) % 2 == 0, join( ";", map { $_ || 'undef' } @_ ) ) | ||||
131 | if DEBUG; | ||||
132 | while ( my $key = shift @_ ) { | ||||
133 | my $val = shift @_; | ||||
134 | if ( $key eq 'params' ) { | ||||
135 | if ( ref($val) ne 'ARRAY' ) { | ||||
136 | $val = [$val]; | ||||
137 | } | ||||
138 | $this->{params} = $val; | ||||
139 | } | ||||
140 | else { | ||||
141 | $this->{$key} = $val || ''; | ||||
142 | } | ||||
143 | } | ||||
144 | return $this; | ||||
145 | } | ||||
146 | |||||
147 | =begin TML | ||||
148 | |||||
149 | ---++ ObjectMethod stringify( [$session] ) -> $string | ||||
150 | |||||
151 | Generates a string representation for the object. if a session is passed in, | ||||
152 | and the exception specifies a def, then that def is expanded. This is to allow | ||||
153 | internal expansion of oops exceptions for example when performing bulk | ||||
154 | operations, and also for debugging. | ||||
155 | |||||
156 | =cut | ||||
157 | |||||
158 | sub stringify { | ||||
159 | my ( $this, $session ) = @_; | ||||
160 | |||||
161 | if ( $this->{template} && $this->{def} && $session ) { | ||||
162 | |||||
163 | # load the defs | ||||
164 | $session->templates->readTemplate( 'oops' . $this->{template}, | ||||
165 | no_oops => 1 ); | ||||
166 | my $message = $session->templates->expandTemplate( $this->{def} ) | ||||
167 | || "Failed to find '$this->{def}' in 'oops$this->{template}'"; | ||||
168 | my $topicObject = | ||||
169 | Foswiki::Meta->new( $session, $this->{web}, $this->{topic} ); | ||||
170 | $message = $topicObject->expandMacros($message); | ||||
171 | my $n = 1; | ||||
172 | foreach my $param ( @{ $this->{params} } ) { | ||||
173 | $message =~ s/%PARAM$n%/$param/g; | ||||
174 | $n++; | ||||
175 | } | ||||
176 | return $message; | ||||
177 | } | ||||
178 | else { | ||||
179 | my $s = 'OopsException('; | ||||
180 | $s .= $this->{template}; | ||||
181 | $s .= '/' . $this->{def} if $this->{def}; | ||||
182 | $s .= ' web=>' . $this->{web} if $this->{web}; | ||||
183 | $s .= ' topic=>' . $this->{topic} if $this->{topic}; | ||||
184 | $s .= ' keep=>1' if $this->{keep}; | ||||
185 | if ( defined $this->{params} ) { | ||||
186 | $s .= ' params=>[' . join( ',', @{ $this->{params} } ) . ']'; | ||||
187 | } | ||||
188 | return $s . ')' . ( (DEBUG) ? $this->stacktrace : '' ); | ||||
189 | } | ||||
190 | } | ||||
191 | |||||
192 | # Generate a redirect to an 'oops' script for this exception. | ||||
193 | # | ||||
194 | # If the 'keep' parameter is set in the | ||||
195 | # exception, it saves parameter values into the query as well. This is needed | ||||
196 | # if the query string might get lost during a passthrough, due to a POST | ||||
197 | # being redirected to a GET. | ||||
198 | # This redirect has been replaced by the generate function below and should | ||||
199 | # not be called in new code. | ||||
200 | sub redirect { | ||||
201 | my ( $this, $session ) = @_; | ||||
202 | |||||
203 | my @p = $this->_prepareResponse($session); | ||||
204 | my $url = | ||||
205 | $session->getScriptUrl( 1, 'oops', $this->{web}, $this->{topic}, @p ); | ||||
206 | $session->redirect( $url, 1 ); | ||||
207 | } | ||||
208 | |||||
209 | =begin TML | ||||
210 | |||||
211 | ---++ ObjectMethod generate( $session ) | ||||
212 | |||||
213 | Generate an error page for the exception. This will output the error page | ||||
214 | to the browser. The default HTTP Status for an Oops page is 500. This | ||||
215 | can be overridden using the 'status => ' parameter to the constructor. | ||||
216 | |||||
217 | =cut | ||||
218 | |||||
219 | sub generate { | ||||
220 | my ( $this, $session ) = @_; | ||||
221 | |||||
222 | my @p = $this->_prepareResponse($session); | ||||
223 | $session->{response}->status( $this->{status} ); | ||||
224 | require Foswiki::UI::Oops; | ||||
225 | Foswiki::UI::Oops::oops( $session, $this->{web}, $this->{topic}, | ||||
226 | $session->{request}, 0 ); | ||||
227 | } | ||||
228 | |||||
229 | sub _prepareResponse { | ||||
230 | my ( $this, $session ) = @_; | ||||
231 | my @p = (); | ||||
232 | |||||
233 | $this->{template} = "oops$this->{template}" | ||||
234 | unless $this->{template} =~ m/^oops/; | ||||
235 | push( @p, template => $this->{template} ); | ||||
236 | push( @p, def => $this->{def} ) if $this->{def}; | ||||
237 | my $n = 1; | ||||
238 | push( @p, map { 'param' . ( $n++ ) => $_ } @{ $this->{params} } ); | ||||
239 | while ( my $p = shift(@p) ) { | ||||
240 | $session->{request}->param( -name => $p, -value => shift(@p) ); | ||||
241 | } | ||||
242 | return @p; | ||||
243 | } | ||||
244 | |||||
245 | 1 | 3µs | 1; | ||
246 | __END__ |