Embedding Perl in HTML with Mason Chapter 8: Building a Mason Site-P4

Chia sẻ: Thanh Cong | Ngày: | Loại File: PDF | Số trang:54

lượt xem

Embedding Perl in HTML with Mason Chapter 8: Building a Mason Site-P4

Mô tả tài liệu
  Download Vui lòng tải xuống để xem tài liệu đầy đủ

Tham khảo tài liệu 'embedding perl in html with mason chapter 8: building a mason site-p4', công nghệ thông tin, kỹ thuật lập trình phục vụ nhu cầu học tập, nghiên cứu và làm việc hiệu quả

Chủ đề:

Nội dung Text: Embedding Perl in HTML with Mason Chapter 8: Building a Mason Site-P4

  1. Chapter 8: Building a Mason Site-P4 . users/user_form.mas This form is used for both creating new users and editing existing ones. To prepopulate the form fields, it first looks at the %ARGS hash. If there are values for these fields here, it assumes that these have priority because the only way for %ARGS to have such values is if the form was submitted but then rejected for a data validation error, in which case the browser is redirected back to the submitting page. When that happens, we want to show the user the rejected values that were just entered into the form. If there is nothing in %ARGS, then we look at the $user object for these values. Unless the user for whom this page is being generated is an admin user, we don't bother showing the checkbox that allows them to turn on the admin flag for a user since that checkbox is respected only when a site administrator submits the form. The $submit_to variable is used to set the form's action attribute. This allows us to use this form for both creating new users and editing existing ones. The $return_to value is simply passed through the form to the component that handles the form submission, which will use it to determine where to send the browser if the form submission is successful. % foreach my $err (@errors) {
  2. % } % if ($user->user_id) { % } Username: Password:
  3. Confirm password: Real name:
  4. Email address: How available are you? % while (my $status = $user_statuses->next) { > status | h %> % }
  5. % if ($User->is_admin) { Site admin: % } $submit_to $return_to => '/' $user @errors => ( )
  6. my $user_statuses = $Schema->UserStatus_t->all_rows ( order_by => $Schema->UserStatus_t- >status_c ); my %form_vals; foreach my $field ( qw( username password real_name email_address user_status_id is_admin ) ) { $form_vals{$field} = exists $ARGS{$field} ? $ARGS{$field} : $user->$field( ); } $ form_vals{password2} = exists $ARGS{password2} ? $ARGS{password2} : exists $ARGS{password} ? $ARGS{password} : $user->password;
  7. • /users/new_user_submit.html Because data validation is handled by our module code, this component doesn't have much to do. If the insert succeeds, we set the cookie used to indicate a successful login and redirect the client to whatever path is in the $return_to variable. Note that we will never set the is_admin flag to true unless the submitting user is a site administrator. One style point: this component calls a few other components, but it uses $m->comp() instead of tags to do so. This is partly just because it was convenient to call the components from within the section, but it also emphasizes the fact that those particular components don't generate any HTML output. $return_to # When inserting a new row, data validation checks are performed and an # exception is thrown if any of the checks fail. my $user = eval { $Schema->User_t->insert
  8. ( values => { ( map { $_ => $ARGS{$_} } qw( username password password2 real_name email_address user_status_id ) ), is_admin => $User- >is_admin ? $ARGS{is_admin} : 0, } ); }; # One or more data validation checks failed $m->comp( '/lib/redirect.mas', path => 'new_user.html', query => { %ARGS, errors => $@->errors } ) if $@ && UNIVERSAL::isa( $@, 'Apprentice::Exception::DataValidation' ); # Some other unforeseen error happened die $@ if $@;
  9. $m->comp( '/lib/set_login_cookie.mas', user => $user ); $m->comp( '/lib/redirect.mas', path => $return_to ); inherit => '/syshandler' • /lib/redirect.mas With Mason's built-in redirect() method, this component is trivially simple. We use the scomp() method to get a URL in the form of a string from the /lib/url.mas component, then pass that to the redirect() method, which will generate the proper headers and send them to the client. my $url = $m->scomp( '/lib/url.mas', %ARGS ); $m->redirect($url); • /users/login_submit.html
  10. This component is the target for the login form we saw back in /left_side_menu.mas, as well as /login_form.html page. We check the given username to make sure it exists and that the password given matches the password in the database. If this is not the case, we simply redirect the user back to the calling page with an error. Otherwise, we set the cookie that marks a successful login and issue a redirect to the URL specified in $success_url. This is a common pattern in web applications. You have a URL that handles form submissions that needs to redirect the browser to a different page, so you make the submission-receiving component capable of taking a parameter indicating where to redirect the client. $username $password $caller_url %caller_args => ( ) $success_url => undef %success_args => ( ) my $user = $Schema->User_t->one_row
  11. ( where => [ $Schema->User_t- >username_c, '=', $username ] ); unless ( $user && $password eq $user- >password ) { $m->comp( '/lib/redirect.mas', path => $caller_url, query => { caller_args => \%caller_args, username => $username, login_error => 'Invalid login.' }, ); } $m->comp( '/lib/set_login_cookie.mas', user => $user ); # By default, we just send them back to the calling page. $success_url = $caller_url unless defined $success_url && length $success_url;
  12. %success_args = %caller_args unless %success_args; $m->comp( '/lib/redirect.mas', path => $success_url, query => \%success_args ); inherit => '/syshandler' • /lib/set_login_cookie.mas We discussed using a MAC for authentication in our explanation of the /syshandler component. This is the flip side of that process. Here we simply set a cookie containing the user's user ID and a MAC based on that user ID. A component that affects the headers sent to the client, such as this one, must be called before headers are sent. Since this site runs with autoflushing turned off, this is not a problem, because headers won't be sent until after all the content is generated. $user Apache::Cookie->new
  13. ( $r, -name => 'apprentice_user_login', -value => { user_id => $user->user_id, MAC => Digest::SHA1::sha1_hex ( $user->user_id, $Apprentice::Secret ) }, -path => '/', -domain => 'apprentice.perl.org', -expires => '+1M', )->bake; • /users/logout.html Here we remove the login cookie set by the /lib/set_login_cookie.mas component by setting a cookie with an expiration date in the past, which removes the cookie from the browser. $caller_url %caller_args => ( ) Apache::Cookie->new
  14. ( $r, -name => 'apprentice_user_login', -value => '', -path => '/', -domain => 'apprentice.perl.org', -expires => '-1d', )->bake; $m->comp( '/lib/redirect.mas', path => $caller_url, query => \%caller_args ); inherit => '/syshandler' • /users/forgot_password.html This is a simple form for users who forgot their password. A user enters her username, and the system sends her an email. • /users/forgot_password_submit.html This component does the actual sending of email for forgotten passwords. Assuming that there is a username matching that entered by the user, we generate a simple email telling her her password. We use the $r->register_cleanup() method to delay sending email until after output has been sent to the client. This technique is
  15. useful for any sort of operation that might take a long time, but the downside is that if the callback fails, there is no easy way to communicate this to the user. If this is a problem, you will simply have to do this while the client waits for output. The $r->register_cleanup() method is documented in the Apache module documentation as well as the books mentioned in the beginning of Chapter 7. $username my $user = $Schema->User_t->one_row ( where => [ $Schema->User_t- >username_c, '=', $username ] ); unless ( $user ) { $m->comp( '/lib/redirect.mas', path => 'forgot_password.html', query => { error => 'Invalid username.' } ); }
  16. my $body = "Your password is:\n\n" . $user- >password . "\n\nwebmaster\@apprentice.perl.org"; $r->register_cleanup ( sub { Apprentice::send_email ( to => $user- >email_address, from => 'webmaster@apprentice.perl.org', subject => 'Your password for apprentice.perl.org', body => $body ) } ); $m->comp( '/lib/redirect.mas', path => '/index.html', query => { login_error => 'Your password has been mailed to you.' } ); inherit => '/syshandler'
  17. Components with Access Controls The components we just looked at are available to anybody who comes to the site, with no login required. The rest of the components are divided into two directories: one for logged-in users and the other for site administrators. We will start with the components available for logged-in users only. They are: • /logged_in/autohandler • /lib/check_access_level.mas • /logged_in/edit_self.html • /logged_in/edit_user_submit.html • /logged_in/new_project.html • /logged_in/project_form.mas • /logged_in/new_project_submit.html • /logged_in/editable_project_list.html • /logged_in/edit_project.html • /logged_in/check_access_to_project.mas • /logged_in/edit_project_submit.html • /logged_in/edit_members.html • /logged_in/add_project_member.html • /logged_in/remove_project_member.html • /logged_in/delete_project.html
  18. These components are all about editing things on the site. Let's take a look. • /logged_in/autohandler All this component does is implement access checking for the directory. If you are not a logged-in user, you cannot look at any components in this directory. $m->comp( '/lib/check_access_level.mas', level => 'is_logged_in' ); $m->call_next; • /lib/check_access_level.mas This component simply redirects the user to the login form if he does not meet the access-level requirement. If the user logs in successfully, he'll be redirected back to the component he was originally prevented from accessing. $level my $requested_url = $r->uri; my %query_args = $m->request_args;
  19. my $level_description = $level eq 'is_logged_in' ? 'a logged-in' : 'an admin'; $m->comp( '/lib/redirect.mas', path => '/login_form.html', query => { message => "This area requires $level_description user.", success_url => $requested_url, success_args => \%query_args, } ) unless $User->$level( ); • /logged_in/edit_self.html Editing a user simply uses the handy /users/user_form.mas component we saw previously, this time with a different action attribute for the form, set via the submit_to parameter. It doesn't get any easier than that.
  20. Edit Your Account 'edit_user_submit.html', return_to => $r->uri, user => $User, %ARGS &> - Edit your account • /logged_in/edit_user_submit.html This component implements an additional access check. We want to make sure that the user submitting this form is either a site administrator or the owner of the account being edited. Otherwise, we simply send her away. As with creating a new user, we always set the is_admin flag to a false value unless the submitting user is a site administrator.
Đồng bộ tài khoản