a2a380b934b793d6aa52bc8802082639f986e21b
[kivitendo-erp.git] / SL / Controller / Base.pm
1 package SL::Controller::Base;
2
3 use parent qw(Rose::Object);
4
5 use List::Util qw(first);
6
7 #
8 # public/helper functions
9 #
10
11 sub parse_html_template {
12   my $self   = shift;
13   my $name   = shift;
14   my $locals = shift || {};
15
16   return $::form->parse_html_template($name, { %{ $locals }, SELF => $self });
17 }
18
19 sub url_for {
20   my $self = shift;
21
22   return $_[0] if (scalar(@_) == 1) && !ref($_[0]);
23
24   my %params      = ref($_[0]) eq 'HASH' ? %{ $_[0] } : @_;
25   my $controller  = delete($params{controller}) || $self->_controller_name;
26   my $action      = delete($params{action})     || 'dispatch';
27   $params{action} = "${controller}/${action}";
28   my $query       = join('&', map { $::form->escape($_) . '=' . $::form->escape($params{$_}) } keys %params);
29
30   return "controller.pl?${query}";
31 }
32
33 sub redirect_to {
34   my $self = shift;
35   my $url  = $self->url_for(@_);
36
37   print $::cgi->redirect($url);
38 }
39
40 #
41 # private functions -- for use in Base only
42 #
43
44 sub _run_action {
45   my $self   = shift;
46   my $action = "action_" . shift;
47
48   return $self->_dispatch(@_) if $action eq 'action_dispatch';
49
50   $::form->error("Invalid action ${action} for controller " . ref($self)) if !$self->can($action);
51   $self->$action(@_);
52 }
53
54 sub _controller_name {
55   return (split(/::/, ref($_[0])))[-1];
56 }
57
58 sub _dispatch {
59   my $self    = shift;
60
61   my @actions = grep { m/^action_/ } keys %{ ref($self) . "::" };
62   my $action  = first { $::form->{$_} } @actions;
63
64   $self->$action(@_);
65 }
66
67 1;
68
69 __END__
70
71 =head1 NAME
72
73 SL::Controller::Base - base class for all action controllers
74
75 =head1 SYNOPSIS
76
77 =head2 OVERVIEW
78
79 This is a base class for all action controllers. Action controllers
80 provide subs that are callable by special URLs.
81
82 For each request made to the web server an instance of the controller
83 will be created. After the request has been served that instance will
84 handed over to garbage collection.
85
86 This base class is derived from L<Rose::Object>.
87
88 =head2 CONVENTIONS
89
90 The URLs have the following properties:
91
92 =over 2
93
94 =item *
95
96 The script part of the URL must be C<controller.pl>.
97
98 =item *
99
100 There must be a GET or POST parameter named C<action> containing the
101 name of the controller and the sub to call separated by C</>,
102 e.g. C<Message/list>.
103
104 =item *
105
106 The controller name is the package's name without the
107 C<SL::Controller::> prefix. At the moment only packages in the
108 C<SL::Controller> namespace are valid; sub-namespaces are not
109 allowed. The package name must start with an upper-case letter.
110
111 =item *
112
113 The sub part of the C<action> parameter is the name of the sub to
114 call. However, the sub's name is automatically prefixed with
115 C<action_>. Therefore for the example C<Message/list> the sub
116 C<SL::DB::Message::action_list> would be called. This in turn means
117 that subs whose name does not start with C<action_> cannot be invoked
118 directly via the URL.
119
120 =back
121
122 =head2 INDIRECT DISPATCHING
123
124 In the case that there are several submit buttons on a page it is
125 often impractical to have a single C<action> parameter match up
126 properly. For such a case a special dispatcher method is available. In
127 that case the C<action> parameter of the URL must be
128 C<Controller/dispatch>.
129
130 The C<SL::Controller::Base::_dispatch> method will iterate over all
131 subs in the controller package whose names start with C<action_>. The
132 first one for which there's a GET or POST parameter with the same name
133 and that's trueish is called.
134
135 Usage from a template usually looks like this:
136
137   <form method="POST" action="controller.pl">
138     ...
139     <input type="hidden" name="action" value="Message/dispatch">
140     <input type="submit" name="action_mark_as_read" value="Mark messages as read">
141     <input type="submit" name="action_delete" value="Delete messages">
142   </form>
143
144 The dispatching is handled by the function L</_dispatch>.
145
146 =head1 FUNCTIONS
147
148 =head2 PUBLIC HELPER FUNCTIONS
149
150 These functions are supposed to be called by sub-classed controllers.
151
152 =over 4
153
154 =item C<parse_html_template $file_name, $local_variables>
155
156 Outputs an HTML template. It is a thin wrapper around
157 C<Form::parse_html_template> which also adds the current object as the
158 template variable C<SELF>.
159
160 =item C<url_for $url>
161
162 =item C<url_for $params>
163
164 =item C<url_for %params>
165
166 Creates an URL for the given parameters suitable for calling an action
167 controller. If there's only one scalar parameter then it is returned
168 verbatim.
169
170 Otherwise the parameters are given either as a single hash ref
171 parameter or as a normal hash.
172
173 The controller to call is given by C<$params{controller}>. It defaults
174 to the current controller as returned by
175 L</_controller_name>.
176
177 The action to call is given by C<$params{action}>. It defaults to
178 C<dispatch>.
179
180 All other key/value pairs in C<%params> are appended as GET parameters
181 to the URL.
182
183 Usage from a template might look like this:
184
185   <a href="[% SELF.url_for(controller => 'Message', action => 'new', recipient_id => 42) %]">create new message</a>
186
187 =item redirect_to %url_params
188
189 Redirects the browser to a new URL by outputting a HTTP redirect
190 header. The URL is generated by calling L</url_for> with
191 C<%url_params>.
192
193 =back
194
195 =head2 PRIVATE FUNCTIONS
196
197 These functions are supposed to be used from this base class only.
198
199 =over 4
200
201 =item C<_controller_name>
202
203 Returns the name of the curernt controller package without the
204 C<SL::Controller::> prefix.
205
206 =item C<_dispatch>
207
208 Implements the method lookup for indirect dispatching mentioned in the
209 section L</INDIRECT DISPATCHING>.
210
211 =item C<_run_action $action>
212
213 Executes a sub based on the value of C<$action>. C<$action> is the sub
214 name part of the C<action> GET or POST parameter as described in
215 L</CONVENTIONS>.
216
217 If C<$action> equals C<dispatch> then the sub L</_dispatch> in this
218 base class is called for L</INDIRECT DISPATCHING>. Otherwise
219 C<$action> is prefixed with C<action_>, and that sub is called on the
220 current controller instance.
221
222 =back
223
224 =head1 AUTHOR
225
226 Moritz Bunkus E<lt>m.bunkus@linet-services.deE<gt>
227
228 =cut