You've already forked explain.depesz.com
							
							
				mirror of
				https://gitlab.com/depesz/explain.depesz.com.git
				synced 2025-10-31 00:07:57 +02:00 
			
		
		
		
	WIP - users support
- users can now:
    - register
    - login
    - logout
    - change password
- new explains know about logged user (it's stored in database)
			
			
This commit is contained in:
		| @@ -5,6 +5,9 @@ use Mojo::Base 'Mojolicious'; | ||||
| sub startup { | ||||
|     my $self = shift; | ||||
|  | ||||
|     $self->sessions->cookie_name('explain'); | ||||
|     $self->sessions->default_expiration( 60 * 60 * 24 * 365 ); | ||||
|  | ||||
|     # register Explain plugins namespace | ||||
|     $self->plugins->namespaces( [ "Explain::Plugin", @{ $self->plugins->namespaces } ] ); | ||||
|  | ||||
| @@ -29,6 +32,15 @@ sub startup { | ||||
|     # route: 'index' | ||||
|     $routes->route( '/' )->to( 'controller#index' )->name( 'new-explain' ); | ||||
|  | ||||
|     # route: 'login' | ||||
|     $routes->route( '/login' )->to( 'controller#login' )->name( 'login' ); | ||||
|  | ||||
|     # route: 'logout' | ||||
|     $routes->route( '/logout' )->to( 'controller#logout' )->name( 'logout' ); | ||||
|  | ||||
|     # route: 'user' | ||||
|     $routes->route( '/user' )->to( 'controller#user' )->name( 'user' ); | ||||
|  | ||||
|     # route: 'show' | ||||
|     $routes->route( '/s/:id' )->to( 'controller#show', id => '' )->name( 'show' ); | ||||
|  | ||||
|   | ||||
| @@ -8,6 +8,86 @@ use Pg::Explain; | ||||
| use Encode; | ||||
| use Email::Valid; | ||||
|  | ||||
| sub logout { | ||||
|     my $self = shift; | ||||
|     delete $self->session->{'user'}; | ||||
|     $self->redirect_to( 'new-explain' ); | ||||
| } | ||||
|  | ||||
| sub user { | ||||
|     my $self = shift; | ||||
|  | ||||
|     my $old = $self->req->param('old-pw'); | ||||
|     my $new = $self->req->param('new-pw'); | ||||
|     my $new2 = $self->req->param('new-pw2'); | ||||
|  | ||||
|     return $self->render unless defined $old; | ||||
|      | ||||
|     if ( | ||||
|         ( !defined $new ) || | ||||
|         ( !defined $new2) || | ||||
|         ( $new ne $new2 ) | ||||
|     ) { | ||||
|         $self->stash->{'message'} = 'You have to provide two identical copies of new password!'; | ||||
|         return; | ||||
|     } | ||||
|     my $status = $self->database->user_change_password( $self->session->{'user'}, $old, $new ); | ||||
|     if ( $status ) { | ||||
|         $self->flash('message' => 'Password changed.'); | ||||
|         $self->redirect_to( 'new-explain' ); | ||||
|     } | ||||
|     $self->stash->{'message'} = 'Changing the password failed.'; | ||||
| } | ||||
|  | ||||
| sub login { | ||||
|     my $self = shift; | ||||
|  | ||||
|     # If there is no username - there is nothing to do | ||||
|     my $username = $self->req->param('username'); | ||||
|     return $self->render unless defined $username; | ||||
|  | ||||
|     if ( 30 < length( $username ) ) { | ||||
|         $self->stash->{'message'} = 'Username cannot be longer than 30 characters. Really?!'; | ||||
|         return;         | ||||
|     } | ||||
|  | ||||
|     my $password = $self->req->param('password'); | ||||
|     my $password2 = $self->req->param('password2'); | ||||
|  | ||||
|     if ( ( ! defined $password ) || ( '' eq $password ) ) { | ||||
|         $self->stash->{'message'} = 'There has to be some password!'; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     # Registration | ||||
|     if ( $self->req->param('is_registration') ) { | ||||
|         if ( | ||||
|             ( ! defined $password2 ) || | ||||
|             ( $password2 ne $password ) | ||||
|         ) { | ||||
|             $self->stash->{'message'} = 'You have to repeat password correctly!'; | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         my $status = $self->database->user_register( $username, $password ); | ||||
|         if ( $status ) { | ||||
|             $self->flash('message' => 'User registered.'); | ||||
|             $self->session( 'user' => $username ); | ||||
|             $self->redirect_to( 'new-explain' ); | ||||
|         } | ||||
|         $self->stash->{'message'} = 'Registration failed.'; | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     if ( $self->database->user_login( $username, $password ) ) { | ||||
|         $self->flash('message' => 'User logged in.'); | ||||
|         $self->session( 'user' => $username ); | ||||
|         $self->redirect_to( 'new-explain' ); | ||||
|     } | ||||
|     $self->stash->{'message'} = 'Bad username or password.'; | ||||
|     return; | ||||
| } | ||||
|  | ||||
| sub index { | ||||
|     my $self = shift; | ||||
|  | ||||
| @@ -61,7 +141,7 @@ sub index { | ||||
|     } | ||||
|  | ||||
|     # save to database | ||||
|     my ( $id, $delete_key ) = $self->database->save_with_random_name( $title, $plan, $is_public, $is_anon, ); | ||||
|     my ( $id, $delete_key ) = $self->database->save_with_random_name( $title, $plan, $is_public, $is_anon, $self->session->{'user'} ); | ||||
|  | ||||
|     # redirect to /show/:id | ||||
|     $self->flash( delete_key => $delete_key ); | ||||
|   | ||||
| @@ -7,9 +7,11 @@ use Mojo::Util 'trim'; | ||||
| use Carp; | ||||
| use DBI; | ||||
| use Date::Simple; | ||||
| use English qw( -no_match_vars ); | ||||
|  | ||||
| has dbh => undef; | ||||
| has connection_args => sub { [] }; | ||||
| has log => undef; | ||||
|  | ||||
| sub register { | ||||
|     my ( $self, $app, $config ) = @_; | ||||
| @@ -45,6 +47,7 @@ sub register { | ||||
|  | ||||
|     # log debug message | ||||
|     $app->log->debug( 'Database connection args: ' . $app->dumper( $self->connection_args ) ); | ||||
|     $self->log( $app->log ); | ||||
|  | ||||
|     # register helper | ||||
|     $app->helper( | ||||
| @@ -67,14 +70,74 @@ sub register { | ||||
|     return; | ||||
| } | ||||
|  | ||||
| sub save_with_random_name { | ||||
| sub user_login { | ||||
|     my $self = shift; | ||||
|     my ( $title, $content, $is_public, $is_anon ) = @_; | ||||
|     my ( $username, $password ) = @_; | ||||
|  | ||||
|     my @row = $self->dbh->selectrow_array( | ||||
|         'SELECT id, delete_key FROM register_plan(?, ?, ?, ?)', | ||||
|         'SELECT password FROM users where username = ?', | ||||
|         undef, | ||||
|         $title, $content, $is_public, $is_anon, | ||||
|         $username, | ||||
|     ); | ||||
|     return if 0 == scalar @row; | ||||
|     my $crypted = crypt( $password, $row[0] ); | ||||
|      | ||||
|     return if $crypted ne $row[0]; | ||||
|     return 1; | ||||
| } | ||||
|  | ||||
| sub user_change_password { | ||||
|     my $self = shift; | ||||
|     my ($username, $old, $new) = @_; | ||||
|  | ||||
|     my @row = $self->dbh->selectrow_array( | ||||
|         'SELECT password FROM users where username = ?', | ||||
|         undef, | ||||
|         $username, | ||||
|     ); | ||||
|     return if 0 == scalar @row; | ||||
|     my $crypted_old = crypt( $old, $row[0] ); | ||||
|  | ||||
|     my $crypted_new = crypt( $new, $self->get_pw_salt() ); | ||||
|      | ||||
|     @row = $self->dbh->selectrow_array( | ||||
|         'UPDATE users SET password = ? WHERE ( username, password ) = ( ?, ? ) returning username', | ||||
|         undef, | ||||
|         $crypted_new, $username, $crypted_old, | ||||
|     ); | ||||
|     return 1 if 1 == scalar @row; | ||||
|     return; | ||||
| } | ||||
|  | ||||
| sub get_pw_salt { | ||||
|     my $self = shift; | ||||
|     my @salt_chars = ( 'a'..'z', 'A'..'Z', 0..9, '.', '/' ); | ||||
|     my $salt = sprintf '$6$%s$', join( '', map { $salt_chars[ rand @salt_chars ] } 1..16 ); | ||||
|     return $salt; | ||||
| } | ||||
|  | ||||
| sub user_register { | ||||
|     my $self = shift; | ||||
|     my ( $username, $password ) = @_; | ||||
|  | ||||
|     my $crypted = crypt( $password, $self->get_pw_salt() ); | ||||
|  | ||||
|     eval { | ||||
|         $self->dbh->do( 'INSERT INTO users (username, password, registered) values (?, ?, now())', undef, $username, $crypted, ); | ||||
|     }; | ||||
|     return 1 unless $EVAL_ERROR; | ||||
|     $self->log->error( "user_register( $username ) => " . $EVAL_ERROR ); | ||||
|     return; | ||||
| } | ||||
|  | ||||
| sub save_with_random_name { | ||||
|     my $self = shift; | ||||
|     my ( $title, $content, $is_public, $is_anon, $username ) = @_; | ||||
|  | ||||
|     my @row = $self->dbh->selectrow_array( | ||||
|         'SELECT id, delete_key FROM register_plan(?, ?, ?, ?, ?)', | ||||
|         undef, | ||||
|         $title, $content, $is_public, $is_anon, $username, | ||||
|     ); | ||||
|  | ||||
|     # return id and delete_key | ||||
|   | ||||
| @@ -123,8 +123,7 @@ nav a, nav span                         {height:35px;padding:0 20px;font-size:1. | ||||
| nav a:hover                             {text-decoration:underline} | ||||
| nav span                                {margin-left:1px;margin-right:1px;background-position:0px -120px;color:#eee} | ||||
|  | ||||
| nav .donate                             {left:auto;right:20px} | ||||
| nav .donate span input                  {position:relative;top:2px;border:none;width:74px} | ||||
| nav .right                              {left:auto;right:20px} | ||||
|  | ||||
| section { | ||||
|          margin-bottom:20px; | ||||
| @@ -185,10 +184,17 @@ form .fe-buttons button span        {display:block;padding:5px 25px;color:#123} | ||||
| form .fe-buttons button:hover       {background:#234;border-color:123} | ||||
| form .fe-buttons button:hover span  {color:#fff;cursor:pointer} | ||||
|  | ||||
| form .fe-buttons-single                    {padding:5px 0 0 0} | ||||
| form .fe-buttons-single button             {display:block;float:left;margin-right:5px;border:1px solid #d0d8d8;background:#e5ecf9} | ||||
| form .fe-buttons-single button span        {display:block;padding:5px 25px;color:#123} | ||||
| form .fe-buttons-single button:hover       {background:#234;border-color:123} | ||||
| form .fe-buttons-single button:hover span  {color:#fff;cursor:pointer} | ||||
| form p.nfo-required {font-size:0.8em;color:#888} | ||||
|  | ||||
| form sup.fe-required {font-size:1.2em;color:#800} | ||||
|  | ||||
| .password2 {display:none;} | ||||
|  | ||||
| div.explain-form            {margin-bottom:5px;text-align:center} | ||||
| div.explain-form a          {padding:0 1em 0.5em 1em;border:1px solid #ccc;background:#f8f8f8;color:#666;text-decoration:none;-moz-border-radius:0 0 4px 4px;border-radius:0 0 4px 4px} | ||||
| div.explain-form a span     {font-size:0.9em;position:relative;top:0.2em} | ||||
|   | ||||
| @@ -354,6 +354,22 @@ | ||||
|                 } | ||||
|             }); | ||||
|         } | ||||
|  | ||||
|         // login form support | ||||
|         var pass2 = $('.password2'); | ||||
|         var lr_button_text = $('#login-button span'); | ||||
|         var is_reg_check = $('#is_registration'); | ||||
|         is_reg_check.change(function(e) { | ||||
|             if (this.checked) { | ||||
|                 pass2.show(); | ||||
|                 lr_button_text.text( 'Register'); | ||||
|             } else { | ||||
|                 pass2.hide(); | ||||
|                 lr_button_text.text( 'Login'); | ||||
|             } | ||||
|         }); | ||||
|         is_reg_check.trigger( 'change' ); | ||||
|         // login form support | ||||
|     }); | ||||
|  | ||||
| } )( jQuery ); | ||||
|   | ||||
| @@ -5,3 +5,28 @@ CREATE TABLE users ( | ||||
| ); | ||||
|  | ||||
| ALTER TABLE plans add column added_by TEXT references users ( username ); | ||||
|  | ||||
| DROP FUNCTION register_plan(in_title text, in_plan text, in_is_public boolean, in_is_anonymized boolean); | ||||
| CREATE FUNCTION register_plan(in_title text, in_plan text, in_is_public boolean, in_is_anonymized boolean, in_username text) RETURNS register_plan_return | ||||
|     LANGUAGE plpgsql | ||||
|     AS $$ | ||||
| DECLARE | ||||
|     use_hash_length int4 := 2; | ||||
|     reply register_plan_return; | ||||
| BEGIN | ||||
|     reply.delete_key := get_random_string( 50 ); | ||||
|     LOOP | ||||
|         reply.id := get_random_string(use_hash_length); | ||||
|         BEGIN | ||||
|             INSERT INTO plans (id, title, plan, is_public, entered_on, is_anonymized, delete_key, added_by) values (reply.id, in_title, in_plan, in_is_public, now(), in_is_anonymized, reply.delete_key, in_username ); | ||||
|             RETURN reply; | ||||
|         EXCEPTION WHEN unique_violation THEN | ||||
|                 -- do nothing | ||||
|         END; | ||||
|         use_hash_length := use_hash_length + 1; | ||||
|         IF use_hash_length >= 30 THEN | ||||
|             raise exception 'Random string of length == 30 requested. something''s wrong.'; | ||||
|         END IF; | ||||
|     END LOOP; | ||||
| END; | ||||
| $$; | ||||
|   | ||||
							
								
								
									
										42
									
								
								templates/controller/login.html.ep
									
									
									
									
									
										Executable file
									
								
							
							
						
						
									
										42
									
								
								templates/controller/login.html.ep
									
									
									
									
									
										Executable file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| % layout 'default'; | ||||
|  | ||||
| % my $title = 'Login / Register'; | ||||
| % title $title; | ||||
|  | ||||
| <h1><%= $title =%></h1> | ||||
|  | ||||
| % if ( stash( 'message' ) ) { | ||||
|     <div class="message"> | ||||
|     <p class="message"><%= stash( 'message' ) =%></p> | ||||
|     </div> | ||||
| % } | ||||
|  | ||||
| % if ( flash( 'message' ) ) { | ||||
|     <div class="message"> | ||||
|     <p class="message"><%= flash( 'message' ) =%></p> | ||||
|     </div> | ||||
| % } | ||||
|  | ||||
| <form id="login" method="post" action="<%= url_for 'current' %>"> | ||||
|     <div class="fe fe-first fe_login"> | ||||
|         <label for="username">username:</label> | ||||
|         <input id="username" name="username" class="auto-hint" title="Enter your username" | ||||
|         <% if ( param('username') ) { %> | ||||
|         value="<%= param('username') %>" | ||||
|         <% } %> | ||||
|         /> | ||||
|         <label for="password">password:</label> | ||||
|         <input id="password" type="password" name="password" title="Enter your password"/> | ||||
|         <label class="password2" for="password2">repeat password:</label> | ||||
|         <input class="password2" id="password2" type="password" name="password2" title="Enter your password"/> | ||||
|  | ||||
|     </div> | ||||
|     <div class="fe fe_register"> | ||||
|         <label for="is_registration">I want to register new account</label> | ||||
|         <input type="checkbox" class="checkbox" id="is_registration" name="is_registration" value="1" <% if ( param('is_registration') ) { %>checked="1"<% } %>/> | ||||
|     </div> | ||||
|  | ||||
|     <div class="fe fe-last fe-buttons"> | ||||
|         <button type="submit" name="login" id="login-button"><span>Login</span></button> | ||||
|     </div> | ||||
| </form> | ||||
							
								
								
									
										40
									
								
								templates/controller/user.html.ep
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								templates/controller/user.html.ep
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,40 @@ | ||||
| % layout 'default'; | ||||
|  | ||||
| % my $title = 'User: ' . session( 'user' ); | ||||
| % title $title; | ||||
|  | ||||
| <h1><%= $title %></h1> | ||||
|  | ||||
| <h2>Change password:</h2> | ||||
|  | ||||
| % if ( stash( 'message' ) ) { | ||||
|     <div class="message"> | ||||
|     <p class="message"><%= stash( 'message' ) =%></p> | ||||
|     </div> | ||||
| % } | ||||
|  | ||||
| % if ( flash( 'message' ) ) { | ||||
|     <div class="message"> | ||||
|     <p class="message"><%= flash( 'message' ) =%></p> | ||||
|     </div> | ||||
| % } | ||||
|  | ||||
| <form id="logout" method="post" action="<%= url_for 'user' %>"> | ||||
|     <div class="fe"> | ||||
|         <label for="old-pw">Current password:</label> | ||||
|         <input id="old-pw" type="password" name="old-pw" title="Enter your old password"/> | ||||
|     </div> | ||||
|     <div class="fe"> | ||||
|         <label for="new-pw">New password:</label> | ||||
|         <input id="new-pw" type="password" name="new-pw" title="Enter your new password"/> | ||||
|         <label for="new-pw2">Repeat new password:</label> | ||||
|         <input id="new-pw2" type="password" name="new-pw2" title="Repeat your new password"/> | ||||
|     </div> | ||||
|     <div class="fe fe-last fe-buttons"> | ||||
|         <button type="submit" name="changepw" id="changepw-button"><span>Change password</span></button> | ||||
|     </div> | ||||
| </form> | ||||
|  | ||||
| <div class="fe"> | ||||
| <h2><a href="<%= url_for 'logout' %>">Logout user  <%= " ".session( 'user' ) =%></a></h2> | ||||
| </div> | ||||
| @@ -77,17 +77,23 @@ | ||||
|                     </li> | ||||
|                 % } | ||||
|                 </ul> | ||||
|                 <ul class="donate"> | ||||
|                 <ul class="right"> | ||||
|                     <li> | ||||
|                         <span> | ||||
|                             <form action="https://www.paypal.com/cgi-bin/webscr" method="post"> | ||||
|                                 <input type="hidden" name="cmd" value="_s-xclick"> | ||||
|                                 <input type="hidden" name="encrypted" value="-----BEGIN PKCS7-----MIIHXwYJKoZIhvcNAQcEoIIHUDCCB0wCAQExggEwMIIBLAIBADCBlDCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb20CAQAwDQYJKoZIhvcNAQEBBQAEgYBaeo/PTwddRsc3FAudTvYTblpooDc87yC2IVGCJt7BQ4TMTvTkB+388KG5ZN53JJ5/nTJLrlW3HgM2ahqQxzTjJtep4YGdnK46o1Dvi/dhvVXa/P9OOHwasi+vE0jaMTtleK+7OToRczh50d3zSwSGNOe3BnobDSTyQUVhkkG8cDELMAkGBSsOAwIaBQAwgdwGCSqGSIb3DQEHATAUBggqhkiG9w0DBwQISih+zXetJ/qAgbhQsZQuFqJNinJBlEVZbvYf5y12OOJq3iGTWwLEsQy8WrMJBlEzgBgTqPSnV2NR+5ogzjMaM6eTHadnweWA2gP2oNSt97afVVAQEMdKfXrmV6Be2YtgdznTzjLyPL+zwvVmbZtZtJSxExMo0wcFFspCxbgtDpN1RTvzC9JLaZpdWFaattrBjcxzHln0ed8ao6eG/1ZoMLs8enrwo4i3WycOLv5MlRfVxJysyiBiBGQGT0+djNONhVYsoIIDhzCCA4MwggLsoAMCAQICAQAwDQYJKoZIhvcNAQEFBQAwgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMB4XDTA0MDIxMzEwMTMxNVoXDTM1MDIxMzEwMTMxNVowgY4xCzAJBgNVBAYTAlVTMQswCQYDVQQIEwJDQTEWMBQGA1UEBxMNTW91bnRhaW4gVmlldzEUMBIGA1UEChMLUGF5UGFsIEluYy4xEzARBgNVBAsUCmxpdmVfY2VydHMxETAPBgNVBAMUCGxpdmVfYXBpMRwwGgYJKoZIhvcNAQkBFg1yZUBwYXlwYWwuY29tMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDBR07d/ETMS1ycjtkpkvjXZe9k+6CieLuLsPumsJ7QC1odNz3sJiCbs2wC0nLE0uLGaEtXynIgRqIddYCHx88pb5HTXv4SZeuv0Rqq4+axW9PLAAATU8w04qqjaSXgbGLP3NmohqM6bV9kZZwZLR/klDaQGo1u9uDb9lr4Yn+rBQIDAQABo4HuMIHrMB0GA1UdDgQWBBSWn3y7xm8XvVk/UtcKG+wQ1mSUazCBuwYDVR0jBIGzMIGwgBSWn3y7xm8XvVk/UtcKG+wQ1mSUa6GBlKSBkTCBjjELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAkNBMRYwFAYDVQQHEw1Nb3VudGFpbiBWaWV3MRQwEgYDVQQKEwtQYXlQYWwgSW5jLjETMBEGA1UECxQKbGl2ZV9jZXJ0czERMA8GA1UEAxQIbGl2ZV9hcGkxHDAaBgkqhkiG9w0BCQEWDXJlQHBheXBhbC5jb22CAQAwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQUFAAOBgQCBXzpWmoBa5e9fo6ujionW1hUhPkOBakTr3YCDjbYfvJEiv/2P+IobhOGJr85+XHhN0v4gUkEDI8r2/rNk1m0GA8HKddvTjyGw/XqXa+LSTlDYkqI8OwR8GEYj4efEtcRpRYBxV8KxAW93YDWzFGvruKnnLbDAF6VR5w/cCMn5hzGCAZowggGWAgEBMIGUMIGOMQswCQYDVQQGEwJVUzELMAkGA1UECBMCQ0ExFjAUBgNVBAcTDU1vdW50YWluIFZpZXcxFDASBgNVBAoTC1BheVBhbCBJbmMuMRMwEQYDVQQLFApsaXZlX2NlcnRzMREwDwYDVQQDFAhsaXZlX2FwaTEcMBoGCSqGSIb3DQEJARYNcmVAcGF5cGFsLmNvbQIBADAJBgUrDgMCGgUAoF0wGAYJKoZIhvcNAQkDMQsGCSqGSIb3DQEHATAcBgkqhkiG9w0BCQUxDxcNMTIxMDMwMTYwODUyWjAjBgkqhkiG9w0BCQQxFgQUb+akCR6HL72NeiJrkvEvKWPu/rwwDQYJKoZIhvcNAQEBBQAEgYCq0fOrW15BGypsRjWSPLQEdg1iE+tEaZxsVLXMyKJoGUr87RtIEQbi76Nq2AfVmDIdCgqIiN7xWyEIsVclD+lXaT8olBtalFb+P9F/5FJ1LvUZGg5fHz80feMX3Z+eIE21gaXzt2yTNFx52hN88ZVBOddCL+3PYlQq6RozLLXI3Q==-----END PKCS7-----"> | ||||
|                                 <input type="image" src="https://www.paypalobjects.com/en_US/i/btn/btn_donate_LG.gif" border="0" name="submit" alt="PayPal - The safer, easier way to pay online!"> | ||||
|                                 <img alt="" border="0" src="https://www.paypalobjects.com/en_US/i/scr/pixel.gif" width="1" height="1"> | ||||
|                             </form> | ||||
|                         </span> | ||||
|                     </li> | ||||
|                     % if ( session('user') ) { | ||||
|                         % if ( $self->match->endpoint->name eq "user" ) { | ||||
|                             <span>user: <%= session('user') %></span> | ||||
|                         % } else { | ||||
|                             <a href="<%= url_for 'user' %>" title="link to: user page" rel="permalink">user: <%= session('user') %></a> | ||||
|                         % } | ||||
|                     % } else { | ||||
|                         % if ( $self->match->endpoint->name eq "login" ) { | ||||
|                             <span>login</span> | ||||
|                         % } else { | ||||
|                             <a href="<%= url_for 'login' %>" title="link to: login/register" rel="permalink">login</a> | ||||
|                         % } | ||||
|                     % } | ||||
|                     <li> | ||||
|  | ||||
|                 </ul> | ||||
|             </nav> | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user