Compare commits
2 Commits
f0ab7713cc
...
cce6e230c9
Author | SHA1 | Date |
---|---|---|
ngoomie | cce6e230c9 | |
ngoomie | dc9a42f0e6 |
|
@ -1,9 +1,6 @@
|
|||
# CharmBoard-specific
|
||||
charmboard.conf
|
||||
|
||||
# Mojolicious examples to be nuked later
|
||||
t/basic.t
|
||||
|
||||
# SQLite
|
||||
*.db
|
||||
*.db-*
|
|
@ -10,7 +10,6 @@
|
|||
"Facepunch",
|
||||
"passchk",
|
||||
"passgen",
|
||||
"pgsql",
|
||||
"resultset",
|
||||
"signup",
|
||||
"subf",
|
||||
|
@ -38,15 +37,6 @@
|
|||
"bold": false,
|
||||
"italic": false
|
||||
},
|
||||
{
|
||||
"tag": "//",
|
||||
"color": "#474747",
|
||||
"strikethrough": true,
|
||||
"underline": false,
|
||||
"backgroundColor": "transparent",
|
||||
"bold": false,
|
||||
"italic": false
|
||||
},
|
||||
{
|
||||
"tag": "todo",
|
||||
"color": "#FF8C00",
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
board_name => '', # this doesn't do anything yet
|
||||
board_name => '',
|
||||
|
||||
database => {
|
||||
type => '', # 'sqlite' or 'mysql'
|
||||
|
|
|
@ -1,67 +1,105 @@
|
|||
package CharmBoard;
|
||||
use utf8;
|
||||
use experimental 'try', 'smartmatch';
|
||||
use Mojo::Base 'Mojolicious', -signatures;
|
||||
use CharmBoard::Schema;
|
||||
|
||||
=pod
|
||||
=head1 NAME
|
||||
CharmBoard - revive the fun posting experience!
|
||||
=head1 NOTES
|
||||
This documentation is intended for prospective code
|
||||
contributors. If you're looking to set CharmBoard up,
|
||||
look for the Markdown format (.md) documentation instead.
|
||||
|
||||
CharmBoard uses a max line length of 60 chars and a tab
|
||||
size of two spaces.
|
||||
=head1 DESCRIPTION
|
||||
CharmBoard is forum software written in Perl with
|
||||
Mojolicious, intended to be a more fun alternative to the
|
||||
bigger forum suites available today, inspired by older
|
||||
forum software like AcmlmBoard, while also being more
|
||||
modernized in terms of security practices than they are.
|
||||
Customization ability is another important goal next to
|
||||
making software that feels fun for the end user to use.
|
||||
=cut
|
||||
|
||||
# this method will run once at server start
|
||||
sub startup ($app) {
|
||||
sub startup ($self) {
|
||||
# load plugins that require no additional conf
|
||||
$app->plugin('TagHelpers');
|
||||
$self->plugin('TagHelpers');
|
||||
|
||||
# load configuration from config file
|
||||
my $config = $app->plugin('Config' => {file => 'charmboard.conf'});
|
||||
my $config = $self->plugin('Config' =>
|
||||
{file => 'charmboard.conf'});
|
||||
|
||||
# set this specific forum's name
|
||||
$self->helper(boardName => sub {$config->{board_name}});
|
||||
|
||||
# load dev env only stuff, if applicable
|
||||
if ( $config->{environment} eq 'dev' ) {
|
||||
$app->plugin('Renderer::WithoutCache');
|
||||
$app->renderer->cache->max_keys(0)};
|
||||
$self->plugin('Renderer::WithoutCache');
|
||||
$self->renderer->cache->max_keys(0)};
|
||||
|
||||
# import Mojolicious secrets
|
||||
$app->secrets($config->{secrets});
|
||||
$self->secrets($config->{secrets});
|
||||
|
||||
# import password pepper value
|
||||
$app->helper(pepper => sub {$config->{pass_crypt}->{pepper}});
|
||||
$self->helper(pepper => sub {$config->{pass_crypt}->{pepper}});
|
||||
|
||||
## database setup
|
||||
# ? this could maybe be a given/when
|
||||
my ($dsn, $dbUnicode);
|
||||
if ($app->config->{database}->{type} ~~ 'sqlite') {
|
||||
if ($self->config->{database}->{type} ~~ 'sqlite') {
|
||||
$dsn = "dbi:SQLite:" . $config->{database}->{name};
|
||||
$dbUnicode = "sqlite_unicode"}
|
||||
elsif ($app->config->{database}->{type} ~~ 'mysql') {
|
||||
|
||||
elsif ($self->config->{database}->{type} ~~ 'mysql') {
|
||||
$dsn = "dbi:mysql:" . $config->{database}->{name};
|
||||
$dbUnicode = "mysql_enable_utf"}
|
||||
else {die "\nUnknown, unsupported, or empty database type in charmboard.conf.
|
||||
If you're sure you've set it to something supported, maybe double check your spelling?\n
|
||||
|
||||
else {die "\nUnknown, unsupported, or empty database type
|
||||
in charmboard.conf. If you're sure you've set it to
|
||||
something supported, maybe double check your spelling?
|
||||
\n\n\t
|
||||
Valid options: 'sqlite', 'mysql'"};
|
||||
|
||||
my $schema = CharmBoard::Schema->connect(
|
||||
$dsn,
|
||||
$config->{database}->{user},
|
||||
$config->{database}->{pass},
|
||||
{$dbUnicode => 1});
|
||||
$app->helper(schema => sub {$schema});
|
||||
$self->helper(schema => sub {$schema});
|
||||
|
||||
# router
|
||||
my $r = $app->routes;
|
||||
my $r = $self->routes;
|
||||
|
||||
# controller routes
|
||||
## index page
|
||||
$r->get('/')->to(
|
||||
controller => 'Controller::Main',
|
||||
controller => 'Controller::Index',
|
||||
action => 'index');
|
||||
|
||||
## registration page
|
||||
$r->get('/register')->to(
|
||||
controller => 'Controller::Auth',
|
||||
controller => 'Controller::Register',
|
||||
action => 'register');
|
||||
$r->post('/register')->to(
|
||||
controller => 'Controller::Auth',
|
||||
controller => 'Controller::Register',
|
||||
action => 'register_do');
|
||||
|
||||
## login page
|
||||
$r->get('/login')->to(
|
||||
controller => 'Controller::Auth',
|
||||
controller => 'Controller::Login',
|
||||
action => 'login');
|
||||
$r->post('/login')->to(
|
||||
controller => 'Controller::Auth',
|
||||
controller => 'Controller::Login',
|
||||
action => 'login_do');
|
||||
|
||||
## logout
|
||||
$r->get('/logout')->to(
|
||||
controller => 'Controller::Logout',
|
||||
action => 'logout_do')
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -1,139 +0,0 @@
|
|||
package CharmBoard::Controller::Auth;
|
||||
use experimental 'try', 'smartmatch';
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
use CharmBoard::Crypt::Password;
|
||||
use CharmBoard::Crypt::Seasoning;
|
||||
|
||||
# initial registration page
|
||||
sub register ($app) {
|
||||
$app->render(
|
||||
template => 'register',
|
||||
error => $app->flash('error'),
|
||||
message => $app->flash('message'))};
|
||||
|
||||
# process submitted registration form
|
||||
sub register_do ($app) {
|
||||
my $username = $app->param('username');
|
||||
my $email = $app->param('email');
|
||||
my $password = $app->param('password');
|
||||
my $confirmPassword = $app->param('confirm-password');
|
||||
|
||||
my $catchError;
|
||||
|
||||
# declare vars used through multiple try/catch blocks with
|
||||
# 'our' so they work throughout the entire subroutine
|
||||
our ($userCheck, $emailCheck, $salt, $hash);
|
||||
|
||||
try { # make sure registration info is valid
|
||||
# TODO: implement email validation here at some point
|
||||
|
||||
# check to make sure all required fields are filled
|
||||
($username, $email, $password, $confirmPassword) ne undef
|
||||
or die "Please fill out all required fields.";
|
||||
|
||||
# check to make sure both passwords match
|
||||
# TODO: add check on frontend for this for people with JS enabled
|
||||
$password eq $confirmPassword
|
||||
or die "Passwords do not match";
|
||||
|
||||
# check to make sure username and/or email isn't already in use;
|
||||
# if not, continue with registration
|
||||
## search for input username and email in database
|
||||
$userCheck = $app->schema->resultset('Users')->search({username => $username})->single;
|
||||
$emailCheck = $app->schema->resultset('Users')->search({email => $email})->single;
|
||||
|
||||
($userCheck && $emailCheck) eq undef
|
||||
or die "Username already in use.\nemail already in use.";
|
||||
($userCheck) eq undef
|
||||
or die "Username already in use.";
|
||||
($emailCheck) eq undef
|
||||
or die "email already in use."}
|
||||
catch ($catchError) {
|
||||
$app->flash(error => $catchError);
|
||||
$app->redirect_to('register');}
|
||||
|
||||
try {
|
||||
$password = $app->pepper . ':' . $password;
|
||||
# return hashed result + salt
|
||||
($salt, $hash) = passgen($password) or die;
|
||||
|
||||
# add user info and pw/salt to DB
|
||||
$app->schema->resultset('Users')->create({
|
||||
username => $username,
|
||||
email => $email,
|
||||
password => $hash,
|
||||
salt => $salt,
|
||||
signup_date => time }) or die;
|
||||
|
||||
$app->flash(message => 'User registered successfully!');
|
||||
$app->redirect_to('register')}
|
||||
catch ($catchError) {
|
||||
print $catchError;
|
||||
$app->flash(error => 'Your registration info was correct,
|
||||
but a server error prevented you from registering.
|
||||
This has been logged so your administrator can fix it.');
|
||||
$app->redirect_to('register')}};
|
||||
|
||||
sub login ($app) {
|
||||
$app->render(
|
||||
template => 'login',
|
||||
error => $app->flash('error'),
|
||||
message => $app->flash('message'))};
|
||||
|
||||
sub login_do ($app) {
|
||||
my $username = $app->param('username');
|
||||
my $password = $app->pepper . ':' . $app->param('password');
|
||||
|
||||
my $catchError;
|
||||
|
||||
# declare vars used through multiple try/catch blocks with
|
||||
# 'our' so they work throughout the entire subroutine
|
||||
our ($userInfo, $passCheck, $userID, $sessionKey);
|
||||
|
||||
try { # check user credentials first
|
||||
# check to see if user by entered username exists
|
||||
$userInfo = $app->schema->resultset('Users')->search({username => $username});
|
||||
$userInfo or die;
|
||||
|
||||
# now check password validity
|
||||
$passCheck = passchk($userInfo->get_column('salt')->first,
|
||||
$userInfo->get_column('password')->first, $password);
|
||||
$passCheck or die;}
|
||||
|
||||
catch ($catchError) { # redirect to login page on fail
|
||||
print $catchError;
|
||||
$app->flash(error => 'Username or password incorrect.');
|
||||
$app->redirect_to('login');}
|
||||
|
||||
try { # now attempt to create session
|
||||
# get user ID for session creation
|
||||
$userID = $userInfo->get_column('user_id')->first;
|
||||
|
||||
# gen session key
|
||||
$sessionKey = seasoning(16);
|
||||
|
||||
# add session to database
|
||||
$app->schema->resultset('Session')->create({
|
||||
session_key => $sessionKey,
|
||||
user_id => $userID,
|
||||
session_expiry => time + 604800,
|
||||
is_ip_bound => 0,
|
||||
bound_ip => undef }) or die;
|
||||
|
||||
# now create session cookie for user
|
||||
$app->session(is_auth => 1);
|
||||
$app->session(user_id => $userID);
|
||||
$app->session(session_key => $sessionKey);
|
||||
$app->session(expiration => 604800);
|
||||
|
||||
# redirect to index upon success
|
||||
$app->redirect_to('/')}
|
||||
|
||||
catch ($catchError) { # redirect to login page on fail
|
||||
print $catchError;
|
||||
$app->flash(error => 'Your username and password were correct,
|
||||
but a server error prevented you from logging in.
|
||||
This has been logged so your administrator can fix it.');
|
||||
$app->redirect_to('login')}}
|
||||
|
||||
1;
|
|
@ -0,0 +1,11 @@
|
|||
package CharmBoard::Controller::Index;
|
||||
use utf8;
|
||||
use experimental 'try', 'smartmatch';
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
|
||||
sub index ($self) {
|
||||
$self->render(template => 'index')
|
||||
|
||||
}
|
||||
|
||||
1;
|
|
@ -0,0 +1,76 @@
|
|||
package CharmBoard::Controller::Login;
|
||||
use utf8;
|
||||
use experimental 'try', 'smartmatch';
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
use CharmBoard::Crypt::Password;
|
||||
use CharmBoard::Crypt::Seasoning;
|
||||
|
||||
=pod
|
||||
=head1 NAME
|
||||
CharmBoard::Controller::Login
|
||||
=cut
|
||||
|
||||
sub login ($self) {
|
||||
$self->render(
|
||||
template => 'login',
|
||||
error => $self->flash('error'),
|
||||
message => $self->flash('message'))};
|
||||
|
||||
sub login_do ($self) {
|
||||
my $username = $self->param('username');
|
||||
my $password = $self->pepper . ':' . $self->param('password');
|
||||
|
||||
my $catchError;
|
||||
|
||||
# declare vars used through multiple try/catch blocks with
|
||||
# 'our' so they work throughout the entire subroutine
|
||||
our ($userInfo, $passCheck, $userID, $sessionKey);
|
||||
|
||||
try { # check user credentials first
|
||||
# check to see if user by entered username exists
|
||||
$userInfo = $self->schema->resultset('Users')->search(
|
||||
{username => $username});
|
||||
$userInfo or die;
|
||||
|
||||
# now check password validity
|
||||
$passCheck = passchk($userInfo->get_column('salt')->first,
|
||||
$userInfo->get_column('password')->first, $password);
|
||||
$passCheck or die;}
|
||||
|
||||
catch ($catchError) { # redirect to login page on fail
|
||||
print $catchError;
|
||||
$self->flash(error => 'Username or password incorrect.');
|
||||
$self->redirect_to('login');}
|
||||
|
||||
try { # now attempt to create session
|
||||
# get user ID for session creation
|
||||
$userID = $userInfo->get_column('user_id')->first;
|
||||
|
||||
# gen session key
|
||||
$sessionKey = seasoning(16);
|
||||
|
||||
# add session to database
|
||||
$self->schema->resultset('Session')->create({
|
||||
session_key => $sessionKey,
|
||||
user_id => $userID,
|
||||
session_expiry => time + 604800,
|
||||
is_ip_bound => 0,
|
||||
bound_ip => undef }) or die;
|
||||
|
||||
# now create session cookie for user
|
||||
$self->session(is_auth => 1);
|
||||
$self->session(user_id => $userID);
|
||||
$self->session(session_key => $sessionKey);
|
||||
$self->session(expiration => 604800);
|
||||
|
||||
# redirect to index upon success
|
||||
$self->redirect_to('/')}
|
||||
|
||||
catch ($catchError) { # redirect to login page on fail
|
||||
print $catchError;
|
||||
$self->flash(error => 'Your username and password were correct,
|
||||
but a server error prevented you from logging in. This has been
|
||||
logged so the administrator can fix it.');
|
||||
$self->redirect_to('login')}}
|
||||
|
||||
1;
|
|
@ -0,0 +1,15 @@
|
|||
package CharmBoard::Controller::Logout;
|
||||
use utf8;
|
||||
use experimental 'try', 'smartmatch';
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
|
||||
sub logout_do ($self) {
|
||||
# destroy entry for this session in the database
|
||||
$self->schema->resultset('Session')->search({
|
||||
session_key => $self->session('session_key')})->delete;
|
||||
# now nuke the actual session cookie
|
||||
$self->session(expires => 1);
|
||||
# redirect to index
|
||||
$self->redirect_to('/')}
|
||||
|
||||
1;
|
|
@ -1,7 +0,0 @@
|
|||
package CharmBoard::Controller::Main;
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
|
||||
sub index ($app) {
|
||||
$app->render(template => 'index')}
|
||||
|
||||
1;
|
|
@ -0,0 +1,79 @@
|
|||
package CharmBoard::Controller::Register;
|
||||
use utf8;
|
||||
use experimental 'try', 'smartmatch';
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
use CharmBoard::Crypt::Password;
|
||||
|
||||
# initial registration page
|
||||
sub register ($self) {
|
||||
$self->render(
|
||||
template => 'register',
|
||||
error => $self->flash('error'),
|
||||
message => $self->flash('message'))};
|
||||
|
||||
# process submitted registration form
|
||||
sub register_do ($self) {
|
||||
my $username = $self->param('username');
|
||||
my $email = $self->param('email');
|
||||
my $password = $self->param('password');
|
||||
my $confirmPassword = $self->param('confirm-password');
|
||||
|
||||
my $catchError;
|
||||
|
||||
# declare vars used through multiple try/catch blocks with
|
||||
# 'our' so they work throughout the entire subroutine
|
||||
our ($userCheck, $emailCheck, $salt, $hash);
|
||||
|
||||
try { # make sure registration info is valid
|
||||
# TODO: implement email validation here at some point
|
||||
|
||||
# check to make sure all required fields are filled
|
||||
($username, $email, $password, $confirmPassword)
|
||||
or die "Please fill out all required fields.";
|
||||
|
||||
# check to make sure both passwords match
|
||||
# TODO: add check on frontend for this for people with JS enabled
|
||||
$password eq $confirmPassword
|
||||
or die "Passwords do not match";
|
||||
|
||||
# check to make sure username and/or email isn't already in use;
|
||||
# if not, continue with registration
|
||||
## search for input username and email in database
|
||||
$userCheck = $self->schema->resultset('Users')->search(
|
||||
{username => $username})->single;
|
||||
$emailCheck = $self->schema->resultset('Users')->search(
|
||||
{email => $email})->single;
|
||||
|
||||
($userCheck && $emailCheck) eq undef
|
||||
or die "Username already in use.\nemail already in use.";
|
||||
($userCheck) eq undef
|
||||
or die "Username already in use.";
|
||||
($emailCheck) eq undef
|
||||
or die "email already in use."}
|
||||
catch ($catchError) {
|
||||
$self->flash(error => $catchError);
|
||||
$self->redirect_to('register');}
|
||||
|
||||
try {
|
||||
$password = $self->pepper . ':' . $password;
|
||||
# return hashed result + salt
|
||||
($salt, $hash) = passgen($password) or die;
|
||||
|
||||
# add user info and pw/salt to DB
|
||||
$self->schema->resultset('Users')->create({
|
||||
username => $username,
|
||||
email => $email,
|
||||
password => $hash,
|
||||
salt => $salt,
|
||||
signup_date => time }) or die;
|
||||
|
||||
$self->flash(message => 'User registered successfully!');
|
||||
$self->redirect_to('register')}
|
||||
catch ($catchError) {
|
||||
print $catchError;
|
||||
$self->flash(error => 'Your registration info was correct, but a
|
||||
server error prevented you from registering. This has been
|
||||
logged so the administrator can fix it.');
|
||||
$self->redirect_to('register')}}
|
||||
|
||||
1;
|
|
@ -1,14 +1,37 @@
|
|||
package CharmBoard::Crypt::Password;
|
||||
use utf8;
|
||||
use Authen::Passphrase::Argon2;
|
||||
use CharmBoard::Crypt::Seasoning;
|
||||
|
||||
use Exporter qw(import);
|
||||
our @EXPORT = qw(passgen passchk);
|
||||
|
||||
# subroutine to generate password salt + hashed pw on pass creation
|
||||
# outputs the salt and then the hashed pw, so when assigning vars
|
||||
# from this sub's output, do it like this:
|
||||
# `my ($salt, $hash) = passgen($password);`
|
||||
=pod
|
||||
=head1 NAME
|
||||
CharmBoard::Crypt::Password - password processing module
|
||||
=head1 SYNOPSIS
|
||||
=begin perl
|
||||
use CharmBoard::Crypt::Password;
|
||||
|
||||
($salt, $hash) = passgen($plaintextPassword);
|
||||
$passwordVerification = passchk($salt, $hash, $plaintextPassword)
|
||||
=end perl
|
||||
=head1 DESCRIPTION
|
||||
CharmBoard::Crypt::Password processes passwords, either processing
|
||||
new passwords for database storage, or checking passwords entered
|
||||
when logging in to make sure they're correct.
|
||||
|
||||
Currently the only available password hashing scheme is Argon2, but
|
||||
this might be changed later on.
|
||||
=cut
|
||||
|
||||
=pod
|
||||
=head2 passgen
|
||||
passgen is the function for generating password salts and hashes to
|
||||
be inserted into the database. It takes the plaintext password you
|
||||
wish to hash as the only argument, and outputs the salt and
|
||||
Argon2 hash string in hexadecimal form.
|
||||
=cut
|
||||
sub passgen ($) {
|
||||
my $argon2 = Authen::Passphrase::Argon2->new(
|
||||
salt => seasoning(32),
|
||||
|
@ -20,9 +43,17 @@ sub passgen ($) {
|
|||
|
||||
return ($argon2->salt_hex, $argon2->hash_hex)};
|
||||
|
||||
# subroutine to check inputted password against one in DB
|
||||
# `$_[0]` is the salt, `$_[1]` is the hashed pass, and
|
||||
# `$_[2]` is the inputted plaintext pepper:password to check
|
||||
=pod
|
||||
=head2 passchk
|
||||
passchk is the function for checking plaintext passwords against the
|
||||
hashed password + salt already stored in the database. It takes the
|
||||
salt and Argon2 hash string in hex form plus the plaintext password
|
||||
as inputs, and outputs a true/false value indicating whether or not
|
||||
the input password matched. Intended for login authentication or
|
||||
anywhere else where one may need to verify passwords (i.e. before
|
||||
changing existing passwords, or for admins confirming they wish to
|
||||
perform a risky or nonreversible operation.)
|
||||
=cut
|
||||
sub passchk ($$$) {
|
||||
my $argon2 = Authen::Passphrase::Argon2->new(
|
||||
salt_hex => $_[0],
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
package CharmBoard::Crypt::Seasoning;
|
||||
use utf8;
|
||||
use Math::Random::Secure qw(irand);
|
||||
|
||||
use Exporter qw(import);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
package CharmBoard::Schema;
|
||||
use base qw(DBIx::Class::Schema);
|
||||
|
||||
__PACKAGE__->load_namespaces();
|
||||
__PACKAGE__->load_namespaces(
|
||||
result_namespace => 'Source',
|
||||
resultset_namespace => 'Set');
|
||||
|
||||
1;
|
|
@ -1,4 +1,4 @@
|
|||
package CharmBoard::Schema::Result::Categories;
|
||||
package CharmBoard::Schema::Source::Categories;
|
||||
use base qw(DBIx::Class::Core);
|
||||
|
||||
__PACKAGE__->table('categories');
|
|
@ -1,22 +1,26 @@
|
|||
package CharmBoard::Schema::Result::Posts;
|
||||
package CharmBoard::Schema::Source::Posts;
|
||||
use base qw(DBIx::Class::Core);
|
||||
|
||||
__PACKAGE__->table('posts');
|
||||
__PACKAGE__->add_columns(
|
||||
post_id => {
|
||||
data_type => 'integer',
|
||||
is_foreign_key => 0,
|
||||
is_auto_increment => 1,
|
||||
is_nullable => 0, },
|
||||
user_id => {
|
||||
data_type => 'integer',
|
||||
is_foreign_key => 1,
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, },
|
||||
thread_id => {
|
||||
data_type => 'integer',
|
||||
is_foreign_key => 1,
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, },
|
||||
post_date => {
|
||||
data_type => 'integer',
|
||||
is_foreign_key => 0,
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, });
|
||||
|
||||
|
@ -24,11 +28,11 @@ __PACKAGE__->set_primary_key('post_id');
|
|||
|
||||
__PACKAGE__->belongs_to(
|
||||
user_id =>
|
||||
'CharmBoard::Schema::Result::Users',
|
||||
'CharmBoard::Schema::Source::Users',
|
||||
'user_id' );
|
||||
__PACKAGE__->belongs_to(
|
||||
thread_id =>
|
||||
'CharmBoard::Schema::Result::Threads',
|
||||
'CharmBoard::Schema::Source::Threads',
|
||||
'thread_id' );
|
||||
|
||||
1
|
|
@ -1,4 +1,4 @@
|
|||
package CharmBoard::Schema::Result::Session;
|
||||
package CharmBoard::Schema::Source::Session;
|
||||
use base qw(DBIx::Class::Core);
|
||||
|
||||
__PACKAGE__->table('sessions');
|
||||
|
@ -28,7 +28,7 @@ __PACKAGE__->set_primary_key('session_key');
|
|||
|
||||
__PACKAGE__->belongs_to(
|
||||
user_id =>
|
||||
'CharmBoard::Schema::Result::Users',
|
||||
'CharmBoard::Schema::Source::Users',
|
||||
'user_id');
|
||||
|
||||
1
|
|
@ -1,4 +1,4 @@
|
|||
package CharmBoard::Schema::Result::Subforums;
|
||||
package CharmBoard::Schema::Source::Subforums;
|
||||
use base qw(DBIx::Class::Core);
|
||||
|
||||
__PACKAGE__->table('subforums');
|
||||
|
@ -9,6 +9,7 @@ __PACKAGE__->add_columns(
|
|||
is_nullable => 0, },
|
||||
subf_cat => {
|
||||
data_type => 'integer',
|
||||
is_foreign_key => 1,
|
||||
is_auto_increment => 0,
|
||||
is_nullable => 0, },
|
||||
subf_name => {
|
||||
|
@ -24,7 +25,7 @@ __PACKAGE__->set_primary_key('subf_id');
|
|||
|
||||
__PACKAGE__->belongs_to(
|
||||
subf_cat =>
|
||||
'CharmBoard::Schema::Result::Categories',
|
||||
'CharmBoard::Schema::Source::Categories',
|
||||
{'foreign.cat_id' => 'self.subf_cat'});
|
||||
|
||||
1
|
|
@ -1,4 +1,4 @@
|
|||
package CharmBoard::Schema::Result::Threads;
|
||||
package CharmBoard::Schema::Source::Threads;
|
||||
use base qw(DBIx::Class::Core);
|
||||
|
||||
__PACKAGE__->table('threads');
|
||||
|
@ -12,13 +12,16 @@ __PACKAGE__->add_columns(
|
|||
is_nullable => 0, },
|
||||
thread_subf => {
|
||||
data_type => 'integer',
|
||||
is_foreign_key => 1,
|
||||
is_nullable => 1, });
|
||||
# ! thread_subf should NOT be nullable once subforums
|
||||
# ! are properly implemented
|
||||
|
||||
__PACKAGE__->set_primary_key('thread_id');
|
||||
|
||||
__PACKAGE__->belongs_to(
|
||||
thread_subf =>
|
||||
'CharmBoard::Schema::Result::Subforums',
|
||||
'CharmBoard::Schema::Source::Subforums',
|
||||
{'foreign.subf_id' => 'self.thread_subf'});
|
||||
|
||||
1
|
|
@ -1,4 +1,5 @@
|
|||
package CharmBoard::Schema::Result::Users;
|
||||
package CharmBoard::Schema::Source::Users;
|
||||
use utf8;
|
||||
use base qw(DBIx::Class::Core);
|
||||
|
||||
__PACKAGE__->table('users');
|
|
@ -1,4 +1,2 @@
|
|||
% layout 'default', title => 'CharmBoard';
|
||||
% my $hpm = "you are not logged in";
|
||||
% if ($self->session('is_auth')) {$hpm = "you're logged in!"};
|
||||
<%= $hpm %>
|
||||
% layout 'default', title => $self->boardName;
|
||||
this is the index page
|
|
@ -1,2 +0,0 @@
|
|||
<a href="/"><h2>CharmBoard</h2></a>
|
||||
<a href="/login">login</a> | <a href="/register">register</a><br /><br />
|
|
@ -5,8 +5,8 @@
|
|||
<title><%= title %></title>
|
||||
</head>
|
||||
<body>
|
||||
%= include 'layouts/_header'
|
||||
%= include 'layouts/default/_header'
|
||||
<%= content %>
|
||||
%= include 'layouts/_footer'
|
||||
%= include 'layouts/default/_footer'
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,14 @@
|
|||
<%
|
||||
my $userControls;
|
||||
|
||||
# TODO: once implemented, put username + profile link first
|
||||
if ($self->session('is_auth') == 1) {
|
||||
my $username =
|
||||
$userControls = "<a href=\"/logout\">logout</a>"}
|
||||
else {
|
||||
$userControls =
|
||||
"<a href=\"/login\">login</a> |
|
||||
<a href=\"/register\">register</a>"};
|
||||
%>
|
||||
<a href="/"><h2><%== $self->boardName %></h2></a>
|
||||
<%== $userControls %><br /><br />
|
|
@ -1,4 +1,4 @@
|
|||
% layout 'default', title => 'CharmBoard - Login';
|
||||
% layout 'default', title => $self->boardName . ' - Login';
|
||||
% if ($error) {
|
||||
<p style="color: red"><%= $error %></p>
|
||||
%};
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
% layout 'default', title => 'CharmBoard - Registration';
|
||||
% layout 'default', title => $self->boardName . ' - Registration';
|
||||
% if ($error) {
|
||||
<p style="color: red"><%= $error %></p>
|
||||
%};
|
||||
|
|
Loading…
Reference in New Issue