Added Auth::CDBI cookbook entry
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Manual / Cookbook.pod
1 =head1 NAME
2
3 Catalyst::Manual::Cookbook - Cooking with Catalyst
4
5 =head1 DESCRIPTION
6
7 Yummy code like your mum used to bake!
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
71 =head2 Serving static files and CSS as text/css
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
96 =head2 Uploads with Catalyst
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
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
263 =head1 AUTHOR
264
265 Sebastian Riedel, C<sri@oook.de>
266 Danijel Milicevic C<me@danijel.de>
267 Viljo Marrandi C<vilts@yahoo.com>
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.