Commit | Line | Data |
fc7ec1d9 |
1 | =head1 NAME |
2 | |
3 | Catalyst::Manual::Cookbook - Cooking with Catalyst |
4 | |
5 | =head1 DESCRIPTION |
6 | |
aba94964 |
7 | Yummy code like your mum used to bake! |
fc7ec1d9 |
8 | |
9 | =head1 RECIPES |
10 | |
11 | =head2 Force debug screen |
12 | |
13 | You can force Catalyst to display the debug screen at the end of the request by |
14 | placing a die() call in the _end action. |
15 | |
16 | __PACKAGE__->action( |
17 | '!end' => sub { |
18 | my ( $self, $c ) = @_; |
19 | die "testing"; |
20 | } |
21 | ); |
22 | |
aff93052 |
23 | If you're tired of removing and adding this all the time, you |
24 | can easily add a condition. for example: |
25 | |
26 | die "Testing" if $c->param->{dump_info}; |
27 | |
fc7ec1d9 |
28 | =head2 Disable statistics |
29 | |
30 | Just add this line to your application class if you don't want those nifty |
31 | statistics in your debug messages. |
32 | |
33 | sub Catalyst::Log::info { } |
34 | |
35 | =head2 Scaffolding |
36 | |
37 | Scaffolding is very simple with Catalyst. |
38 | Just use Catalyst::Model::CDBI::CRUD as baseclass. |
39 | |
40 | # lib/MyApp/Model/CDBI.pm |
41 | package MyApp::Model::CDBI; |
42 | |
43 | use strict; |
44 | use base 'Catalyst::Model::CDBI::CRUD'; |
45 | |
46 | __PACKAGE__->config( |
47 | dsn => 'dbi:SQLite:/tmp/myapp.db', |
48 | relationships => 1 |
49 | ); |
50 | |
51 | 1; |
52 | |
53 | # lib/MyApp.pm |
54 | package MyApp; |
55 | |
56 | use Catalyst 'FormValidator'; |
57 | |
58 | __PACKAGE__->config( |
59 | name => 'My Application', |
60 | root => '/home/joeuser/myapp/root' |
61 | ); |
62 | |
63 | __PACKAGE__->action( |
64 | 'table' => sub { |
65 | my ( $self, $c ) = @_; |
66 | $c->form( optional => [ MyApp::Model::CDBI::Table->columns ] ); |
67 | $c->forward('MyApp::Model::CDBI::Table'); |
68 | } |
69 | ); |
70 | |
71 | 1; |
72 | |
73 | Modify the $c->form() parameters to match your needs, and don't forget to copy |
74 | the templates. ;) |
75 | |
deb90705 |
76 | =head2 Serving static files and CSS as text/css |
aba94964 |
77 | |
78 | If you want to serve static content (like images, txt or CSS) via Catalyst, |
79 | then all you need is the plugin Catalyst::Plugin::Static as well as a small |
80 | regex to set the MIME type for CSS to text/css. |
81 | |
82 | # lib/MyApp.pm |
83 | package MyApp; |
84 | |
85 | use strict; |
86 | use Catalyst qw/-Debug Static/; |
87 | |
88 | __PACKAGE__->action( |
89 | |
90 | '!default' => sub { |
91 | my ( $self, $c ) = @_; |
92 | $c->serve_static; |
93 | }, |
94 | |
95 | '/^.*\.css$/' => sub { |
96 | my ( $self, $c ) = @_; |
97 | $c->serve_static('text/css'); |
98 | }, |
99 | ); |
100 | |
deb90705 |
101 | =head2 Uploads with Catalyst |
aba94964 |
102 | |
103 | To implement uploads in Catalyst you need to have a HTML form similiar to |
104 | this: |
105 | |
106 | <form action="/upload" method="post" enctype="multipart/form-data"> |
107 | <input type="hidden" name="form_submit" value="yes"> |
108 | <input type="file" name="my_file"> |
109 | <input type="submit" value="Send"> |
110 | </form> |
111 | |
112 | It's very important not to forget enctype="multipart/form-data" in form, |
113 | if it's not there, uploads just don't work. |
114 | |
115 | Catalyst Controller module 'upload' action: |
116 | |
117 | MyApp->action( |
118 | |
119 | 'upload' => sub { |
120 | my ($self, $c) = @_; |
121 | if ($c->req->parameters->{form_submit} eq 'yes') { |
122 | my $filename = $c->req->parameters->{my_file}; |
123 | if ($filename) { |
124 | my $fh = $c->req->uploads->{$filename}->{fh}; |
125 | open(NEW_FILE, ">/tmp/$filename") or die |
126 | "Can't open file for writing: $!"; |
127 | while ($fh->read(my $buf, 32768)) { |
128 | print NEW_FILE $buf; |
129 | } |
130 | close(NEW_FILE); |
131 | } |
132 | } |
133 | $c->stash->{template} = 'upload_form.tt'; |
134 | $c->forward('MyApp::V::View'); |
135 | }, |
136 | ); |
137 | |
138 | If you want to upload bigger files than 1MB, then just add to your Controller |
139 | module: |
140 | |
141 | $CGI::Simple::POST_MAX = 1048576000; |
142 | |
deb90705 |
143 | =head2 Authentication with Catalyst::Plugin::Authentication::CDBI |
144 | |
145 | There are (at least) two ways to implement authentication with this plugin: |
146 | 1) only checking username and password |
147 | 2) checking username, password and the roles the user has |
148 | |
149 | For both variants you'll need the following code in your MyApp package: |
150 | |
151 | use Catalyst qw/Session::FastMmap Static Authentication::CDBI/; |
152 | |
153 | MyApp->config( authentication => { user_class => 'MyApp::M::MyApp::Users', |
154 | user_field => 'email', |
155 | password_field => 'password' }); |
156 | |
157 | 'user_class' is a Class::DBI class for your users table. |
158 | 'user_field' tells which field is used for username lookup (might be |
159 | email, first name, surname etc). |
160 | 'password_field' is, well, password field in your table and by default |
161 | password is stored in plain text. Authentication::CDBI looks for 'user' |
162 | and 'password' fields in table, if they're not defined in the config. |
163 | |
164 | In PostgreSQL users table might be something like: |
165 | |
166 | CREATE TABLE users ( |
167 | user_id serial, |
168 | name varchar(100), |
169 | surname varchar(100), |
170 | password varchar(100), |
171 | email varchar(100), |
172 | primary key(user_id) |
173 | ); |
174 | |
175 | We'll discuss the first variant for now: |
176 | 1. user:password login / auth without roles |
177 | |
178 | To log in a user you might use a action like this: |
179 | |
180 | '?login' => sub { |
181 | my ($self, $c) = @_; |
182 | if ($c->req->params->{username}) { |
183 | $c->session_login($c->req->params->{username}, |
184 | $c->req->params->{password} ); |
185 | if ($c->req->{user}) { |
186 | $c->forward('?restricted_area'); |
187 | } |
188 | } |
189 | }, |
190 | |
191 | $c->req->params->{username} and $c->req->params->{password} are html |
192 | form parameters from a login form. If login succeeds, then $c->req->{user} |
193 | contains the username of the authenticated user. |
194 | |
195 | If you want to remember the users login status inbetween further requests, |
196 | then just use the $c->session_login method, Catalyst will create a session |
197 | id, session cookie and automatically append session id to all urls. So |
198 | all you have to do, is just check $c->req->{user} where needed. |
199 | |
200 | To log out user, just call $c->session_logout. |
201 | |
202 | Now lets take a look at the second variant: |
203 | 2. user:password login / auth with roles |
204 | |
205 | To use roles you need to add to MyApp->config in the 'authentication' |
206 | section following parameters: |
207 | |
208 | role_class => 'MyApp::M::MyApp::Roles', |
209 | user_role_class => 'MyApp::M::MyApp::UserRoles', |
210 | user_role_user_field => 'user_id', |
211 | user_role_role_field => 'role_id', |
212 | |
213 | Corresponding tables in PostgreSQL could look like this: |
214 | |
215 | CREATE TABLE roles ( |
216 | role_id serial, |
217 | name varchar(100), |
218 | primary key(role_id) |
219 | ); |
220 | |
221 | CREATE TABLE user_roles ( |
222 | user_role_id serial, |
223 | user_id int, |
224 | role_id int, |
225 | primary key(user_role_id), |
226 | foreign key(user_id) references users(user_id), |
227 | foreign key(role_id) references roles(role_id) |
228 | ); |
229 | |
230 | The 'roles' table is a list of role names and the 'user_role' table is used for |
231 | the user -> role lookup. |
232 | |
233 | Now if a logged in user wants to see a location which is allowed only for |
234 | people with 'admin' role then in you controller you can check it with: |
235 | |
236 | '?add' => sub { |
237 | my ($self, $c) = @_; |
238 | if ($c->roles(qw/admin/)) { |
239 | $c->req->output("Your account has the role 'admin.'"); |
240 | } else { |
241 | $c->req->output("You're not allowed to be here"); |
242 | } |
243 | }, |
244 | |
245 | One thing you might need is to forward non-authenticated users to login |
246 | form, if they try to access restricted areas. If you want to do this |
247 | controller-wide (if you have one controller for admin section) then it's |
248 | best to add user check to '!begin' action: |
249 | |
250 | '!begin' => sub { |
251 | my ($self, $c) = @_; |
252 | unless ($c->req->{user}) { |
253 | $c->req->action(undef); ## notice this!! |
254 | $c->forward('?login'); |
255 | } |
256 | }, |
257 | |
258 | Pay attention to $c->req->action(undef). This is needed, because of the |
259 | way $c->forward works - forward to login gets called, but after that |
260 | Catalyst executes anyway the action defined in the uri (eg. if you tried to |
261 | watch /add, then first '!begin' forwards to '?login', but after that |
262 | anyway '?add' is executed). So $c->req->action(undef) undefines any |
263 | actions that were to be called and forwards user where we want him/her |
264 | to be. |
265 | |
266 | And this is all you need to do, isn't Catalyst wonderful? |
267 | |
fc7ec1d9 |
268 | =head1 AUTHOR |
269 | |
270 | Sebastian Riedel, C<sri@oook.de> |
deb90705 |
271 | Danijel Milicevic C<me@danijel.de> |
272 | Viljo Marrandi C<vilts@yahoo.com> |
fc7ec1d9 |
273 | |
274 | =head1 COPYRIGHT |
275 | |
276 | This program is free software, you can redistribute it and/or modify it under |
277 | the same terms as Perl itself. |