Compare commits

..

No commits in common. "main" and "slapbird" have entirely different histories.

36 changed files with 114 additions and 296 deletions

View File

@ -1,10 +1,11 @@
FROM docker.io/perl:5.40
FROM docker.io/perl:5.36
# Move it
WORKDIR /opt
COPY lib/ ./lib/
COPY migrations/ ./migrations/
COPY public/ ./public/
COPY t/ ./t/
COPY templates/ ./templates/
COPY script/ ./script/
COPY cpanfile .

View File

@ -61,7 +61,7 @@ tests locally:
## TODOs
- Stop working on this and start the imageboard
- Tripcodes/PGP signing somehow perhaps...
## AGPL-3.0+ANTIFA compliance

View File

@ -7,3 +7,4 @@ requires 'XML::RSS';
requires 'Text::Markdown';
requires 'HTML::Restrict';
requires 'Roman::Unicode';
requires 'SlapbirdAPM::Agent::Mojo';

View File

@ -4,7 +4,6 @@
remarks_per_page => 5,
results_per_page => 5,
body_max_length => 8_000,
version => 'Feb 11 2025',
secrets => ['t0p_s3cr3t'],
development => {
pg_string =>

View File

@ -19,6 +19,12 @@ sub startup($self) {
$self->plugin('Config');
$self->plugin('TagHelpers::Pagination');
# Alpha testing Slapbird APM
if (my $slapbirdapm_api_key = $self->config->{'slapbirdapm_api_key'}) {
$self->plugin('SlapbirdAPM', key => $slapbirdapm_api_key)
if $self->mode eq 'production'
}
# Helpers
$self->helper(pg => sub ($c) {
state $pg = Mojo::Pg->new($c->config->{$self->mode}{'pg_string'})
@ -30,8 +36,6 @@ sub startup($self) {
$self->helper(hr => sub ($c) {
state $hr = HTML::Restrict->new(
# filter_text breaks greater and less than symbols in Markdown code blocks
# Also breaks Markdown quote blocks
filter_text => 0,
strip_enclosed_content => [],
rules => {
@ -140,14 +144,7 @@ sub startup($self) {
$c->session(expires => 1);
$c->redirect_to('threads_list');
})->name('new_session');
# Hide a version string to check build later
if (my $version = $self->config->{'version'}) {
$r->get('/version', sub ($c) {
$c->render(text => $version . "\n")
})->name('version_string');
}
});
# Static pages
$r->get('/about')->to('page#about')->name('about_page');
@ -191,11 +188,6 @@ sub startup($self) {
->to('thread#flag')
->name('flag_thread');
# Redirect for this old path to the new one
$thread->any([qw{GET POST}], '/post', sub ($c) {
$c->redirect_to('post_thread')
});
$human_thread->any([qw{GET POST}], '/post')
->to('thread#create')
->name('post_thread');

View File

@ -39,15 +39,8 @@ sub login($self) {
if ($v && $v->has_data) {
$v->required('email' )->size(6, 320);
$v->required('password')->size(12, undef);
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
$self->stash(
status => 403,
error => 'Something went wrong, please try again. 🥺'
);
}
elsif ($v->has_error) {
if ($v->has_error) {
$self->stash(status => 400)
}
else {
@ -160,15 +153,8 @@ sub create($self) {
$v->required('name' )->size(1, 64);
$v->required('email' )->size(6, 320);
$v->required('password')->size(12, undef);
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
$self->stash(
status => 403,
error => 'Something went wrong, please try again. 🥺'
)
}
elsif ($v->has_error) {
if ($v->has_error) {
$self->stash(status => 400)
}
else {
@ -192,15 +178,8 @@ sub admin_reset($self) {
if ($v && $v->has_data) {
$v->required('email' )->size(6, 320);
$v->required('password')->size(12, undef);
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
$self->stash(
status => 403,
error => 'Something went wrong, please try again. 🥺'
)
}
elsif ($v->has_error) {
if ($v->has_error) {
$self->stash(status => 400)
}
else {
@ -222,15 +201,8 @@ sub mod_reset($self) {
if ($v && $v->has_data) {
$v->required('password')->size(12, undef);
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
$self->stash(
status => 403,
error => 'Something went wrong, please try again. 🥺'
)
}
elsif ($v->has_error) {
if ($v->has_error) {
$self->stash(status => 400)
}
else {
@ -254,15 +226,8 @@ sub lock_acct($self) {
if ($v && $v->has_data) {
$v->required('email')->size(6, 320);
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
$self->stash(
status => 403,
error => 'Something went wrong, please try again. 🥺'
)
}
elsif ($v->has_error) {
if ($v->has_error) {
$self->stash(status => 400)
}
else {
@ -283,15 +248,8 @@ sub unlock_acct($self) {
if ($v && $v->has_data) {
$v->required('email')->size(6, 320);
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
$self->stash(
status => 403,
error => 'Something went wrong, please try again. 🥺'
)
}
elsif ($v->has_error) {
if ($v->has_error) {
$self->stash(status => 400)
}
else {
@ -312,15 +270,8 @@ sub promote($self) {
if ($v && $v->has_data) {
$v->required('email')->size(6, 320);
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
$self->stash(
status => 403,
error => 'Something went wrong, please try again. 🥺'
)
}
elsif ($v->has_error) {
if ($v->has_error) {
$self->stash(status => 400)
}
else {
@ -341,15 +292,8 @@ sub demote($self) {
if ($v && $v->has_data) {
$v->required('email')->size(6, 320);
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
$self->stash(
status => 403,
error => 'Something went wrong, please try again. 🥺'
)
}
elsif ($v->has_error) {
if ($v->has_error) {
$self->stash(status => 400)
}
else {

View File

@ -20,15 +20,8 @@ sub captcha($self) {
if ($v && $v->has_data) {
$v->required('answer')->num(1, 9);
$v->required('number')->size(1, 4);
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
$self->stash(
status => 403,
error => 'Something went wrong, please try again. 🥺'
)
}
elsif ($v->has_error) {
if ($v->has_error) {
$self->stash(status => 400)
}
else {

View File

@ -36,15 +36,8 @@ sub create($self) {
$v->required('body' )->size(2, $body_limit);
$v->optional('bump' );
$v->optional('preview');
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
$self->stash(
status => 403,
error => 'Something went wrong, please try again. 🥺'
)
}
elsif ($v->has_error) {
if ($v->has_error) {
$self->stash(status => 400)
}
else {

View File

@ -15,15 +15,8 @@ sub create($self) {
$v->required('title' )->size(1, 127);
$v->required('body' )->size(2, $body_limit);
$v->optional('preview');
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
$self->stash(
status => 403,
error => 'Something went wrong, please try again. 🥺'
);
}
elsif ($v->has_error) {
if ($v->has_error) {
$self->stash(status => 400)
}
else {

View File

@ -156,8 +156,7 @@ sub flagged($self) {
SELECT 'remark',
remark_id
FROM remarks
WHERE flagged_status
ORDER BY id DESC;
WHERE flagged_status;
END_SQL
}
@ -171,8 +170,7 @@ sub hidden($self) {
SELECT 'remark',
remark_id
FROM remarks
WHERE hidden_status
ORDER BY id DESC;
WHERE hidden_status;
END_SQL
}
@ -272,8 +270,7 @@ sub list($self) {
TO_CHAR(last_login_date, $1) AS last_login_date,
lock_status,
admin_status
FROM moderators
ORDER BY last_login_date DESC;
FROM moderators;
END_SQL
}

View File

@ -73,6 +73,7 @@ table { width: 100%; }
table, th, td {
border-collapse: collapse;
border: 0.15em dotted black;
text-align: center;
}

View File

@ -1,6 +1,8 @@
(function () {
const bodyStyle = document.body.style;
function setImage(url) {
document.body.style.backgroundImage = "url('" + url + "')";
bodyStyle.backgroundImage = "url('" + url + "')";
}
switch (new Date().getMonth()) {

View File

@ -3,7 +3,8 @@
# PostText v0.1
# Jul 22 2022
use v5.40;
use v5.36;
#no warnings 'experimental';
use Mojo::File qw{curfile};
use lib curfile->dirname->sibling('lib')->to_string;
use Mojolicious::Commands;

View File

@ -10,11 +10,6 @@ my %valid_login = (
);
subtest Login => sub {
$t->get_ok('/login');
$valid_login{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/login', form => \%valid_login)
->status_is(302)
->header_like(Location => qr{moderator/flagged});
@ -36,10 +31,9 @@ subtest Login => sub {
$t->get_ok('/moderator/admin/create')
->status_is(200)
->text_like(h2 => qr/Create Moderator/)
->element_exists('form input[name="name"]' )
->element_exists('form input[name="email"]' )
->element_exists('form input[name="password"]' )
->element_exists('form input[name="csrf_token"]')
->element_exists('form input[name="name"]' )
->element_exists('form input[name="email"]' )
->element_exists('form input[name="password"]')
};
subtest Reset => sub {
@ -49,7 +43,6 @@ subtest Login => sub {
->element_exists('a[href*="/moderator/admin/reset"]')
->element_exists('form input[name="email"]' )
->element_exists('form input[name="password"]' )
->element_exists('form input[name="csrf_token"]' )
};
subtest Lock => sub {
@ -58,7 +51,6 @@ subtest Login => sub {
->text_like(h2 => qr/Lock Account/)
->element_exists('a[href*="/moderator/admin/lock"]')
->element_exists('form input[name="email"]' )
->element_exists('form input[name="csrf_token"]' )
};
subtest Unlock => sub {
@ -67,7 +59,6 @@ subtest Login => sub {
->text_like(h2 => qr/Unlock Account/)
->element_exists('a[href*="/moderator/admin/unlock"]')
->element_exists('form input[name="email"]' )
->element_exists('form input[name="csrf_token"]' )
};
subtest Promote => sub {
@ -76,7 +67,6 @@ subtest Login => sub {
->text_like(h2 => qr/Promote Moderator/)
->element_exists('a[href*="/moderator/admin/promote"]')
->element_exists('form input[name="email"]' )
->element_exists('form input[name="csrf_token"]' )
};
subtest Demote => sub {
@ -85,7 +75,6 @@ subtest Login => sub {
->text_like(h2 => qr/Demote Admin/)
->element_exists('a[href*="/moderator/admin/demote"]')
->element_exists('form input[name="email"]' )
->element_exists('form input[name="csrf_token"]' )
};
# Admin session ends

View File

@ -25,39 +25,18 @@ subtest 'Bumping thread', sub {
$t->get_ok('/human/thread/bump/1')->status_is(302)
->header_like(Location => qr/captcha/);
$t->get_ok($bump_thread_url)
->status_is(200)
->element_exists('input[name="answer"]' )
->element_exists('input[name="number"]' )
->element_exists('input[name="csrf_token"]');
# Bad CSRF
$t->post_ok($bump_thread_url, form => \%bad_bot)
->status_is(403)
->element_exists('p[class="stash-with-error"]')
->text_like(p => qr/Something went wrong/);
# Bad CAPTCHA
$bad_bot{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($bump_thread_url, form => \%bad_bot)
->status_is(400)
->element_exists('p[class="stash-with-error"]')
->text_like(p => qr/Sounds like something a robot would say/);
$invalid_captcha{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($bump_thread_url, form => \%invalid_captcha)
->status_is(400)
->element_exists('p[class="field-with-error"]')
->text_like(p => qr/Should be a single number/);
# Solved CAPTCHA
$good_human{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($bump_thread_url, form => \%good_human)
->status_is(302)
->header_like(Location => qr{human/thread/bump/1});
@ -77,35 +56,18 @@ subtest 'Flagging thread', sub {
$t->get_ok('/human/thread/flag/1')->status_is(302)
->header_like(Location => qr/captcha/);
# Bad CSRF
$t->get_ok($flag_thread_url);
$t->post_ok($flag_thread_url, form => \%bad_bot)
->status_is(403)
->element_exists('p[class="stash-with-error"]')
->text_like(p => qr/Something went wrong/);
# Bad CAPTCHA
$bad_bot{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($flag_thread_url, form => \%bad_bot)
->status_is(400)
->element_exists('p[class="stash-with-error"]')
->text_like(p => qr/Sounds like something a robot would say/);
$invalid_captcha{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($flag_thread_url, form => \%invalid_captcha)
->status_is(400)
->element_exists('p[class="field-with-error"]')
->text_like(p => qr/Should be a single number/);
# Solved CAPTCHA
$good_human{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($flag_thread_url, form => \%good_human)
->status_is(302)
->header_like(Location => qr{human/thread/flag/1});
@ -121,35 +83,18 @@ subtest 'Flagging remark', sub {
$t->get_ok('/human/remark/flag/1')->status_is(302)
->header_like(Location => qr/captcha/);
# Bad CSRF
$t->get_ok($flag_remark_url);
$t->post_ok($flag_remark_url, form => \%bad_bot)
->status_is(403)
->element_exists('p[class="stash-with-error"]')
->text_like(p => qr/Something went wrong/);
# Bad CAPTCHA
$bad_bot{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($flag_remark_url, form => \%bad_bot)
->status_is(400)
->element_exists('p[class="stash-with-error"]')
->text_like(p => qr/Sounds like something a robot would say/);
$invalid_captcha{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($flag_remark_url, form => \%invalid_captcha)
->status_is(400)
->element_exists('p[class="field-with-error"]')
->text_like(p => qr/Should be a single number/);
# Solved CAPTCHA
$good_human{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($flag_remark_url, form => \%good_human)
->status_is(302)
->header_like(Location => qr{human/remark/flag/1});

View File

@ -17,28 +17,16 @@ my %invalid_login = (
subtest Login => sub {
$t->get_ok('/login')
->status_is(200)
->element_exists('form input[name="email"]' )
->element_exists('form input[name="password"]' )
->element_exists('form input[name="csrf_token"]')
->element_exists('form input[name="email"]')
->element_exists('form input[name="password"]')
->text_like(h2 => qr/Moderator Login/);
# Bad CSRF token
$t->post_ok('/login', form => \%valid_login)
->status_is(403)
->text_like(p => qr/Something went wrong/);
$invalid_login{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/login', form => \%invalid_login)
->status_is(403)
->element_exists('form input[name="email"]')
->element_exists('form input[name="password"]')
->text_like(p => qr/Invalid login/);
$valid_login{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/login', form => \%valid_login)
->status_is(302)
->header_like(Location => qr{moderator/flagged});
@ -87,13 +75,13 @@ subtest Login => sub {
};
subtest 'Buttons for mods', sub {
$t->get_ok('/moderator/thread/single/1')
$t->get_ok('/thread/single/1')
->status_is(200)
->element_exists('a[href*="/hide/1"]' )
->element_exists('a[href*="/unhide/1"]')
->element_exists('a[href*="/unflag/1"]');
$t->get_ok('/moderator/remark/single/1')
$t->get_ok('/remark/single/1')
->status_is(200)
->element_exists('a[href*="/hide/1"]' )
->element_exists('a[href*="/unhide/1"]')

View File

@ -44,11 +44,6 @@ $t->get_ok('/human/remark/post/1')->status_is(302)
->header_like(Location => qr/captcha/);
# Do CAPTCHA
$t->get_ok($bump_thread_url);
$good_captcha{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($bump_thread_url, form => \%good_captcha)
->status_is(302)
->header_like(Location => qr{human/thread/bump/1});
@ -58,11 +53,10 @@ $t->ua->max_redirects(1);
subtest 'Post new thread', sub {
# GET
$t->get_ok('/human/thread/post')->status_is(200)
->element_exists('form input[name="author"]' )
->element_exists('form input[name="title"]' )
->element_exists('form textarea[name="body"]' )
->element_exists('form button[type="submit"]' )
->element_exists('form input[name="csrf_token"]')
->element_exists('form input[name="author"]' )
->element_exists('form input[name="title"]' )
->element_exists('form textarea[name="body"]')
->element_exists('form button[type="submit"]' )
->text_like(h2 => qr/New Thread/);
# POST
@ -70,31 +64,17 @@ subtest 'Post new thread', sub {
->element_exists('form input[name="author"]' )
->element_exists('form input[name="title"]' )
->element_exists('form textarea[name="body"]')
->element_exists('form button[type="submit"]')
->element_exists('form button[type="submit"]' )
->text_like(h2 => qr/New Thread/);
# No CSRF token
$t->post_ok('/human/thread/post', form => \%valid_thread)
->status_is(403)
->text_like(p => qr/Something went wrong/);
$invalid_title{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/human/thread/post', form => \%invalid_title)
->status_is(400)
->text_like(p => qr/Must be between/);
$invalid_thread{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/human/thread/post', form => \%invalid_thread)
->status_is(400)
->text_like(p => qr/Must be between/);
$valid_thread{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/human/thread/post', form => \%valid_thread)
->status_is(200)
->text_like(h2 => qr/Thread #\d+/);
@ -105,44 +85,29 @@ subtest 'Post new remark', sub {
$t->get_ok('/human/remark/post/1')->status_is(200)
->element_exists('form input[name="author"]' )
->element_exists('form textarea[name="body"]')
->element_exists('form button[type="submit"]')
->element_exists('form button[type="submit"]' )
->text_like(h2 => qr/Remark on Thread #/);
$t->get_ok('/human/remark/post/65536')->status_is(404)
->text_like(p => qr/Thread not found/);
# Test the remark-to-remark thing
$t->get_ok('/human/remark/post/1/1')->status_is(200)
->element_exists('form input[name="author"]' )
->element_exists('form textarea[name="body"]' )
->element_exists('form input[name="author"]' )
->element_exists('form textarea[name="body"]')
->element_exists('form button[type="submit"]' )
->element_exists('a[href$="/remark/single/1"]')
->text_like(h3 => qr/Last Remark/);
# POST
$t->post_ok('/human/remark/post/1')->status_is(200)
->element_exists('form input[name="author"]' )
->element_exists('form textarea[name="body"]' )
->element_exists('form input[name="author"]' )
->element_exists('form textarea[name="body"]')
->element_exists('form button[type="submit"]' )
->text_like(h2 => qr/Remark on Thread #/);
# No CSRF token
$t->post_ok('/human/remark/post/1', form => \%valid_remark)
->status_is(403)
->text_like(p => qr/Something went wrong/);
$t->get_ok('/human/remark/post/1');
$valid_remark{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/human/remark/post/1', form => \%valid_remark)
->status_is(200)
->text_like(h2 => qr/Thread #1/);
$t->get_ok('/human/remark/post/1');
$invalid_remark{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/human/remark/post/1', form => \%invalid_remark)
->status_is(400)
->text_like(p => qr/Must be between/);

View File

@ -23,11 +23,6 @@ my %preview_remark = (
);
# Do CAPTCHA
$t->get_ok($bump_thread_url);
$good_captcha{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($bump_thread_url, form => \%good_captcha)
->status_is(302)
->header_like(Location => qr{human/thread/bump/1});
@ -41,20 +36,10 @@ subtest 'Check the form + button', sub {
};
subtest 'Submit input', sub {
$t->get_ok('/human/remark/post/1');
$preview_remark{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/human/remark/post/1', form => \%preview_remark)
->status_is(200)
->text_like(p => qr/ayy\.\.\. lmao/);
$t->get_ok('/human/thread/post');
$preview_thread{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/human/thread/post', form => \%preview_thread)
->status_is(200)
->text_like(p => qr/ayy\.\.\. lmao/);

View File

@ -1,10 +0,0 @@
use Mojo::Base -strict;
use Test::More;
use Test::Mojo;
my $t = Test::Mojo->new('PostText');
$t->get_ok('/thread/post')->status_is(302)
->header_like(Location => qr{human/thread/post});
done_testing;

View File

@ -8,23 +8,12 @@ my %good_human = (answer => 1, number => '');
my $search_url =
'/captcha/H4sIABJ8PGUAA8soKSmw0tfPyU9OzMnILy6xMjYwMNDPKM1NzNMvTk0sSs4AAPrUR3kiAAAA%0A';
subtest 'Search form', sub {
$t->get_ok('/thread/list')
->element_exists('form input[name="q"]' )
->element_exists('form button[type="submit"]');
};
subtest 'Search before CAPTCHA', sub {
$t->get_ok('/human/search')->status_is(302)
->header_like(Location => qr/captcha/);
};
subtest 'Search after CAPTCHA', sub {
$t->get_ok($search_url);
$good_human{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok($search_url, form => \%good_human)
->status_is(302)
->header_like(Location => qr{human/search});

View File

@ -93,9 +93,6 @@
<%= content =%>
<footer class="site-footer">
<p>In UTF-8 we trust. 🫡</p>
<p><%= link_to new_session => begin %>
New Session/Identity
<% end %></p>
<p><%= link_to javascript_page =>
('data-jslicense', 1),
begin %>JavaScript License Information<% end %></p>

View File

@ -18,6 +18,5 @@
<%= label_for password => 'Password' %>
<%= password_field password => (id => 'password') %>
</div>
<%= csrf_field %>
<button type="submit" class="form-button">Reset</button>
</form>

View File

@ -26,6 +26,5 @@
<%= label_for password => 'Password' %>
<%= password_field password => (id => 'password') %>
</div>
<%= csrf_field %>
<button type="submit" class="form-button">Create</button>
</form>

View File

@ -10,6 +10,5 @@
<%= label_for email => 'Email' %>
<%= email_field email => (id => 'email', autofocus => undef) %>
</div>
<%= csrf_field %>
<button type="submit" class="form-button">Demote</button>
</form>

View File

@ -10,6 +10,5 @@
<%= label_for email => 'Email' %>
<%= email_field email => (id => 'email', autofocus => undef) %>
</div>
<%= csrf_field %>
<button type="submit" class="form-button">Lock</button>
</form>

View File

@ -12,12 +12,11 @@
</div>
<div class="form-field">
<% if (my $error = validation->error('password')) { =%>
<p class="field-with-error">Must be atleast <%= $error->[2] %>
characters.</p>
<p class="field-with-error">Must be between <%= $error->[2] %>
and <%= $error->[3] %> characters.</p>
<% } =%>
<%= label_for password => 'Password' %>
<%= password_field password => (id => 'password') %>
</div>
<%= csrf_field %>
<button type="submit" class="form-button">Login</button>
</form>

View File

@ -10,7 +10,6 @@
<%= label_for password => 'Password' %>
<%= password_field password => (id => 'password', autofocus => undef) %>
</div>
<%= csrf_field %>
<button type="submit" class="form-button">Reset</button>
</form>

View File

@ -10,6 +10,5 @@
<%= label_for email => 'Email' %>
<%= email_field email => (id => 'email', autofocus => undef) %>
</div>
<%= csrf_field %>
<button type="submit" class="form-button">Promote</button>
</form>

View File

@ -12,6 +12,15 @@
<div class="post__body">
<%== markdown $remark->{'body'} =%>
</div>
<nav class="post__nav">
<%= link_to Thread => single_thread =>
{thread_id => $remark->{'thread_id'}}, (class => 'click') %>
<%= link_to Remark => post_remark =>
{thread_id => $remark->{'thread_id'}}, (class => 'click') %>
<%= link_to Flag => flag_remark => {remark_id => $remark->{'id'}},
(class => 'click') %>
</nav>
<% if (is_mod) { =%>
<nav class="post__nav">
<%= link_to Hide => hide_remark => {remark_id => $remark->{'id'}},
(class => 'click') %>
@ -20,6 +29,7 @@
<%= link_to Unflag => unflag_remark => {remark_id => $remark->{'id'}},
(class => 'click') %>
</nav>
<% } =%>
</article>
</main>
<% } =%>

View File

@ -13,6 +13,19 @@
<div class="post__body">
<%== markdown $thread->{'body'} =%>
</div>
<nav class="post__nav">
<%= link_to post_remark => {thread_id => $thread->{'id'}},
(class => 'click'), begin %>
Remark (<%= $thread->{'remark_tally'} %>)
<% end %>
<%= link_to bump_thread => {thread_id => $thread->{'id'}},
(class => 'click'), begin %>
Bump (<%= $thread->{'bump_tally'} %>)
<% end %>
<%= link_to Flag => flag_thread => {thread_id => $thread->{'id'}},
(class => 'click') %>
</nav>
<% if (is_mod) { =%>
<nav class="post__nav">
<%= link_to Hide => hide_thread => {thread_id => $thread->{'id'}},
(class => 'click') %>
@ -21,6 +34,7 @@
<%= link_to Unflag => unflag_thread => {thread_id => $thread->{'id'}},
(class => 'click') %>
</nav>
<% } =%>
</article>
</main>
<% } =%>

View File

@ -10,6 +10,5 @@
<%= label_for email => 'Email' %>
<%= email_field email => (id => 'email', autofocus => undef) %>
</div>
<%= csrf_field %>
<button type="submit" class="form-button">Unlock</button>
</form>

View File

@ -26,6 +26,5 @@
) %>
</div>
<%= hidden_field number => $roman_numeral, id => 'number' %>
<%= csrf_field %>
<button type="submit" class="form-button">Answer</button>
</form>

View File

@ -36,6 +36,16 @@
<%= link_to Flag => flag_remark => {remark_id => $remark->{'id'}},
(class => 'click') %>
</nav>
<% if (is_mod) { =%>
<nav class="post__nav">
<%= link_to Hide => hide_remark => {remark_id => $remark->{'id'}},
(class => 'click') %>
<%= link_to Unhide => unhide_remark => {remark_id => $remark->{'id'}},
(class => 'click') %>
<%= link_to Unflag => unflag_remark => {remark_id => $remark->{'id'}},
(class => 'click') %>
</nav>
<% } =%>
</article>
</main>
<% } =%>

View File

@ -55,7 +55,6 @@
<%= check_box preview => 1, id => 'preview' %>
<%= label_for preview => 'Preview' %>
</div>
<%= csrf_field %>
<button type="submit" class="form-button">Post</button>
</form>
<%# Putting this first above the thread body (nested if, yucky sry) %>
@ -82,6 +81,16 @@
<%= link_to Flag => flag_remark => {remark_id => $last_remark->{'id'}},
(class => 'click') %>
</nav>
<% if (is_mod) { =%>
<nav class="post__nav">
<%= link_to Hide => hide_remark => {remark_id => $last_remark->{'id'}},
(class => 'click') %>
<%= link_to Unhide => unhide_remark => {remark_id =>
$last_remark->{'id'}}, (class => 'click') %>
<%= link_to Unflag => unflag_remark => {remark_id =>
$last_remark->{'id'}}, (class => 'click') %>
</nav>
<% } =%>
</article>
</section>
<% } =%><%# Close the last_remark 'if' %>
@ -112,6 +121,16 @@
<%= link_to Flag => flag_thread => {thread_id => $thread->{'id'}},
(class => 'click') %>
</nav>
<% if (is_mod) { =%>
<nav class="post__nav">
<%= link_to Hide => hide_thread => {thread_id => $thread->{'id'}},
(class => 'click') %>
<%= link_to Unhide => unhide_thread => {thread_id => $thread->{'id'}},
(class => 'click') %>
<%= link_to Unflag => unflag_thread => {thread_id => $thread->{'id'}},
(class => 'click') %>
</nav>
<% } =%>
</article>
</section>
<% } =%><%# Close the thread 'if' %>

View File

@ -38,6 +38,16 @@
<%= link_to Flag => flag_thread => {thread_id => $thread->{'id'}},
(class => 'click') %>
</nav>
<% if (is_mod) { =%>
<nav class="post__nav">
<%= link_to Hide => hide_thread => {thread_id => $thread->{'id'}},
(class => 'click') %>
<%= link_to Unhide => unhide_thread => {thread_id => $thread->{'id'}},
(class => 'click') %>
<%= link_to Unflag => unflag_thread => {thread_id => $thread->{'id'}},
(class => 'click') %>
</nav>
<% } =%>
</article>
</main>
<% } =%>

View File

@ -61,6 +61,5 @@
<%= check_box preview => 1, id => 'preview' %>
<%= label_for preview => 'Preview' %>
</div>
<%= csrf_field %>
<button type="submit" class="form-button">Post</button>
</form>