rt 4.2.14 (#13852)
[freeside.git] / rt / lib / RT / Interface / Web / Request.pm
1 # BEGIN BPS TAGGED BLOCK {{{
2 #
3 # COPYRIGHT:
4 #
5 # This software is Copyright (c) 1996-2017 Best Practical Solutions, LLC
6 #                                          <sales@bestpractical.com>
7 #
8 # (Except where explicitly superseded by other copyright notices)
9 #
10 #
11 # LICENSE:
12 #
13 # This work is made available to you under the terms of Version 2 of
14 # the GNU General Public License. A copy of that license should have
15 # been provided with this software, but in any event can be snarfed
16 # from www.gnu.org.
17 #
18 # This work is distributed in the hope that it will be useful, but
19 # WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
21 # General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
26 # 02110-1301 or visit their web page on the internet at
27 # http://www.gnu.org/licenses/old-licenses/gpl-2.0.html.
28 #
29 #
30 # CONTRIBUTION SUBMISSION POLICY:
31 #
32 # (The following paragraph is not intended to limit the rights granted
33 # to you to modify and distribute this software under the terms of
34 # the GNU General Public License and is only of importance to you if
35 # you choose to contribute your changes and enhancements to the
36 # community by submitting them to Best Practical Solutions, LLC.)
37 #
38 # By intentionally submitting any modifications, corrections or
39 # derivatives to this work, or any other work intended for use with
40 # Request Tracker, to Best Practical Solutions, LLC, you confirm that
41 # you are the copyright holder for those contributions and you grant
42 # Best Practical Solutions,  LLC a nonexclusive, worldwide, irrevocable,
43 # royalty-free, perpetual, license to use, copy, create derivative
44 # works based on those contributions, and sublicense and distribute
45 # those contributions and any derivatives thereof.
46 #
47 # END BPS TAGGED BLOCK }}}
48
49 package RT::Interface::Web::Request;
50
51 use strict;
52 use warnings;
53
54 use HTML::Mason::PSGIHandler;
55 use base qw(HTML::Mason::Request::PSGI);
56 use Params::Validate qw(:all);
57
58 sub new {
59     my $class = shift;
60     $class->valid_params( %{ $class->valid_params },cgi_request => { type => OBJECT, optional => 1 } );
61     return $class->SUPER::new(@_);
62 }
63
64
65 =head2 callback
66
67 Takes hash with optional C<CallbackPage>, C<CallbackName>
68 and C<CallbackOnce> arguments, other arguments are passed
69 throught to callback components.
70
71 =over 4
72
73 =item CallbackPage
74
75 Page path relative to the root, leading slash is mandatory.
76 By default is equal to path of the caller component.
77
78 =item CallbackName
79
80 Name of the callback. C<Default> is used unless specified.
81
82 =item CallbackOnce
83
84 By default is false, otherwise runs callbacks only once per
85 process of the server. Such callbacks can be used to fill
86 structures.
87
88 =back
89
90 Searches for callback components in
91 F<< /Callbacks/<any dir>/CallbackPage/CallbackName >>, for
92 example F</Callbacks/MyExtension/autohandler/Default> would
93 be called as default callback for F</autohandler>.
94
95 =cut
96
97 {
98 my %cache = ();
99 my %called = ();
100 sub callback {
101     my ($self, %args) = @_;
102
103     my $name = delete $args{'CallbackName'} || 'Default';
104     my $page = delete $args{'CallbackPage'} || $self->callers(0)->path;
105     unless ( $page ) {
106         $RT::Logger->error("Couldn't get a page name for callbacks");
107         return;
108     }
109
110     my $CacheKey = "$page--$name";
111     return 1 if delete $args{'CallbackOnce'} && $called{ $CacheKey };
112     $called{ $CacheKey } = 1;
113
114     my $callbacks = $cache{ $CacheKey };
115     unless ( $callbacks ) {
116         $callbacks = [];
117         my $path  = "/Callbacks/*$page/$name";
118         my @roots = RT::Interface::Web->ComponentRoots;
119         my %seen;
120         @$callbacks = (
121             grep defined && length,
122             # Skip backup files, files without a leading package name,
123             # and files we've already seen
124             grep !$seen{$_}++ && !m{/\.} && !m{~$} && m{^/Callbacks/[^/]+\Q$page/$name\E$},
125             map { sort $self->interp->resolver->glob_path($path, $_) }
126             @roots
127         );
128         foreach my $comp (keys %seen) {
129             next unless $seen{$comp} > 1;
130             $RT::Logger->error("Found more than one occurrence of the $comp callback.  This may cause only one of the callbacks to run.  Look for the duplicate Callback in your @roots");
131         }
132
133         $cache{ $CacheKey } = $callbacks unless RT->Config->Get('DevelMode');
134     }
135
136     my @rv;
137     foreach my $cb ( @$callbacks ) {
138         push @rv, scalar $self->comp( $cb, %args );
139     }
140     return @rv;
141 }
142
143 sub clear_callback_cache {
144     %cache = %called = ();
145 }
146 }
147
148 =head2 request_path
149
150 Returns path of the request.
151
152 Very close to C<< $m->request_comp->path >>, but if called in a dhandler returns
153 path of the request without dhandler name, but with dhandler arguments instead.
154
155 =cut
156
157 sub request_path {
158     my $self = shift;
159
160     my $path = $self->request_comp->path;
161     # disabled dhandlers, not RT case, but anyway
162     return $path unless my $dh_name = $self->dhandler_name;
163     # not a dhandler
164     return $path unless substr($path, -length("/$dh_name")) eq "/$dh_name";
165     substr($path, -length $dh_name) = $self->dhandler_arg;
166     return $path;
167 }
168
169 =head2 abort
170
171 Logs any recorded SQL statements for this request before calling the standard
172 abort.
173
174 =cut
175
176 sub abort {
177     my $self = shift;
178     RT::Interface::Web::LogRecordedSQLStatements(
179         RequestData => {
180             Path => $self->request_path,
181         },
182     );
183     return $self->SUPER::abort(@_);
184 }
185
186 1;