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