Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Register, login controller and templates.
  • Loading branch information
Kaitlyn Parkhurst committed Oct 9, 2021
1 parent 478e58e commit 32cb62a
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 15 deletions.
37 changes: 23 additions & 14 deletions Web/lib/BlogDB/Web.pm
Expand Up @@ -31,11 +31,14 @@ sub startup ($self) {
});

# Get the router.
my $r = $self->routes;
my $router = $self->routes;

# Create a dispatch chain that gives us a logged in user if we have one.
#
# This will NOT throw out a user.
# Templates must consider that $person is undef when no one is logged in.
my $r = $router->under( '/' => sub ($c) {

# Create a dispatch chain that requires the user is logged in.
my $auth = $r->under( '/' => sub ($c) {

# Login via session cookie.
if ( $c->session('uid') ) {
my $person = $c->db->resultset('Person')->find( $c->session('uid') );
Expand All @@ -44,26 +47,32 @@ sub startup ($self) {
$c->stash->{person} = $person;
return 1;
}
$c->redirect_to( $c->url_for( 'auth_login' ) );
return undef;
}

$c->redirect_to( $c->url_for( 'auth_login' ) );
return 1;
});

# Only allow logged in users to access the page.
my $auth = $r->under( '/' => sub ($c) {

return 1 if $c->stash->{person};

$c->redirect_to( $c->url_for( 'homepage' ) );
return undef;
});

# Home Page
$r->get('/')->to( 'Root#get_homepage' )->name('homepage');

# Standard user stuff: register, forgot password, login and logout.
$r->get ( '/register' )->to( 'Root#get_register' )->name('register');
$r->post( '/register' )->to( 'Root#post_register')->name('do_register');
$r->get ( '/forgot' )->to( 'Root#get_forgot' )->name('forgot_password');
$r->get ( '/register' )->to( 'Root#get_register' )->name('register' );
$r->post( '/register' )->to( 'Root#post_register')->name('do_register' );
$r->get ( '/forgot' )->to( 'Root#get_forgot' )->name('forgot_password' );
$r->post( '/forgot' )->to( 'Root#post_forgot' )->name('do_forgot_password');
$r->get ( '/forgot/:token')->to( 'Root#get_reset' )->name('reset_password');
$r->post( '/forgot/:token')->to( 'Root#post_reset' )->name('do_reset_password');
$r->post( '/login' )->to( 'Root#post_login' )->name('do_login');
$r->post( '/logout' )->to( 'Root#post_logout' )->name('do_logout');
$r->get ( '/forgot/:token')->to( 'Root#get_reset' )->name('reset_password' );
$r->post( '/forgot/:token')->to( 'Root#post_reset' )->name('do_reset_password' );
$r->post( '/login' )->to( 'Root#post_login' )->name('do_login' );
$r->post( '/logout' )->to( 'Root#post_logout' )->name('do_logout' );

# /user/ routes
$r->get ( '/user/:name' )->to( 'User#get_user' )->name( 'user' );
Expand Down
75 changes: 74 additions & 1 deletion Web/lib/BlogDB/Web/Controller/Root.pm
@@ -1,5 +1,6 @@
package BlogDB::Web::Controller::Root;
use Mojo::Base 'Mojolicious::Controller', -signatures;
use Try::Tiny;

sub get_register ($c) {
$c->set_template( 'register' );
Expand All @@ -9,6 +10,55 @@ sub get_register ($c) {
sub post_register ($c) {
$c->set_template( 'register' );

my $username = $c->stash->{form_username} = $c->param('username');
my $email = $c->stash->{form_email} = $c->param('email');
my $password = $c->stash->{form_password} = $c->param('password');
my $confirm = $c->stash->{form_confirm} = $c->param('confirm');

# Error Checking - We have all of the information.
push @{$c->stash->{errors}}, "Username is required." unless $username;
push @{$c->stash->{errors}}, "Email address is required." unless $email;
push @{$c->stash->{errors}}, "Password is required." unless $password;
push @{$c->stash->{errors}}, "Confirm password is required." unless $password;

# Error Checking - Username conforms to expectations:
push @{$c->stash->{errors}}, "Usernames must start with a letter and then may contain letters, numbers, underscores and dashes."
unless $username =~ /^[a-zA-Z][a-zA-Z0-9_-]+$/;

return 0 if $c->stash->{errors}; # Drop out of processing the registration if there are any errors.

# Error Checking - No user exists with this username or email address, password is valid.
my $is_user_exist = $c->db->resultset('Person')->find( { username => $username } );
my $is_email_exist = $c->db->resultset('Person')->find( { email => $email } );

push @{$c->stash->{errors}}, "Password & Confirmation must match." unless $password eq $confirm;
push @{$c->stash->{errors}}, "Password must be at least 7 chars." unless 7 < length($password);
push @{$c->stash->{errors}}, "This username is already in use." unless not $is_user_exist;
push @{$c->stash->{errors}}, "This email address is already registered." unless not $is_email_exist;

return 0 if $c->stash->{errors}; # Drop out of processing the registration if there are any errors.

# Alright, we are clear to create the account now.

my $person = try {
$c->db->storage->schema->txn_do( sub {
my $person = $c->db->resultset('Person')->create({
email => $email,
username => $username,
});
$person->new_related( 'auth_password', {} )->set_password($password);
return $person;
});
} catch {
push @{$c->stash->{errors}}, "The account could not be created: $_";
};

return 0 if $c->stash->{errors}; # Drop out of processing the registration if there are any errors.

# We have created a user account for this person
$c->session->{uid} = $person->id;

$c->redirect_to( $c->url_for( 'homepage' ) );
}

sub get_forgot ($c) {
Expand All @@ -31,11 +81,34 @@ sub post_reset ($c) {

}

sub post_login ($c) {
sub post_reset ($c) {
$c->set_template( 'login' );

}

sub post_login ($c) {
my $username = $c->stash->{form_username} = $c->param('username');
my $password = $c->stash->{form_password} = $c->param('password');
my $return = $c->stash->{form_return} = $c->param('return_url');

# Find the user -- if they have an @, assume it's an email addresss.
my $person = $c->db->resultset('Person')->find( index($username, '@') == -1
? { username => $username }
: { email => $username }
);

# Check that we have a user account and the password matches, otherwise redirect without login.
$c->redirect_to( $return ) unless $person;
$c->redirect_to( $return ) unless $person->auth_password->check_password( $password );

# The user checks out, log them in.
$c->session->{uid} = $person->id; # Set the user cookie for the login.
$c->redirect_to( $return ); # Send them to the page they were on with a refresh.
}

sub post_logout ($c) {
delete $c->session->{uid};
$c->redirect_to( $c->url_for( 'homepage' ));

}

Expand Down
7 changes: 7 additions & 0 deletions Web/templates/default/_/form/input.tx
@@ -0,0 +1,7 @@
<!-- RUN _/forms/input.tx -->
<div class="mb-3">
<label for="form_[% $name %]" class="form-label">[% $title %]</label>
<input type="[% $type %]" class="form-control" id="form_[% $name %]" name="[% $name %]" value="[% $value %]" aria-describedby="form_[% $name %]_help">
<div id="form_[% $name %]_help" class="form-text">[% $help %]</div>
</div>
<!-- END _forms/input.tx -->
129 changes: 129 additions & 0 deletions Web/templates/default/register.html.tx
@@ -0,0 +1,129 @@
%% my $gear_icon = mark_raw('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-gear" viewBox="0 0 16 16"> <path d="M8 4.754a3.246 3.246 0 1 0 0 6.492 3.246 3.246 0 0 0 0-6.492zM5.754 8a2.246 2.246 0 1 1 4.492 0 2.246 2.246 0 0 1-4.492 0z"/> <path d="M9.796 1.343c-.527-1.79-3.065-1.79-3.592 0l-.094.319a.873.873 0 0 1-1.255.52l-.292-.16c-1.64-.892-3.433.902-2.54 2.541l.159.292a.873.873 0 0 1-.52 1.255l-.319.094c-1.79.527-1.79 3.065 0 3.592l.319.094a.873.873 0 0 1 .52 1.255l-.16.292c-.892 1.64.901 3.434 2.541 2.54l.292-.159a.873.873 0 0 1 1.255.52l.094.319c.527 1.79 3.065 1.79 3.592 0l.094-.319a.873.873 0 0 1 1.255-.52l.292.16c1.64.893 3.434-.902 2.54-2.541l-.159-.292a.873.873 0 0 1 .52-1.255l.319-.094c1.79-.527 1.79-3.065 0-3.592l-.319-.094a.873.873 0 0 1-.52-1.255l.16-.292c.893-1.64-.902-3.433-2.541-2.54l-.292.159a.873.873 0 0 1-1.255-.52l-.094-.319zm-2.633.283c.246-.835 1.428-.835 1.674 0l.094.319a1.873 1.873 0 0 0 2.693 1.115l.291-.16c.764-.415 1.6.42 1.184 1.185l-.159.292a1.873 1.873 0 0 0 1.116 2.692l.318.094c.835.246.835 1.428 0 1.674l-.319.094a1.873 1.873 0 0 0-1.115 2.693l.16.291c.415.764-.42 1.6-1.185 1.184l-.291-.159a1.873 1.873 0 0 0-2.693 1.116l-.094.318c-.246.835-1.428.835-1.674 0l-.094-.319a1.873 1.873 0 0 0-2.692-1.115l-.292.16c-.764.415-1.6-.42-1.184-1.185l.159-.291A1.873 1.873 0 0 0 1.945 8.93l-.319-.094c-.835-.246-.835-1.428 0-1.674l.319-.094A1.873 1.873 0 0 0 3.06 4.377l-.16-.292c-.415-.764.42-1.6 1.185-1.184l.292.159a1.873 1.873 0 0 0 2.692-1.115l.094-.319z"/> </svg>');

%% my $door_icon = mark_raw('<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-door-closed-fill" viewBox="0 0 16 16"><path d="M12 1a1 1 0 0 1 1 1v13h1.5a.5.5 0 0 1 0 1h-13a.5.5 0 0 1 0-1H3V2a1 1 0 0 1 1-1h8zm-2 9a1 1 0 1 0 0-2 1 1 0 0 0 0 2z"/></svg>');

<!doctype html>
<html lang="en">
<head>
<!-- Required meta tags -->
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">

<!-- Bootstrap CSS -->
<link
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/css/bootstrap.min.css"
rel="stylesheet"
integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF"
crossorigin="anonymous">

<style>

.main {
background-color: orange;
height: 100%;
}
</style>

<title>Hello, world!</title>
</head>
<body>
<nav class="navbar navbar-light bg-light">
<div class="container-fluid">
<div class="col">
<a class="navbar-brand" style="font-size: 3em;" href="#">BlogDB</a>
</div>
<div class="col">
%% if ( $login_fail ) {
<p class="warning">Error: Login Failed.</p>
%% }
</div>
%% if ( $person ) {
<div class="col">
<a href="[% $c.url_for( 'user', { name => $person.username } ) %]">[% $person.username %]</a>
<a href="[% $c.url_for( 'user_settings' ) %]">[% $gear_icon %]</a>

<form method="post" action="[% $c.url_for( 'do_logout' ) %]">
<button class="link" type="submit">[% $door_icon %]</button>
</form>
</div>

%% } else {
<div class="col">
<form method="post" action="[% $c.url_for( 'do_login' ) %]" class="d-flex">
<input type="hidden" name="return_url" value="[% $form_return || $c.url_for() %]">
<input class="form-control me-2" type="text" name="username" placeholder="Username" aria-label="Username">
<input class="form-control me-2" type="password" name="password" placeholder="Password" aria-label="Password">
<button class="btn btn-outline-success" type="submit">Login</button>
</form>
<div class="container-fluid">
<p style="margin-right: 0em;"><a href="[% $c.url_for('register') %]">Register</a> | <a href="[% $c.url_for( 'forgot' ) %]">Forgot</a></p>
</div>
</div>
%% }
</div>
</nav>

<main class="content-fluid main">

<div class="row">
<!-- Error Handling On LHS -->
<div class="col">
%% if ( $errors.size() ) {
<div style="margin-top: 2em" class="alert alert-danger" role="alert">
There were errors with your request that could not be resolved:
<ul>
%% for $errors -> $error {
<li>[% $error %]</li>
%% }
</ul>
</div>
%% }
</div>

<!-- Registration Handling On RHS -->
<div class="col">
<form method="post" action="[% $c.url_for( 'do_register' ) %]">
%% include 'default/_/form/input.tx' { type => 'text', name => 'username',
%% title => 'Username',
%% help => 'Your username is unique, friends can follow you at /user/YourName',
%% value => $form_username,
%% };

%% include 'default/_/form/input.tx' { type => 'email', name => 'email',
%% title => 'Email address',
%% help => 'You will need to confirm your email address to post comments.',
%% value => $form_email,
%% };

%% include 'default/_/form/input.tx' { type => 'password', name => 'password',
%% title => 'Password',
%% help => 'You will need your password to login.',
%% value => $form_password,
%% };

%% include 'default/_/form/input.tx' { type => 'password', name => 'confirm',
%% title => 'Confirm password',
%% help => mark_raw('Just to <strike>annoy you</strike> be sure it is correct.'),
%% value => $form_confirm,
%% };

<button type="submit" class="btn btn-primary float-end">Register</button>

</form>
</div>


</main>





<!-- Bootstrap Javascript -->
<script
src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/js/bootstrap.bundle.min.js"
integrity="sha384-kQtW33rZJAHjgefvhyyzcGF3C5TFyBQBA13V1RKPf4uH+bwyzQxZ6CmMZHmNBEfJ"
crossorigin="anonymous">
</script>
</body>
</html>

0 comments on commit 32cb62a

Please sign in to comment.