changed user table name to users in order to dodge reserved word
[catagits/Catalyst-Manual.git] / lib / Catalyst / Manual / Tutorial / 05_Authentication.pod
index bf47ad6..3831d6a 100644 (file)
@@ -82,10 +82,10 @@ authorization section, Chapter 6).  Create a new SQL script file by opening
 C<myapp02.sql> in your editor and insert:
 
     --
-    -- Add user and role tables, along with a many-to-many join table
+    -- Add users and role tables, along with a many-to-many join table
     --
     PRAGMA foreign_keys = ON;
-    CREATE TABLE user (
+    CREATE TABLE users (
             id            INTEGER PRIMARY KEY,
             username      TEXT,
             password      TEXT,
@@ -106,9 +106,9 @@ C<myapp02.sql> in your editor and insert:
     --
     -- Load up some initial test data
     --
-    INSERT INTO user VALUES (1, 'test01', 'mypass', 't01@na.com', 'Joe',  'Blow', 1);
-    INSERT INTO user VALUES (2, 'test02', 'mypass', 't02@na.com', 'Jane', 'Doe',  1);
-    INSERT INTO user VALUES (3, 'test03', 'mypass', 't03@na.com', 'No',   'Go',   0);
+    INSERT INTO users VALUES (1, 'test01', 'mypass', 't01@na.com', 'Joe',  'Blow', 1);
+    INSERT INTO users VALUES (2, 'test02', 'mypass', 't02@na.com', 'Jane', 'Doe',  1);
+    INSERT INTO users VALUES (3, 'test03', 'mypass', 't03@na.com', 'No',   'Go',   0);
     INSERT INTO role VALUES (1, 'user');
     INSERT INTO role VALUES (2, 'admin');
     INSERT INTO user_role VALUES (1, 1);
@@ -146,19 +146,19 @@ files, those changes would have only been written above the C<# DO NOT
 MODIFY THIS OR ANYTHING ABOVE!> comment and your hand-edited
 enhancements would have been preserved.
 
-Speaking of "hand-editted enhancements," we should now add the 
-C<many_to_many> relationship information to the User Result Source file. 
-As with the Book, BookAuthor, and Author files in 
-L<Chapter 3|Catalyst::Manual::Tutorial::03_MoreCatalystBasics>, 
-L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> has 
-automatically created the C<has_many> and C<belongs_to> relationships 
-for the new User, UserRole, and Role tables. However, as a convenience 
-for mapping Users to their assigned roles (see 
-L<Chapter 6|Catalyst::Manual::Tutorial::06_Authorization>), we will 
-also manually add a C<many_to_many> relationship. Edit 
-C<lib/MyApp/Schema/Result/User.pm> add the following information between 
-the C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment and the closing 
-C<1;>: 
+Speaking of "hand-editted enhancements," we should now add the
+C<many_to_many> relationship information to the User Result Source file.
+As with the Book, BookAuthor, and Author files in
+L<Chapter 3|Catalyst::Manual::Tutorial::03_MoreCatalystBasics>,
+L<DBIx::Class::Schema::Loader|DBIx::Class::Schema::Loader> has
+automatically created the C<has_many> and C<belongs_to> relationships
+for the new User, UserRole, and Role tables. However, as a convenience
+for mapping Users to their assigned roles (see
+L<Chapter 6|Catalyst::Manual::Tutorial::06_Authorization>), we will
+also manually add a C<many_to_many> relationship. Edit
+C<lib/MyApp/Schema/Result/User.pm> add the following information between
+the C<# DO NOT MODIFY THIS OR ANYTHING ABOVE!> comment and the closing
+C<1;>:
 
     # many_to_many():
     #   args:
@@ -168,12 +168,12 @@ C<1;>:
     #   You must already have the has_many() defined to use a many_to_many().
     __PACKAGE__->many_to_many(roles => 'user_roles', 'role');
 
-The code for this update is obviously very similar to the edits we made 
-to the C<Book> and C<Author> classes created in Chapter 3 with one 
-exception: we only defined the C<many_to_many> relationship in one 
-direction. Whereas we felt that we would want to map Authors to Books 
-B<AND> Books to Authors, here we are only adding the convenience 
-C<many_to_many> in the Users to Roles direction. 
+The code for this update is obviously very similar to the edits we made
+to the C<Book> and C<Author> classes created in Chapter 3 with one
+exception: we only defined the C<many_to_many> relationship in one
+direction. Whereas we felt that we would want to map Authors to Books
+B<AND> Books to Authors, here we are only adding the convenience
+C<many_to_many> in the Users to Roles direction.
 
 Note that we do not need to make any change to the
 C<lib/MyApp/Schema.pm> schema file.  It simply tells DBIC to load all
@@ -184,13 +184,13 @@ new table information.
 
 =head2 Sanity-Check of the Development Server Reload
 
-We aren't ready to try out the authentication just yet; we only want to 
-do a quick check to be sure our model loads correctly. Assuming that you 
-are following along and using the "-r" option on C<myapp_server.pl>, 
-then the development server should automatically reload (if not, press 
-C<Ctrl-C> to break out of the server if it's running and then enter 
-C<script/myapp_server.pl> to start it). Look for the three new model 
-objects in the startup debug output: 
+We aren't ready to try out the authentication just yet; we only want to
+do a quick check to be sure our model loads correctly. Assuming that you
+are following along and using the "-r" option on C<myapp_server.pl>,
+then the development server should automatically reload (if not, press
+C<Ctrl-C> to break out of the server if it's running and then enter
+C<script/myapp_server.pl> to start it). Look for the three new model
+objects in the startup debug output:
 
     ...
      .-------------------------------------------------------------------+----------.
@@ -223,11 +223,11 @@ C<StackTrace> is new):
         -Debug
         ConfigLoader
         Static::Simple
-    
+
         StackTrace
-    
+
         Authentication
-    
+
         Session
         Session::Store::FastMmap
         Session::State::Cookie
@@ -273,9 +273,9 @@ backed session store).
 
 There are a variety of ways to provide configuration information to
 L<Catalyst::Plugin::Authentication|Catalyst::Plugin::Authentication>.
-Here we will use 
+Here we will use
 L<Catalyst::Authentication::Realm::SimpleDB|Catalyst::Authentication::Realm::SimpleDB>
-because it automatically sets a reasonable set of defaults for us. Open 
+because it automatically sets a reasonable set of defaults for us. Open
 C<lib/MyApp.pm> and place the following text above the call to
 C<__PACKAGE__-E<gt>setup();>:
 
@@ -288,14 +288,14 @@ C<__PACKAGE__-E<gt>setup();>:
             },
         };
 
-We could have placed this configuration in C<myapp.conf>, but placing 
-it in C<lib/MyApp.pm> is probably a better place since it's not likely 
-something that users of your application will want to change during 
-deployment (or you could use a mixture: leave C<class> and 
-C<user_model> defined in C<lib/MyApp.pm> as we show above, but place 
-C<password_type> in C<myapp.conf> to allow the type of password to be 
-easily modified during deployment).  We will stick with putting 
-all of the authentication-related configuration in C<lib/MyApp.pm> 
+We could have placed this configuration in C<myapp.conf>, but placing
+it in C<lib/MyApp.pm> is probably a better place since it's not likely
+something that users of your application will want to change during
+deployment (or you could use a mixture: leave C<class> and
+C<user_model> defined in C<lib/MyApp.pm> as we show above, but place
+C<password_type> in C<myapp.conf> to allow the type of password to be
+easily modified during deployment).  We will stick with putting
+all of the authentication-related configuration in C<lib/MyApp.pm>
 for the tutorial, but if you wish to use C<myapp.conf>, just convert
 to the following code:
 
@@ -307,23 +307,23 @@ to the following code:
         </default>
     </Plugin::Authentication>
 
-B<TIP:> Here is a short script that will dump the contents of 
+B<TIP:> Here is a short script that will dump the contents of
 C<MyApp->config> to L<Config::General|Config::General> format in
 C<myapp.conf>:
 
-    $ CATALYST_DEBUG=0 perl -Ilib -e 'use MyApp; use Config::General; 
+    $ CATALYST_DEBUG=0 perl -Ilib -e 'use MyApp; use Config::General;
         Config::General->new->save_file("myapp.conf", MyApp->config);'
 
 B<HOWEVER>, if you try out the command above, be sure to delete the
 "myapp.conf" command.  Otherwise, you will wind up with duplicate
 configurations.
 
-B<NOTE:> Because we are using SimpleDB along with a database layout 
+B<NOTE:> Because we are using SimpleDB along with a database layout
 that complies with its default assumptions: we don't need to specify
 the names of the columns where our username and password information
 is stored (hence, the "Simple" part of "SimpleDB").  That being said,
 SimpleDB lets you specify that type of information if you need to.
-Take a look at 
+Take a look at
 C<Catalyst::Authentication::Realm::SimpleDB|Catalyst::Authentication::Realm::SimpleDB>
 for details.
 
@@ -347,18 +347,18 @@ inserted by the helpers when we created the Login controller above,
 and update the definition of C<sub index> to match:
 
     =head2 index
-    
+
     Login logic
-    
+
     =cut
-    
+
     sub index :Path :Args(0) {
         my ($self, $c) = @_;
-    
+
         # Get the username and password from form
         my $username = $c->request->params->{username};
         my $password = $c->request->params->{password};
-    
+
         # If the username and password values were found in form
         if ($username && $password) {
             # Attempt to log the user in
@@ -376,12 +376,12 @@ and update the definition of C<sub index> to match:
             # Set an error message
             $c->stash(error_msg => "Empty username or password.");
         }
-    
+
         # If either of above don't work out, send to the login page
         $c->stash(template => 'login.tt2');
     }
 
-Be sure to remove the 
+Be sure to remove the
 C<$c-E<gt>response-E<gt>body('Matched MyApp::Controller::Login in Login.');>
 line of the C<sub index>.
 
@@ -413,17 +413,17 @@ Next, update the corresponding method in
 C<lib/MyApp/Controller/Logout.pm> to match:
 
     =head2 index
-    
+
     Logout logic
-    
+
     =cut
-    
+
     sub index :Path :Args(0) {
         my ($self, $c) = @_;
-    
+
         # Clear the user's state
         $c->logout;
-    
+
         # Send the user to the starting point
         $c->response->redirect($c->uri_for('/'));
     }
@@ -438,7 +438,7 @@ line of the C<sub index>.
 Create a login form by opening C<root/src/login.tt2> and inserting:
 
     [% META title = 'Login' %]
-    
+
     <!-- Login form -->
     <form method="post" action="[% c.uri_for('/login') %]">
       <table>
@@ -462,24 +462,24 @@ Create a login form by opening C<root/src/login.tt2> and inserting:
 We need something that provides enforcement for the authentication
 mechanism -- a I<global> mechanism that prevents users who have not
 passed authentication from reaching any pages except the login page.
-This is generally done via an C<auto> action/method in 
+This is generally done via an C<auto> action/method in
 C<lib/MyApp/Controller/Root.pm>.
 
 Edit the existing C<lib/MyApp/Controller/Root.pm> class file and insert
 the following method:
 
     =head2 auto
-    
+
     Check if there is a user and, if not, forward to login page
-    
+
     =cut
-    
+
     # Note that 'auto' runs after 'begin' but before your actions and that
     # 'auto's "chain" (all from application path to most specific class are run)
     # See the 'Actions' section of 'Catalyst::Manual::Intro' for more info.
     sub auto :Private {
         my ($self, $c) = @_;
-    
+
         # Allow unauthenticated users to reach the login page.  This
         # allows unauthenticated users to reach any action in the Login
         # controller.  To lock it down to a single action, we could use:
@@ -489,7 +489,7 @@ the following method:
         if ($c->controller eq $c->controller('Login')) {
             return 1;
         }
-    
+
         # If a user doesn't exist, force login
         if (!$c->user_exists) {
             # Dump a log message to the development server debug output
@@ -499,7 +499,7 @@ the following method:
             # Return 0 to cancel 'post-auto' processing and prevent use of application
             return 0;
         }
-    
+
         # User found, so return 1 to continue with processing after this 'auto'
         return 1;
     }
@@ -551,15 +551,15 @@ use of an IF-THEN-ELSE construct in TT).
 
 =head2 Try Out Authentication
 
-The development server should have reloaded each time we edited one of 
-the Controllers in the previous section. Now trying going to 
-L<http://localhost:3000/books/list> and you should be redirected to the 
-login page, hitting Shift+Reload or Ctrl+Reload if necessary (the "You 
-are already logged in" message should I<not> appear -- if it does, click 
-the C<logout> button and try again). Note the C<***Root::auto User not 
-found...> debug message in the development server output. Enter username 
-C<test01> and password C<mypass>, and you should be taken to the Book 
-List page. 
+The development server should have reloaded each time we edited one of
+the Controllers in the previous section. Now trying going to
+L<http://localhost:3000/books/list> and you should be redirected to the
+login page, hitting Shift+Reload or Ctrl+Reload if necessary (the "You
+are already logged in" message should I<not> appear -- if it does, click
+the C<logout> button and try again). Note the C<***Root::auto User not
+found...> debug message in the development server output. Enter username
+C<test01> and password C<mypass>, and you should be taken to the Book
+List page.
 
 B<IMPORTANT NOTE:> If you are having issues with authentication on
 Internet Explorer, be sure to check the system clocks on both your
@@ -604,8 +604,8 @@ need to log in to use this application."
 
 =head1 USING PASSWORD HASHES
 
-In this section we increase the security of our system by converting 
-from cleartext passwords to SHA-1 password hashes that include a 
+In this section we increase the security of our system by converting
+from cleartext passwords to SHA-1 password hashes that include a
 random "salt" value to make them extremely difficult to crack with
 dictionary and "rainbow table" attacks.
 
@@ -622,9 +622,9 @@ easy with the Catalyst plugin Catalyst::Plugin:RequireSSL.
 
 =head2 Re-Run the DBIC::Schema Model Helper to Include DBIx::Class::EncodedColumn
 
-Next, we can re-run the model helper to have it include 
-L<DBIx::Class::EncodedColumn|DBIx::Class::EncodedColumn> in all of the 
-Result Classes it generates for us.  Simply use the same command we 
+Next, we can re-run the model helper to have it include
+L<DBIx::Class::EncodedColumn|DBIx::Class::EncodedColumn> in all of the
+Result Classes it generates for us.  Simply use the same command we
 saw in Chapters 3 and 4, but add C<,EncodedColumn> to the C<components>
 argument:
 
@@ -632,8 +632,8 @@ argument:
         create=static components=TimeStamp,EncodedColumn dbi:SQLite:myapp.db \
         on_connect_do="PRAGMA foreign_keys = ON"
 
-If you then open one of the Result Classes, you will see that it 
-includes EncodedColumn in the C<load_components> line.  Take a look at 
+If you then open one of the Result Classes, you will see that it
+includes EncodedColumn in the C<load_components> line.  Take a look at
 C<lib/MyApp/Schema/Result/User.pm> since that's the main class where we
 want to use hashed and salted passwords:
 
@@ -657,22 +657,22 @@ the closing "1;":
         },
     );
 
-This redefines the automatically generated definition for the password 
-fields at the top of the Result Class file to now use EncodedColumn 
-logic (C<encoded_column> is set to 1).  C<encode_class> can be set to 
-either C<Digest> to use 
-L<DBIx::Class::EncodedColumn::Digest|DBIx::Class::EncodedColumn::Digest>, 
-or C<Crypt::Eksblowfish::Bcrypt> for 
+This redefines the automatically generated definition for the password
+fields at the top of the Result Class file to now use EncodedColumn
+logic (C<encoded_column> is set to 1).  C<encode_class> can be set to
+either C<Digest> to use
+L<DBIx::Class::EncodedColumn::Digest|DBIx::Class::EncodedColumn::Digest>,
+or C<Crypt::Eksblowfish::Bcrypt> for
 L<DBIx::Class::EncodedColumn::Crypt::Eksblowfish::Bcrypt|DBIx::Class::EncodedColumn::Crypt::Eksblowfish::Bcrypt>.
-C<encode_args> is then used to customize the type of Digest you 
+C<encode_args> is then used to customize the type of Digest you
 selected. Here we only specified the size of the salt to use, but
-we could have also modified the hashing algorithm ('SHA-256' is 
+we could have also modified the hashing algorithm ('SHA-256' is
 the default) and the format to use ('base64' is the default, but
-'hex' and 'binary' are other options).  To use these, you could 
+'hex' and 'binary' are other options).  To use these, you could
 change the C<encode_args> to something like:
 
-            encode_args         => {algorithm => 'SHA-1', 
-                                    format => 'hex', 
+            encode_args         => {algorithm => 'SHA-1',
+                                    format => 'hex',
                                     salt_length => 10},
 
 
@@ -683,63 +683,63 @@ into the C<password> column of our C<users> table.  Open the file
 C<set_hashed_passwords.pl> in your editor and enter the following text:
 
     #!/usr/bin/perl
-    
+
     use strict;
     use warnings;
-    
+
     use MyApp::Schema;
-    
+
     my $schema = MyApp::Schema->connect('dbi:SQLite:myapp.db');
-    
+
     my @users = $schema->resultset('User')->all;
-    
+
     foreach my $user (@users) {
         $user->password('mypass');
         $user->update;
     }
 
-EncodedColumn lets us simple call C<$user->check_password($password)> 
-to see if the user has supplied the correct password, or, as we show 
-above, call C<$user->update($new_password)> to update the hashed 
+EncodedColumn lets us simple call C<$user->check_password($password)>
+to see if the user has supplied the correct password, or, as we show
+above, call C<$user->update($new_password)> to update the hashed
 password stored for this user.
 
 Then run the following command:
 
     $ DBIC_TRACE=1 perl -Ilib set_hashed_passwords.pl
 
-We had to use the C<-Ilib> argument to tell perl to look under the 
+We had to use the C<-Ilib> argument to tell perl to look under the
 C<lib> directory for our C<MyApp::Schema> model.
 
 The DBIC_TRACE output should show that the update worked:
 
     $ DBIC_TRACE=1 perl -Ilib set_hashed_passwords.pl
-    SELECT me.id, me.username, me.password, me.email_address, 
-    me.first_name, me.last_name, me.active FROM user me: 
-    UPDATE user SET password = ? WHERE ( id = ? ): 
+    SELECT me.id, me.username, me.password, me.email_address,
+    me.first_name, me.last_name, me.active FROM users me:
+    UPDATE users SET password = ? WHERE ( id = ? ):
     'oXiyAcGOjowz7ISUhpIm1IrS8AxSZ9r4jNjpX9VnVeQmN6GRtRKTz', '1'
-    UPDATE user SET password = ? WHERE ( id = ? ): 
+    UPDATE users SET password = ? WHERE ( id = ? ):
     'PmyEPrkB8EGwvaF/DvJm7LIfxoZARjv8ygFIR7pc1gEA1OfwHGNzs', '2'
-    UPDATE user SET password = ? WHERE ( id = ? ): 
+    UPDATE users SET password = ? WHERE ( id = ? ):
     'h7CS1Fm9UCs4hjcbu2im0HumaHCJUq4Uriac+SQgdUMUfFSoOrz3c', '3'
 
 But we can further confirm our actions by dumping the users table:
 
-    $ sqlite3 myapp.db "select * from user"
+    $ sqlite3 myapp.db "select * from users"
     1|test01|38d3974fa9e9263099f7bc2574284b2f55473a9bM=fwpX2NR8|t01@na.com|Joe|Blow|1
     2|test02|6ed8586587e53e0d7509b1cfed5df08feadc68cbMJlnPyPt0I|t02@na.com|Jane|Doe|1
     3|test03|af929a151340c6aed4d54d7e2651795d1ad2e2f7UW8dHoGv9z|t03@na.com|No|Go|0
 
-As you can see, the passwords are much harder to steal from the 
-database (not only are the hashes stored, but every hash is different 
-even though the passwords are the same because of the added "salt" 
-value).  Also note that this demonstrates how to use a DBIx::Class 
-model outside of your web application -- a very useful feature in many 
+As you can see, the passwords are much harder to steal from the
+database (not only are the hashes stored, but every hash is different
+even though the passwords are the same because of the added "salt"
+value).  Also note that this demonstrates how to use a DBIx::Class
+model outside of your web application -- a very useful feature in many
 situations.
 
 
 =head2 Enable Hashed and Salted Passwords
 
-Edit C<lib/MyApp.pm> and update it to match the following text (the 
+Edit C<lib/MyApp.pm> and update it to match the following text (the
 only change is to the C<password_type> field):
 
     # Configure SimpleDB Authentication
@@ -751,30 +751,30 @@ only change is to the C<password_type> field):
             },
         };
 
-The use of C<self_check> will cause 
-Catalyst::Plugin::Authentication::Store::DBIC to call the 
+The use of C<self_check> will cause
+Catalyst::Plugin::Authentication::Store::DBIC to call the
 C<check_password> method we enabled on our C<password> columns.
 
 
 =head2 Try Out the Hashed Passwords
 
-The development server should restart as soon as your save the 
-C<lib/MyApp.pm> file in the previous section. You should now be able to 
-go to L<http://localhost:3000/books/list> and login as before. When 
-done, click the "logout" link on the login page (or point your browser 
-at L<http://localhost:3000/logout>). 
+The development server should restart as soon as your save the
+C<lib/MyApp.pm> file in the previous section. You should now be able to
+go to L<http://localhost:3000/books/list> and login as before. When
+done, click the "logout" link on the login page (or point your browser
+at L<http://localhost:3000/logout>).
 
 
 =head1 USING THE SESSION FOR FLASH
 
-As discussed in the previous chapter of the tutorial, C<flash> allows 
-you to set variables in a way that is very similar to C<stash>, but it 
-will remain set across multiple requests.  Once the value is read, it 
-is cleared (unless reset).  Although C<flash> has nothing to do with 
-authentication, it does leverage the same session plugins.  Now that 
-those plugins are enabled, let's go back and update the "delete and 
-redirect with query parameters" code seen at the end of the L<Basic 
-CRUD|Catalyst::Manual::Tutorial::04_BasicCRUD> chapter of the tutorial to 
+As discussed in the previous chapter of the tutorial, C<flash> allows
+you to set variables in a way that is very similar to C<stash>, but it
+will remain set across multiple requests.  Once the value is read, it
+is cleared (unless reset).  Although C<flash> has nothing to do with
+authentication, it does leverage the same session plugins.  Now that
+those plugins are enabled, let's go back and update the "delete and
+redirect with query parameters" code seen at the end of the L<Basic
+CRUD|Catalyst::Manual::Tutorial::04_BasicCRUD> chapter of the tutorial to
 take advantage of C<flash>.
 
 First, open C<lib/MyApp/Controller/Books.pm> and modify C<sub delete>
@@ -782,21 +782,21 @@ to match the following (everything after the model search line of code
 has changed):
 
     =head2 delete
-    
+
     Delete a book
-    
+
     =cut
-    
+
     sub delete :Chained('object') :PathPart('delete') :Args(0) {
         my ($self, $c) = @_;
-    
+
         # Use the book object saved by 'object' and delete it along
         # with related 'book_authors' entries
         $c->stash->{object}->delete;
-    
+
         # Use 'flash' to save information across requests until it's read
         $c->flash->{status_msg} = "Book deleted";
-    
+
         # Redirect the user back to the list page
         $c->response->redirect($c->uri_for($self->action_for('list')));
     }
@@ -815,8 +815,8 @@ flash vs. the C<status_msg> query parameter:
     ...
 
 Although the sample above only shows the C<content> div, leave the
-rest of the file intact -- the only change we made to replace 
-"|| c.request.params.status_msg" with "c.flash.status_msg" in the 
+rest of the file intact -- the only change we made to replace
+"|| c.request.params.status_msg" with "c.flash.status_msg" in the
 C<< <span class="message"> >> line.
 
 
@@ -870,10 +870,10 @@ to match the following:
 
     <span class="message">[% status_msg %]</span>
 
-Now go to L<http://localhost:3000/books/list> in your browser. Delete 
-another of the "Test" books you added in the previous step. Flash should 
-still maintain the status message across the redirect even though you 
-are no longer explicitly accessing C<c.flash>. 
+Now go to L<http://localhost:3000/books/list> in your browser. Delete
+another of the "Test" books you added in the previous step. Flash should
+still maintain the status message across the redirect even though you
+are no longer explicitly accessing C<c.flash>.
 
 
 =head1 AUTHOR
@@ -884,5 +884,5 @@ Please report any errors, issues or suggestions to the author.  The
 most recent version of the Catalyst Tutorial can be found at
 L<http://dev.catalyst.perl.org/repos/Catalyst/Catalyst-Manual/5.80/trunk/lib/Catalyst/Manual/Tutorial/>.
 
-Copyright 2006-2008, Kennedy Clark, under Creative Commons License
+Copyright 2006-2010, Kennedy Clark, under Creative Commons License
 (L<http://creativecommons.org/licenses/by-sa/3.0/us/>).