updated log format
[catagits/Catalyst-Runtime.git] / lib / Catalyst / Manual / Cookbook.pod
CommitLineData
fc7ec1d9 1=head1 NAME
2
3Catalyst::Manual::Cookbook - Cooking with Catalyst
4
5=head1 DESCRIPTION
6
aba94964 7Yummy code like your mum used to bake!
fc7ec1d9 8
9=head1 RECIPES
10
11=head2 Force debug screen
12
eff5f524 13You can force Catalyst to display the debug screen at the end of the request by
14placing a C<die()> call in the C<end> action.
fc7ec1d9 15
61b1e958 16 sub end : Private {
17 my ( $self, $c ) = @_;
2343e117 18 die "forced debug";
61b1e958 19 }
fc7ec1d9 20
379ca371 21If you're tired of removing and adding this all the time, you can add a
22condition in the C<end> action. For example:
23
24 sub end : Private {
25 my ( $self, $c ) = @_;
26 die "forced debug" if $c->req->params->{dump_info};
27 }
28
29Then just add to your query string C<"&dump_info=1">, or the like, to
30force debug output.
aff93052 31
aff93052 32
fc7ec1d9 33=head2 Disable statistics
34
35Just add this line to your application class if you don't want those nifty
36statistics in your debug messages.
37
38 sub Catalyst::Log::info { }
39
40=head2 Scaffolding
41
42Scaffolding is very simple with Catalyst.
fc7ec1d9 43
4b8cb778 44The recommended way is to use Catalyst::Helper::Controller::Scaffold.
fc7ec1d9 45
4b8cb778 46Just install this module, and to scaffold a Class::DBI Model class, do the following:
fc7ec1d9 47
4b8cb778 48./script/myapp_create controller <name> Scaffold <CDBI::Class>Scaffolding
fc7ec1d9 49
fc7ec1d9 50
fc7ec1d9 51
fc7ec1d9 52
822fe954 53=head2 File uploads
54
55=head3 Single file upload with Catalyst
aba94964 56
26e73131 57To implement uploads in Catalyst, you need to have a HTML form similar to
aba94964 58this:
59
60 <form action="/upload" method="post" enctype="multipart/form-data">
61 <input type="hidden" name="form_submit" value="yes">
62 <input type="file" name="my_file">
63 <input type="submit" value="Send">
64 </form>
65
379ca371 66It's very important not to forget C<enctype="multipart/form-data"> in
67the form.
aba94964 68
69Catalyst Controller module 'upload' action:
70
5c0ff128 71 sub upload : Global {
72 my ($self, $c) = @_;
4d89569d 73
74 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
75
76 if ( my $upload = $c->request->upload('my_file') ) {
b248fa4a 77
5c0ff128 78 my $filename = $upload->filename;
47ae6960 79 my $target = "/tmp/upload/$filename";
b248fa4a 80
3ffaf022 81 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
47ae6960 82 die( "Failed to copy '$filename' to '$target': $!" );
5c0ff128 83 }
5c0ff128 84 }
85 }
b248fa4a 86
5c0ff128 87 $c->stash->{template} = 'file_upload.html';
88 }
89
822fe954 90=head3 Multiple file upload with Catalyst
5c0ff128 91
379ca371 92Code for uploading multiple files from one form needs a few changes:
5c0ff128 93
379ca371 94The form should have this basic structure:
5c0ff128 95
96 <form action="/upload" method="post" enctype="multipart/form-data">
97 <input type="hidden" name="form_submit" value="yes">
98 <input type="file" name="file1" size="50"><br>
99 <input type="file" name="file2" size="50"><br>
100 <input type="file" name="file3" size="50"><br>
101 <input type="submit" value="Send">
102 </form>
103
379ca371 104And in the controller:
5c0ff128 105
106 sub upload : Local {
107 my ($self, $c) = @_;
4d89569d 108
109 if ( $c->request->parameters->{form_submit} eq 'yes' ) {
110
111 for my $field ( $c->req->upload ) {
112
02a53b81 113 my $upload = $c->req->upload($field);
4d89569d 114 my $filename = $upload->filename;
47ae6960 115 my $target = "/tmp/upload/$filename";
b248fa4a 116
3ffaf022 117 unless ( $upload->link_to($target) || $upload->copy_to($target) ) {
47ae6960 118 die( "Failed to copy '$filename' to '$target': $!" );
aba94964 119 }
120 }
61b1e958 121 }
4d89569d 122
5c0ff128 123 $c->stash->{template} = 'file_upload.html';
124 }
125
379ca371 126C<for my $field ($c-E<gt>req->upload)> loops automatically over all file
127input fields and gets input names. After that is basic file saving code,
128just like in single file upload.
aba94964 129
379ca371 130Notice: C<die>ing might not be what you want to do, when an error
131occurs, but it works as an example. A better idea would be to store
132error C<$!> in $c->stash->{error} and show a custom error template
133displaying this message.
aba94964 134
5c0ff128 135For more information about uploads and usable methods look at
379ca371 136L<Catalyst::Request::Upload> and L<Catalyst::Request>.
aba94964 137
e112461a 138=head2 Authentication with Catalyst::Plugin::Authentication
139
140In this example, we'll use the
141L<Catalyst::Plugin::Authentication::Store::DBIC> store and the
142L<Catalyst::Plugin::Authentication::Credential::Password> credentials.
143
144In the lib/MyApp.pm package, we'll need to change the C<use Catalyst;>
145line to include the following modules:
146
147 use Catalyst qw/
148 ConfigLoader
149 Authentication
150 Authentication::Store::DBIC
151 Authentication::Credential::Password
152 Session
153 Session::Store::FastMmap
154 Session::State::Cookie
155 HTML::Widget
156 Static::Simple
157 /;
158
159The Session, Session::Store::* and Session::State::* modules listed above
160ensure that we stay logged-in across multiple page-views.
161
162In our MyApp.yml configuration file, we'll need to add:
163
164 authentication:
165 dbic:
166 user_class: MyApp::Model::DBIC::User
167 user_field: username
168 password_field: password
169 password_type: hashed
170 password_hash_type: SHA-1
171
172'user_class' is a DBIx::Class package for your users table.
173'user_field' tells which field (column) is used for username lookup.
174'password_field' is the password field in your table.
175The above settings for 'password_type' and 'password_hash_type' ensure that
176the password won't be stored in the database in clear text.
177
178In SQLite, the users table might be something like:
179
180 CREATE TABLE user (
181 id INTEGER PRIMARY KEY,
182 username VARCHAR(100),
183 password VARCHAR(100)
184 );
185
186Now we need to create a DBIC::SchemaLoader component for this database
187(changing "myapp.db" to wherever your SQLite database is).
188
189 script/myapp_create.pl model DBIC DBIC::SchemaLoader 'dbi:SQLite:myapp.db'
190
191Now we can start creating our page controllers and templates.
192For our homepage, we create the file "root/index.tt" containing:
193
194 <html>
195 <body>
196 [% IF c.user %]
197 <p>hello [% c.user.username %]</p>
198 <p><a href="[% c.uri_for( '/logout' ) %]">logout</a></p>
199 [% ELSE %]
200 <p><a href="[% c.uri_for( '/login' ) %]">login</a></p>
201 [% END %]
202 </body>
203 </html>
204
205If the user is logged in, they will be shown their name, and a logout link.
206Otherwise, they will be shown a login link.
207
208To display the homepage, we can uncomment the C<default> and C<end>
209subroutines in lib/MyApp/Controller/Root.pm and populate them as so:
210
211 sub default : Private {
212 my ( $self, $c ) = @_;
213
214 $c->stash->{template} = 'index.tt';
215 }
deb90705 216
e112461a 217 sub end : Private {
218 my ( $self, $c ) = @_;
219
220 $c->forward( $c->view('TT') )
221 unless $c->response->body || $c->response->redirect;
222 }
deb90705 223
e112461a 224The login template is very simple, as L<HTML::Widget> will handle the
225HTML form creation for use. This is saved as "root/login.tt".
deb90705 226
e112461a 227 <html>
228 <head>
229 <link href="[% c.uri_for('/static/simple.css') %]" rel="stylesheet" type="text/css">
230 </head>
231 <body>
232 [% result %]
233 </body>
234 </html>
deb90705 235
e112461a 236For the HTML form to look correct, we also copy the C<simple.css> file
237from the L<HTML::Widget> distribution into our "root/static" folder.
238This file is automatically server by the L<Catalyst::Plugin::Static::Simple>
239module which we loaded in our lib/MyApp.pm package.
deb90705 240
e112461a 241To handle login requests, we first create a controller, like so:
deb90705 242
e112461a 243 script/myapp_create.pl controller Login
deb90705 244
e112461a 245In the lib/MyApp/Controller/Login.pm package, we can then uncomment the
246C<default> subroutine, and populate it, as below.
deb90705 247
e112461a 248First the widget is created, it needs the 'action' set, and 'username' and
249'password' fields and a submit button added.
deb90705 250
e112461a 251Then, if we've received a username and password in the request, we attempt
252to login. If successful, we redirect to the homepage; if not the login form
253will be displayed again.
deb90705 254
e112461a 255 sub default : Private {
256 my ( $self, $c ) = @_;
257
258 $c->widget->method('POST')->action( $c->uri_for('/login') );
259 $c->widget->element( 'Textfield', 'username' )->label( 'Username' );
260 $c->widget->element( 'Password', 'password' )->label( 'Password' );
261 $c->widget->element( 'Submit' )->value( 'Login' );
262
263 my $result = $c->widget->process( $c->req );
264
265 if ( my $user = $result->param('username')
266 and my $pass = $result->param('password') )
267 {
268 if ( $c->login( $user, $pass ) ) {
269 $c->response->redirect( $c->uri_for( "/" ) );
270 return;
deb90705 271 }
272 }
e112461a 273
274 $c->stash->{template} = 'login.tt';
275 $c->stash->{result} = $result;
61b1e958 276 }
deb90705 277
e112461a 278To handle logout's, we create a new controller:
deb90705 279
e112461a 280 script/myapp_create.pl controller Logout
deb90705 281
e112461a 282Then in the lib/MyApp/Controller/Logout.pm package, we change the
283C<default> subroutine, to logout and then redirect back to the
284homepage.
51ef2818 285
e112461a 286 sub default : Private {
287 my ( $self, $c ) = @_;
288
289 $c->logout;
290
291 $c->response->redirect( $c->uri_for( "/" ) );
61b1e958 292 }
deb90705 293
e112461a 294Remember that to test this, we would first need to add a user to the
295database, ensuring that the password field is saved as the SHA1 hash
296of our desired password.
deb90705 297
deb90705 298
afb208ae 299=head2 Pass-through login (and other actions)
300
eff5f524 301An easy way of having assorted actions that occur during the processing
302of a request that are orthogonal to its actual purpose - logins, silent
afb208ae 303commands etc. Provide actions for these, but when they're required for
eff5f524 304something else fill e.g. a form variable __login and have a sub begin
305like so:
afb208ae 306
eff5f524 307 sub begin : Private {
308 my ($self, $c) = @_;
309 foreach my $action (qw/login docommand foo bar whatever/) {
310 if ($c->req->params->{"__${action}"}) {
311 $c->forward($action);
312 }
313 }
afb208ae 314 }
145074c2 315
316=head2 How to use Catalyst without mod_perl
317
318Catalyst applications give optimum performance when run under mod_perl.
61b1e958 319However sometimes mod_perl is not an option, and running under CGI is
51ef2818 320just too slow. There's also an alternative to mod_perl that gives
dec2a2a9 321reasonable performance named FastCGI.
145074c2 322
822fe954 323=head3 Using FastCGI
145074c2 324
61b1e958 325To quote from L<http://www.fastcgi.com/>: "FastCGI is a language
326independent, scalable, extension to CGI that provides high performance
327without the limitations of specific server APIs." Web server support
328is provided for Apache in the form of C<mod_fastcgi> and there is Perl
329support in the C<FCGI> module. To convert a CGI Catalyst application
330to FastCGI one needs to initialize an C<FCGI::Request> object and loop
331while the C<Accept> method returns zero. The following code shows how
332it is done - and it also works as a normal, single-shot CGI script.
145074c2 333
334 #!/usr/bin/perl
335 use strict;
336 use FCGI;
337 use MyApp;
338
339 my $request = FCGI::Request();
340 while ($request->Accept() >= 0) {
1c61c726 341 MyApp->run;
145074c2 342 }
343
61b1e958 344Any initialization code should be included outside the request-accept
345loop.
145074c2 346
51ef2818 347There is one little complication, which is that C<MyApp-E<gt>run> outputs a
61b1e958 348complete HTTP response including the status line (e.g.:
349"C<HTTP/1.1 200>").
350FastCGI just wants a set of headers, so the sample code captures the
351output and drops the first line if it is an HTTP status line (note:
352this may change).
353
354The Apache C<mod_fastcgi> module is provided by a number of Linux
de6fb80a 355distro's and is straightforward to compile for most Unix-like systems.
61b1e958 356The module provides a FastCGI Process Manager, which manages FastCGI
357scripts. You configure your script as a FastCGI script with the
358following Apache configuration directives:
145074c2 359
360 <Location /fcgi-bin>
361 AddHandler fastcgi-script fcgi
362 </Location>
363
364or:
365
366 <Location /fcgi-bin>
367 SetHandler fastcgi-script
368 Action fastcgi-script /path/to/fcgi-bin/fcgi-script
369 </Location>
370
371C<mod_fastcgi> provides a number of options for controlling the FastCGI
372scripts spawned; it also allows scripts to be run to handle the
51ef2818 373authentication, authorization, and access check phases.
145074c2 374
61b1e958 375For more information see the FastCGI documentation, the C<FCGI> module
376and L<http://www.fastcgi.com/>.
b248fa4a 377
379ca371 378=head2 Serving static content
379
380Serving static content in Catalyst can be somewhat tricky; this recipe
381shows one possible solution. Using this recipe will serve all static
382content through Catalyst when developing with the built-in HTTP::Daemon
383server, and will make it easy to use Apache to serve the content when
384your app goes into production.
385
386Static content is best served from a single directory within your root
387directory. Having many different directories such as C<root/css> and
388C<root/images> requires more code to manage, because you must separately
389identify each static directory--if you decide to add a C<root/js>
390directory, you'll need to change your code to account for it. In
391contrast, keeping all static directories as subdirectories of a main
392C<root/static> directory makes things much easier to manager. Here's an
393example of a typical root directory structure:
394
395 root/
396 root/content.tt
397 root/controller/stuff.tt
398 root/header.tt
399 root/static/
400 root/static/css/main.css
401 root/static/images/logo.jpg
402 root/static/js/code.js
403
404
405All static content lives under C<root/static> with everything else being
406Template Toolkit files. Now you can identify the static content by
407matching C<static> from within Catalyst.
408
409=head3 Serving with HTTP::Daemon (myapp_server.pl)
410
411To serve these files under the standalone server, we first must load the
412Static plugin. Install L<Catalyst::Plugin::Static> if it's not already
413installed.
414
415In your main application class (MyApp.pm), load the plugin:
416
417 use Catalyst qw/-Debug FormValidator Static OtherPlugin/;
418
419You will also need to make sure your end method does I<not> forward
420static content to the view, perhaps like this:
421
422 sub end : Private {
423 my ( $self, $c ) = @_;
424
425 $c->forward( 'MyApp::V::TT' )
426 unless ( $c->res->body || !$c->stash->{template} );
427 }
428
429This code will only forward to the view if a template has been
430previously defined by a controller and if there is not already data in
431C<$c-E<gt>res-E<gt>body>.
432
433Next, create a controller to handle requests for the /static path. Use
434the Helper to save time. This command will create a stub controller as
435C<lib/MyApp/C/Static.pm>.
436
437 $ script/myapp_create.pl controller Static
438
439Edit the file and add the following methods:
440
441 # serve all files under /static as static files
442 sub default : Path('/static') {
443 my ( $self, $c ) = @_;
b248fa4a 444
379ca371 445 # Optional, allow the browser to cache the content
446 $c->res->headers->header( 'Cache-Control' => 'max-age=86400' );
447
448 $c->serve_static; # from Catalyst::Plugin::Static
449 }
450
451 # also handle requests for /favicon.ico
452 sub favicon : Path('/favicon.ico') {
453 my ( $self, $c ) = @_;
b248fa4a 454
379ca371 455 $c->serve_static;
456 }
457
458You can also define a different icon for the browser to use instead of
459favicon.ico by using this in your HTML header:
460
461 <link rel="icon" href="/static/myapp.ico" type="image/x-icon" />
462
463=head3 Common problems
464
465The Static plugin makes use of the C<shared-mime-info> package to
466automatically determine MIME types. This package is notoriously
26e73131 467difficult to install, especially on win32 and OS X. For OS X the easiest
379ca371 468path might be to install Fink, then use C<apt-get install
469shared-mime-info>. Restart the server, and everything should be fine.
470
471Make sure you are using the latest version (>= 0.16) for best
472results. If you are having errors serving CSS files, or if they get
473served as text/plain instead of text/css, you may have an outdated
474shared-mime-info version. You may also wish to simply use the following
475code in your Static controller:
476
477 if ($c->req->path =~ /css$/i) {
478 $c->serve_static( "text/css" );
479 } else {
480 $c->serve_static;
481 }
482
483=head3 Serving with Apache
484
485When using Apache, you can completely bypass Catalyst and the Static
486controller by intercepting requests for the C<root/static> path at the
487server level. All that is required is to define a DocumentRoot and add a
488separate Location block for your static content. Here is a complete
6d23e1f4 489config for this application under mod_perl 1.x:
379ca371 490
491 <Perl>
492 use lib qw(/var/www/MyApp/lib);
493 </Perl>
494 PerlModule MyApp
b248fa4a 495
379ca371 496 <VirtualHost *>
497 ServerName myapp.example.com
498 DocumentRoot /var/www/MyApp/root
499 <Location />
500 SetHandler perl-script
501 PerlHandler MyApp
502 </Location>
503 <LocationMatch "/(static|favicon.ico)">
504 SetHandler default-handler
505 </LocationMatch>
506 </VirtualHost>
507
6d23e1f4 508And here's a simpler example that'll get you started:
509
510 Alias /static/ "/my/static/files/"
511 <Location "/static">
512 SetHandler none
513 </Location>
514
379ca371 515=head2 Forwarding with arguments
145074c2 516
eff5f524 517Sometimes you want to pass along arguments when forwarding to another
518action. As of version 5.30, arguments can be passed in the call to
519C<forward>; in earlier versions, you can manually set the arguments in
520the Catalyst Request object:
e6394847 521
eff5f524 522 # version 5.30 and later:
523 $c->forward('/wherever', [qw/arg1 arg2 arg3/]);
2343e117 524
eff5f524 525 # pre-5.30
2343e117 526 $c->req->args([qw/arg1 arg2 arg3/]);
527 $c->forward('/wherever');
528
b248fa4a 529(See the L<Catalyst::Manual::Intro> Flow_Control section for more
530information on passing arguments via C<forward>.)
eff5f524 531
822fe954 532=head2 Configure your application
533
534You configure your application with the C<config> method in your
535application class. This can be hard-coded, or brought in from a
536separate configuration file.
537
538=head3 Using YAML
539
540YAML is a method for creating flexible and readable configuration
541files. It's a great way to keep your Catalyst application configuration
542in one easy-to-understand location.
543
544In your application class (e.g. C<lib/MyApp.pm>):
545
546 use YAML;
547 # application setup
548 __PACKAGE__->config( YAML::LoadFile(__PACKAGE__->config->{'home'} . '/myapp.yml') );
549 __PACKAGE__->setup;
550
551Now create C<myapp.yml> in your application home:
552
553 --- #YAML:1.0
554 # DO NOT USE TABS FOR INDENTATION OR label/value SEPARATION!!!
555 name: MyApp
556
822fe954 557 # session; perldoc Catalyst::Plugin::Session::FastMmap
558 session:
559 expires: '3600'
560 rewrite: '0'
561 storage: '/tmp/myapp.session'
562
563 # emails; perldoc Catalyst::Plugin::Email
564 # this passes options as an array :(
565 email:
566 - SMTP
567 - localhost
568
569This is equivalent to:
570
571 # configure base package
572 __PACKAGE__->config( name => MyApp );
573 # configure authentication
574 __PACKAGE__->config->{authentication} = {
575 user_class => 'MyApp::M::MyDB::Customer',
576 ...
577 };
578 # configure sessions
579 __PACKAGE__->config->{session} = {
580 expires => 3600,
581 ...
582 };
583 # configure email sending
584 __PACKAGE__->config->{email} = [qw/SMTP localhost/];
585
586See also L<YAML>.
587
e112461a 588=head2 Using existing DBIC (etc.) classes with Catalyst
3912ee04 589
590Many people have existing Model classes that they would like to use with
591Catalyst (or, conversely, they want to write Catalyst models that can be
592used outside of Catalyst, e.g. in a cron job). It's trivial to write a
593simple component in Catalyst that slurps in an outside Model:
594
e112461a 595 package MyApp::Model::DB;
596 use base qw/Catalyst::Model::DBIC::Schema/;
597 __PACKAGE__->config(
598 schema_class => 'Some::DBIC::Schema',
599 connect_info => ['dbi:SQLite:foo.db', '', '', {AutoCommit=>1}];
600 );
3912ee04 601 1;
602
e112461a 603and that's it! Now C<Some::DBIC::Schema> is part of your
604Cat app as C<MyApp::Model::DB>.
3912ee04 605
6d23e1f4 606=head2 Delivering a Custom Error Page
f25a3283 607
6d23e1f4 608By default, Catalyst will display its own error page whenever it
609encounters an error in your application. When running under C<-Debug>
610mode, the error page is a useful screen including the error message and
f63c03e4 611L<Data::Dump> output of the relevant parts of the C<$c> context object.
612When not in C<-Debug>, users see a simple "Please come back later" screen.
f25a3283 613
26e73131 614To use a custom error page, use a special C<end> method to short-circuit
6d23e1f4 615the error processing. The following is an example; you might want to
616adjust it further depending on the needs of your application (for
617example, any calls to C<fillform> will probably need to go into this
618C<end> method; see L<Catalyst::Plugin::FillInForm>).
f25a3283 619
6d23e1f4 620 sub end : Private {
621 my ( $self, $c ) = @_;
b248fa4a 622
6d23e1f4 623 if ( scalar @{ $c->error } ) {
624 $c->stash->{errors} = $c->error;
625 $c->stash->{template} = 'errors.tt';
626 $c->forward('MyApp::View::TT');
121baf93 627 $c->error(0);
6d23e1f4 628 }
b248fa4a 629
6d23e1f4 630 return 1 if $c->response->status =~ /^3\d\d$/;
631 return 1 if $c->response->body;
b248fa4a 632
6d23e1f4 633 unless ( $c->response->content_type ) {
634 $c->response->content_type('text/html; charset=utf-8');
635 }
b248fa4a 636
6d23e1f4 637 $c->forward('MyApp::View::TT');
638 }
f25a3283 639
6d23e1f4 640You can manually set errors in your code to trigger this page by calling
f25a3283 641
6d23e1f4 642 $c->error( 'You broke me!' );
f25a3283 643
d761e049 644=head2 Require user logins
645
646It's often useful to restrict access to your application to a set of
647registered users, forcing everyone else to the login page until they're
648signed in.
649
650To implement this in your application make sure you have a customer
651table with username and password fields and a corresponding Model class
652in your Catalyst application, then make the following changes:
653
654=head3 lib/MyApp.pm
655
158c8782 656 use Catalyst qw/
657 Authentication
658 Authentication::Store::DBIC
659 Authentication::Credential::Password
660 /;
661
662 __PACKAGE__->config->{authentication}->{dbic} = {
663 'user_class' => 'My::Model::DBIC::User',
d761e049 664 'user_field' => 'username',
158c8782 665 'password_field' => 'password'
666 'password_type' => 'hashed',
667 'password_hash_type'=> 'SHA-1'
d761e049 668 };
669
670 sub auto : Private {
671 my ($self, $c) = @_;
672 my $login_path = 'user/login';
673
674 # allow people to actually reach the login page!
158c8782 675 if ($c->request->path eq $login_path) {
d761e049 676 return 1;
677 }
678
158c8782 679 # if a user doesn't exist, force login
680 if ( !$c->user_exists ) {
d761e049 681 # force the login screen to be shown
158c8782 682 $c->response->redirect($c->request->base . $login_path);
d761e049 683 }
684
158c8782 685 # otherwise, we have a user - continue with the processing chain
d761e049 686 return 1;
687 }
688
689=head3 lib/MyApp/C/User.pm
690
691 sub login : Path('/user/login') {
692 my ($self, $c) = @_;
693
694 # default template
695 $c->stash->{'template'} = "user/login.tt";
696 # default form message
697 $c->stash->{'message'} = 'Please enter your username and password';
698
158c8782 699 if ( $c->request->param('username') ) {
d761e049 700 # try to log the user in
158c8782 701 # login() is provided by ::Authentication::Credential::Password
702 if( $c->login(
703 $c->request->param('username'),
704 $c->request->param('password'),
705 );
b248fa4a 706
158c8782 707 # if login() returns 1, user is now logged in
708 $c->response->redirect('/some/page');
d761e049 709 }
710
711 # otherwise we failed to login, try again!
158c8782 712 $c->stash->{'message'} =
713 'Unable to authenticate the login details supplied';
d761e049 714 }
715 }
716
717 sub logout : Path('/user/logout') {
718 my ($self, $c) = @_;
158c8782 719 # log the user out
720 $c->logout;
d761e049 721
722 # do the 'default' action
158c8782 723 $c->response->redirect($c->request->base);
724 }
d761e049 725
726
727=head3 root/base/user/login.tt
728
729 [% INCLUDE header.tt %]
730 <form action="/user/login" method="POST" name="login_form">
731 [% message %]<br />
732 <label for="username">username:</label><br />
733 <input type="text" id="username" name="username" /><br />
734
735 <label for="password">password:</label><br />
736 <input type="password" id="password" name="password" /><br />
737
738 <input type="submit" value="log in" name="form_submit" />
739 </form>
740 [% INCLUDE footer.tt %]
741
158c8782 742=head2 Role-based Authorization
743
744For more advanced access control, you may want to consider using role-based
745authorization. This means you can assign different roles to each user, e.g.
746"user", "admin", etc.
747
748The C<login> and C<logout> methods and view template are exactly the same as
749in the previous example.
750
751The L<Catalyst::Plugin::Authorization::Roles> plugin is required when
752implementing roles:
753
754 use Catalyst qw/
755 Authentication
756 Authentication::Credential::Password
757 Authentication::Store::Htpasswd
758 Authorization::Roles
759 /;
760
761Roles are implemented automatically when using
de6fb80a 762L<Catalyst::Authentication::Store::Htpasswd>:
158c8782 763
764 # no additional role configuration required
765 __PACKAGE__->config->{authentication}{htpasswd} = "passwdfile";
766
767Or can be set up manually when using L<Catalyst::Authentication::Store::DBIC>:
768
769 # Authorization using a many-to-many role relationship
770 __PACKAGE__->config->{authorization}{dbic} = {
771 'role_class' => 'My::Model::DBIC::Role',
772 'role_field' => 'name',
773 'user_role_user_field' => 'user',
774
775 # DBIx::Class only (omit if using Class::DBI)
776 'role_rel' => 'user_role',
777
778 # Class::DBI only, (omit if using DBIx::Class)
779 'user_role_class' => 'My::Model::CDBI::UserRole'
780 'user_role_role_field' => 'role',
781 };
782
783To restrict access to any action, you can use the C<check_user_roles> method:
784
785 sub restricted : Local {
786 my ( $self, $c ) = @_;
787
788 $c->detach("unauthorized")
789 unless $c->check_user_roles( "admin" );
790
791 # do something restricted here
792 }
793
794You can also use the C<assert_user_roles> method. This just gives an error if
795the current user does not have one of the required roles:
796
797 sub also_restricted : Global {
798 my ( $self, $c ) = @_;
799 $c->assert_user_roles( qw/ user admin / );
800 }
801
fc7ec1d9 802=head1 AUTHOR
803
804Sebastian Riedel, C<sri@oook.de>
379ca371 805Danijel Milicevic, C<me@danijel.de>
806Viljo Marrandi, C<vilts@yahoo.com>
822fe954 807Marcus Ramberg, C<mramberg@cpan.org>
808Jesse Sheidlower, C<jester@panix.com>
379ca371 809Andy Grundman, C<andy@hybridized.org>
3912ee04 810Chisel Wright, C<pause@herlpacker.co.uk>
158c8782 811Will Hawes, C<info@whawes.co.uk>
de6fb80a 812Gavin Henry, C<ghenry@cpan.org> (Spell checking)
813
fc7ec1d9 814
815=head1 COPYRIGHT
816
61b1e958 817This program is free software, you can redistribute it and/or modify it
818under the same terms as Perl itself.