Filename | /var/www/foswiki11/lib/Foswiki/OopsException.pm |
Statements | Executed 10 statements in 696µs |
Calls | P | F | Exclusive Time |
Inclusive Time |
Subroutine |
---|---|---|---|---|---|
1 | 1 | 1 | 19µs | 32µs | BEGIN@91 | Foswiki::OopsException::
1 | 1 | 1 | 9µs | 24µs | BEGIN@97 | Foswiki::OopsException::
1 | 1 | 1 | 8µs | 14µs | BEGIN@92 | 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 | 26µs | 2 | 46µs | # spent 32µs (19+13) within Foswiki::OopsException::BEGIN@91 which was called:
# once (19µs+13µs) by Foswiki::Plugin::BEGIN@15 at line 91 # spent 32µs making 1 call to Foswiki::OopsException::BEGIN@91
# spent 13µs making 1 call to strict::import |
92 | 2 | 22µs | 2 | 19µs | # spent 14µs (8+5) within Foswiki::OopsException::BEGIN@92 which was called:
# once (8µs+5µs) by Foswiki::Plugin::BEGIN@15 at line 92 # spent 14µs making 1 call to Foswiki::OopsException::BEGIN@92
# spent 5µs making 1 call to warnings::import |
93 | |||||
94 | 2 | 40µ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 | 599µs | 2 | 40µs | # spent 24µs (9+15) within Foswiki::OopsException::BEGIN@97 which was called:
# once (9µs+15µs) by Foswiki::Plugin::BEGIN@15 at line 97 # spent 24µs making 1 call to Foswiki::OopsException::BEGIN@97
# spent 15µs making 1 call to Assert::import |
98 | |||||
99 | =begin TML | ||||
100 | |||||
101 | ---++ ClassMethod new( $template, ...) | ||||
102 | * =template= is the name of an oops template. e.g. 'bathplugin' refers to =templates/oopsbathplugin.tmpl= | ||||
103 | The remaining parameters are interpreted as key-value pairs. The following keys are used: | ||||
104 | * =web= will be used as the web for the oops | ||||
105 | * =topic= will be used as the topic for the oops | ||||
106 | * =def= - is the (optional) name of a TMPL:DEF within the template | ||||
107 | * =keep= - if set, the exception handler should try its damnedest to retain parameter values from the query. | ||||
108 | * =params= is a reference to an array of parameters. These will be substituted for !%PARAM1%, !%PARAM2% ... !%PARAMn% in the template. | ||||
109 | |||||
110 | For an example of how to use the =def= parameter, see the =oopsattention= | ||||
111 | template. | ||||
112 | |||||
113 | NOTE: parameter values are automatically and unconditionally entity-encoded | ||||
114 | |||||
115 | =cut | ||||
116 | |||||
117 | sub new { | ||||
118 | my $class = shift; | ||||
119 | my $template = shift; | ||||
120 | my $this = $class->SUPER::new(); | ||||
121 | $this->{template} = $template || 'generic'; | ||||
122 | $this->{status} = 500; # default server error | ||||
123 | ASSERT( scalar(@_) % 2 == 0, join( ";", map { $_ || 'undef' } @_ ) ) | ||||
124 | if DEBUG; | ||||
125 | while ( my $key = shift @_ ) { | ||||
126 | my $val = shift @_; | ||||
127 | if ( $key eq 'params' ) { | ||||
128 | if ( ref($val) ne 'ARRAY' ) { | ||||
129 | $val = [$val]; | ||||
130 | } | ||||
131 | $this->{params} = $val; | ||||
132 | } | ||||
133 | else { | ||||
134 | $this->{$key} = $val || ''; | ||||
135 | } | ||||
136 | } | ||||
137 | return $this; | ||||
138 | } | ||||
139 | |||||
140 | =begin TML | ||||
141 | |||||
142 | ---++ ObjectMethod stringify( [$session] ) -> $string | ||||
143 | |||||
144 | Generates a string representation for the object. if a session is passed in, | ||||
145 | and the exception specifies a def, then that def is expanded. This is to allow | ||||
146 | internal expansion of oops exceptions for example when performing bulk | ||||
147 | operations, and also for debugging. | ||||
148 | |||||
149 | =cut | ||||
150 | |||||
151 | sub stringify { | ||||
152 | my ( $this, $session ) = @_; | ||||
153 | |||||
154 | if ( $this->{template} && $this->{def} && $session ) { | ||||
155 | |||||
156 | # load the defs | ||||
157 | $session->templates->readTemplate( 'oops' . $this->{template}, | ||||
158 | no_oops => 1 ); | ||||
159 | my $message = $session->templates->expandTemplate( $this->{def} ) | ||||
160 | || "Failed to find '$this->{def}' in 'oops$this->{template}'"; | ||||
161 | my $topicObject = | ||||
162 | Foswiki::Meta->new( $session, $this->{web}, $this->{topic} ); | ||||
163 | $message = $topicObject->expandMacros($message); | ||||
164 | my $n = 1; | ||||
165 | foreach my $param ( @{ $this->{params} } ) { | ||||
166 | $message =~ s/%PARAM$n%/$param/g; | ||||
167 | $n++; | ||||
168 | } | ||||
169 | return $message; | ||||
170 | } | ||||
171 | else { | ||||
172 | my $s = 'OopsException('; | ||||
173 | $s .= $this->{template}; | ||||
174 | $s .= '/' . $this->{def} if $this->{def}; | ||||
175 | $s .= ' web=>' . $this->{web} if $this->{web}; | ||||
176 | $s .= ' topic=>' . $this->{topic} if $this->{topic}; | ||||
177 | $s .= ' keep=>1' if $this->{keep}; | ||||
178 | if ( defined $this->{params} ) { | ||||
179 | $s .= ' params=>[' . join( ',', @{ $this->{params} } ) . ']'; | ||||
180 | } | ||||
181 | return $s . ')' . ( (DEBUG) ? $this->stacktrace : '' ); | ||||
182 | } | ||||
183 | } | ||||
184 | |||||
185 | # Generate a redirect to an 'oops' script for this exception. | ||||
186 | # | ||||
187 | # If the 'keep' parameter is set in the | ||||
188 | # exception, it saves parameter values into the query as well. This is needed | ||||
189 | # if the query string might get lost during a passthrough, due to a POST | ||||
190 | # being redirected to a GET. | ||||
191 | # This redirect has been replaced by the generate function below and should | ||||
192 | # not be called in new code. | ||||
193 | sub redirect { | ||||
194 | my ( $this, $session ) = @_; | ||||
195 | |||||
196 | my @p = $this->_prepareResponse($session); | ||||
197 | my $url = | ||||
198 | $session->getScriptUrl( 1, 'oops', $this->{web}, $this->{topic}, @p ); | ||||
199 | $session->redirect( $url, 1 ); | ||||
200 | } | ||||
201 | |||||
202 | =begin TML | ||||
203 | |||||
204 | ---++ ObjectMethod generate( $session ) | ||||
205 | |||||
206 | Generate an error page for the exception. This will output the error page | ||||
207 | to the browser. The default HTTP Status for an Oops page is 500. This | ||||
208 | can be overridden using the 'status => ' parameter to the constructor. | ||||
209 | |||||
210 | =cut | ||||
211 | |||||
212 | sub generate { | ||||
213 | my ( $this, $session ) = @_; | ||||
214 | |||||
215 | my @p = $this->_prepareResponse($session); | ||||
216 | $session->{response}->status( $this->{status} ); | ||||
217 | require Foswiki::UI::Oops; | ||||
218 | Foswiki::UI::Oops::oops( $session, $this->{web}, $this->{topic}, | ||||
219 | $session->{request}, 0 ); | ||||
220 | } | ||||
221 | |||||
222 | sub _prepareResponse { | ||||
223 | my ( $this, $session ) = @_; | ||||
224 | my @p = (); | ||||
225 | |||||
226 | $this->{template} = "oops$this->{template}" | ||||
227 | unless $this->{template} =~ /^oops/; | ||||
228 | push( @p, template => $this->{template} ); | ||||
229 | push( @p, def => $this->{def} ) if $this->{def}; | ||||
230 | my $n = 1; | ||||
231 | push( @p, map { 'param' . ( $n++ ) => $_ } @{ $this->{params} } ); | ||||
232 | while ( my $p = shift(@p) ) { | ||||
233 | $session->{request}->param( -name => $p, -value => shift(@p) ); | ||||
234 | } | ||||
235 | return @p; | ||||
236 | } | ||||
237 | |||||
238 | 1 | 3µs | 1; | ||
239 | __END__ |