Skip to content

Commit 32cb62a

Browse files
author
Kaitlyn Parkhurst
committed
Register, login controller and templates.
1 parent 478e58e commit 32cb62a

File tree

4 files changed

+233
-15
lines changed

4 files changed

+233
-15
lines changed

Web/lib/BlogDB/Web.pm

Lines changed: 23 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,14 @@ sub startup ($self) {
3131
});
3232

3333
# Get the router.
34-
my $r = $self->routes;
34+
my $router = $self->routes;
35+
36+
# Create a dispatch chain that gives us a logged in user if we have one.
37+
#
38+
# This will NOT throw out a user.
39+
# Templates must consider that $person is undef when no one is logged in.
40+
my $r = $router->under( '/' => sub ($c) {
3541

36-
# Create a dispatch chain that requires the user is logged in.
37-
my $auth = $r->under( '/' => sub ($c) {
38-
3942
# Login via session cookie.
4043
if ( $c->session('uid') ) {
4144
my $person = $c->db->resultset('Person')->find( $c->session('uid') );
@@ -44,26 +47,32 @@ sub startup ($self) {
4447
$c->stash->{person} = $person;
4548
return 1;
4649
}
47-
$c->redirect_to( $c->url_for( 'auth_login' ) );
48-
return undef;
4950
}
5051

51-
$c->redirect_to( $c->url_for( 'auth_login' ) );
52+
return 1;
53+
});
54+
55+
# Only allow logged in users to access the page.
56+
my $auth = $r->under( '/' => sub ($c) {
57+
58+
return 1 if $c->stash->{person};
59+
60+
$c->redirect_to( $c->url_for( 'homepage' ) );
5261
return undef;
5362
});
5463

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

5867
# Standard user stuff: register, forgot password, login and logout.
59-
$r->get ( '/register' )->to( 'Root#get_register' )->name('register');
60-
$r->post( '/register' )->to( 'Root#post_register')->name('do_register');
61-
$r->get ( '/forgot' )->to( 'Root#get_forgot' )->name('forgot_password');
68+
$r->get ( '/register' )->to( 'Root#get_register' )->name('register' );
69+
$r->post( '/register' )->to( 'Root#post_register')->name('do_register' );
70+
$r->get ( '/forgot' )->to( 'Root#get_forgot' )->name('forgot_password' );
6271
$r->post( '/forgot' )->to( 'Root#post_forgot' )->name('do_forgot_password');
63-
$r->get ( '/forgot/:token')->to( 'Root#get_reset' )->name('reset_password');
64-
$r->post( '/forgot/:token')->to( 'Root#post_reset' )->name('do_reset_password');
65-
$r->post( '/login' )->to( 'Root#post_login' )->name('do_login');
66-
$r->post( '/logout' )->to( 'Root#post_logout' )->name('do_logout');
72+
$r->get ( '/forgot/:token')->to( 'Root#get_reset' )->name('reset_password' );
73+
$r->post( '/forgot/:token')->to( 'Root#post_reset' )->name('do_reset_password' );
74+
$r->post( '/login' )->to( 'Root#post_login' )->name('do_login' );
75+
$r->post( '/logout' )->to( 'Root#post_logout' )->name('do_logout' );
6776

6877
# /user/ routes
6978
$r->get ( '/user/:name' )->to( 'User#get_user' )->name( 'user' );

Web/lib/BlogDB/Web/Controller/Root.pm

Lines changed: 74 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package BlogDB::Web::Controller::Root;
22
use Mojo::Base 'Mojolicious::Controller', -signatures;
3+
use Try::Tiny;
34

45
sub get_register ($c) {
56
$c->set_template( 'register' );
@@ -9,6 +10,55 @@ sub get_register ($c) {
910
sub post_register ($c) {
1011
$c->set_template( 'register' );
1112

13+
my $username = $c->stash->{form_username} = $c->param('username');
14+
my $email = $c->stash->{form_email} = $c->param('email');
15+
my $password = $c->stash->{form_password} = $c->param('password');
16+
my $confirm = $c->stash->{form_confirm} = $c->param('confirm');
17+
18+
# Error Checking - We have all of the information.
19+
push @{$c->stash->{errors}}, "Username is required." unless $username;
20+
push @{$c->stash->{errors}}, "Email address is required." unless $email;
21+
push @{$c->stash->{errors}}, "Password is required." unless $password;
22+
push @{$c->stash->{errors}}, "Confirm password is required." unless $password;
23+
24+
# Error Checking - Username conforms to expectations:
25+
push @{$c->stash->{errors}}, "Usernames must start with a letter and then may contain letters, numbers, underscores and dashes."
26+
unless $username =~ /^[a-zA-Z][a-zA-Z0-9_-]+$/;
27+
28+
return 0 if $c->stash->{errors}; # Drop out of processing the registration if there are any errors.
29+
30+
# Error Checking - No user exists with this username or email address, password is valid.
31+
my $is_user_exist = $c->db->resultset('Person')->find( { username => $username } );
32+
my $is_email_exist = $c->db->resultset('Person')->find( { email => $email } );
33+
34+
push @{$c->stash->{errors}}, "Password & Confirmation must match." unless $password eq $confirm;
35+
push @{$c->stash->{errors}}, "Password must be at least 7 chars." unless 7 < length($password);
36+
push @{$c->stash->{errors}}, "This username is already in use." unless not $is_user_exist;
37+
push @{$c->stash->{errors}}, "This email address is already registered." unless not $is_email_exist;
38+
39+
return 0 if $c->stash->{errors}; # Drop out of processing the registration if there are any errors.
40+
41+
# Alright, we are clear to create the account now.
42+
43+
my $person = try {
44+
$c->db->storage->schema->txn_do( sub {
45+
my $person = $c->db->resultset('Person')->create({
46+
email => $email,
47+
username => $username,
48+
});
49+
$person->new_related( 'auth_password', {} )->set_password($password);
50+
return $person;
51+
});
52+
} catch {
53+
push @{$c->stash->{errors}}, "The account could not be created: $_";
54+
};
55+
56+
return 0 if $c->stash->{errors}; # Drop out of processing the registration if there are any errors.
57+
58+
# We have created a user account for this person
59+
$c->session->{uid} = $person->id;
60+
61+
$c->redirect_to( $c->url_for( 'homepage' ) );
1262
}
1363

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

3282
}
3383

34-
sub post_login ($c) {
84+
sub post_reset ($c) {
85+
$c->set_template( 'login' );
86+
87+
}
3588

89+
sub post_login ($c) {
90+
my $username = $c->stash->{form_username} = $c->param('username');
91+
my $password = $c->stash->{form_password} = $c->param('password');
92+
my $return = $c->stash->{form_return} = $c->param('return_url');
93+
94+
# Find the user -- if they have an @, assume it's an email addresss.
95+
my $person = $c->db->resultset('Person')->find( index($username, '@') == -1
96+
? { username => $username }
97+
: { email => $username }
98+
);
99+
100+
# Check that we have a user account and the password matches, otherwise redirect without login.
101+
$c->redirect_to( $return ) unless $person;
102+
$c->redirect_to( $return ) unless $person->auth_password->check_password( $password );
103+
104+
# The user checks out, log them in.
105+
$c->session->{uid} = $person->id; # Set the user cookie for the login.
106+
$c->redirect_to( $return ); # Send them to the page they were on with a refresh.
36107
}
37108

38109
sub post_logout ($c) {
110+
delete $c->session->{uid};
111+
$c->redirect_to( $c->url_for( 'homepage' ));
39112

40113
}
41114

Web/templates/default/_/form/input.tx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<!-- RUN _/forms/input.tx -->
2+
<div class="mb-3">
3+
<label for="form_[% $name %]" class="form-label">[% $title %]</label>
4+
<input type="[% $type %]" class="form-control" id="form_[% $name %]" name="[% $name %]" value="[% $value %]" aria-describedby="form_[% $name %]_help">
5+
<div id="form_[% $name %]_help" class="form-text">[% $help %]</div>
6+
</div>
7+
<!-- END _forms/input.tx -->
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
%% 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>');
2+
3+
%% 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>');
4+
5+
<!doctype html>
6+
<html lang="en">
7+
<head>
8+
<!-- Required meta tags -->
9+
<meta charset="utf-8">
10+
<meta name="viewport" content="width=device-width, initial-scale=1">
11+
12+
<!-- Bootstrap CSS -->
13+
<link
14+
href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/css/bootstrap.min.css"
15+
rel="stylesheet"
16+
integrity="sha384-uWxY/CJNBR+1zjPWmfnSnVxwRheevXITnMqoEIeG1LJrdI0GlVs/9cVSyPYXdcSF"
17+
crossorigin="anonymous">
18+
19+
<style>
20+
21+
.main {
22+
background-color: orange;
23+
height: 100%;
24+
}
25+
</style>
26+
27+
<title>Hello, world!</title>
28+
</head>
29+
<body>
30+
<nav class="navbar navbar-light bg-light">
31+
<div class="container-fluid">
32+
<div class="col">
33+
<a class="navbar-brand" style="font-size: 3em;" href="#">BlogDB</a>
34+
</div>
35+
<div class="col">
36+
%% if ( $login_fail ) {
37+
<p class="warning">Error: Login Failed.</p>
38+
%% }
39+
</div>
40+
%% if ( $person ) {
41+
<div class="col">
42+
<a href="[% $c.url_for( 'user', { name => $person.username } ) %]">[% $person.username %]</a>
43+
<a href="[% $c.url_for( 'user_settings' ) %]">[% $gear_icon %]</a>
44+
45+
<form method="post" action="[% $c.url_for( 'do_logout' ) %]">
46+
<button class="link" type="submit">[% $door_icon %]</button>
47+
</form>
48+
</div>
49+
50+
%% } else {
51+
<div class="col">
52+
<form method="post" action="[% $c.url_for( 'do_login' ) %]" class="d-flex">
53+
<input type="hidden" name="return_url" value="[% $form_return || $c.url_for() %]">
54+
<input class="form-control me-2" type="text" name="username" placeholder="Username" aria-label="Username">
55+
<input class="form-control me-2" type="password" name="password" placeholder="Password" aria-label="Password">
56+
<button class="btn btn-outline-success" type="submit">Login</button>
57+
</form>
58+
<div class="container-fluid">
59+
<p style="margin-right: 0em;"><a href="[% $c.url_for('register') %]">Register</a> | <a href="[% $c.url_for( 'forgot' ) %]">Forgot</a></p>
60+
</div>
61+
</div>
62+
%% }
63+
</div>
64+
</nav>
65+
66+
<main class="content-fluid main">
67+
68+
<div class="row">
69+
<!-- Error Handling On LHS -->
70+
<div class="col">
71+
%% if ( $errors.size() ) {
72+
<div style="margin-top: 2em" class="alert alert-danger" role="alert">
73+
There were errors with your request that could not be resolved:
74+
<ul>
75+
%% for $errors -> $error {
76+
<li>[% $error %]</li>
77+
%% }
78+
</ul>
79+
</div>
80+
%% }
81+
</div>
82+
83+
<!-- Registration Handling On RHS -->
84+
<div class="col">
85+
<form method="post" action="[% $c.url_for( 'do_register' ) %]">
86+
%% include 'default/_/form/input.tx' { type => 'text', name => 'username',
87+
%% title => 'Username',
88+
%% help => 'Your username is unique, friends can follow you at /user/YourName',
89+
%% value => $form_username,
90+
%% };
91+
92+
%% include 'default/_/form/input.tx' { type => 'email', name => 'email',
93+
%% title => 'Email address',
94+
%% help => 'You will need to confirm your email address to post comments.',
95+
%% value => $form_email,
96+
%% };
97+
98+
%% include 'default/_/form/input.tx' { type => 'password', name => 'password',
99+
%% title => 'Password',
100+
%% help => 'You will need your password to login.',
101+
%% value => $form_password,
102+
%% };
103+
104+
%% include 'default/_/form/input.tx' { type => 'password', name => 'confirm',
105+
%% title => 'Confirm password',
106+
%% help => mark_raw('Just to <strike>annoy you</strike> be sure it is correct.'),
107+
%% value => $form_confirm,
108+
%% };
109+
110+
<button type="submit" class="btn btn-primary float-end">Register</button>
111+
112+
</form>
113+
</div>
114+
115+
116+
</main>
117+
118+
119+
120+
121+
122+
<!-- Bootstrap Javascript -->
123+
<script
124+
src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.2/dist/js/bootstrap.bundle.min.js"
125+
integrity="sha384-kQtW33rZJAHjgefvhyyzcGF3C5TFyBQBA13V1RKPf4uH+bwyzQxZ6CmMZHmNBEfJ"
126+
crossorigin="anonymous">
127+
</script>
128+
</body>
129+
</html>

0 commit comments

Comments
 (0)