Compare commits

...

18 Commits

13 changed files with 250 additions and 43 deletions

View File

@ -61,7 +61,9 @@ tests locally:
## TODOs
- Stop working on this and start the imageboard
- Do the if-markdown-else-plaintext stuff in templates
- Fix preview tests
- Do I need `SUM` for `by_id()`?
## AGPL-3.0+ANTIFA compliance

View File

@ -91,7 +91,7 @@ sub startup($self) {
# Finish configuring some things
$self->secrets($self->config->{'secrets'}) || die $@;
$self->pg->migrations->from_dir('migrations')->migrate(15);
$self->pg->migrations->from_dir('migrations')->migrate(16);
if (my $threads_per_page = $self->config->{'threads_per_page'}) {
$self->thread->per_page($threads_per_page)

View File

@ -32,10 +32,11 @@ sub create($self) {
$v = $self->validation if $self->req->method eq 'POST';
if ($v && $v->has_data) {
$v->required('author' )->size(1, 63);
$v->required('body' )->size(2, $body_limit);
$v->optional('bump' );
$v->optional('preview');
$v->required('author' )->size(1, 63);
$v->required('body' )->size(2, $body_limit);
$v->optional('bump' );
$v->optional('preview' );
$v->optional('markdown');
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
@ -48,10 +49,11 @@ sub create($self) {
$self->stash(status => 400)
}
else {
my $remark_author = $v->param('author' );
my $remark_body = $v->param('body' );
my $bump_thread = $v->param('bump' );
my $preview = $v->param('preview');
my $remark_author = $v->param('author' );
my $remark_body = $v->param('body' );
my $bump_thread = $v->param('bump' );
my $preview = $v->param('preview' );
my $markdown = $v->param('markdown');
$self->session(author => $remark_author);
@ -59,7 +61,8 @@ sub create($self) {
$self->remark->create(
$thread_id,
$remark_author,
$remark_body
$remark_body,
$markdown
);
$self->thread->bump($thread_id) if $bump_thread;
@ -70,7 +73,7 @@ sub create($self) {
})->fragment('remarks'));
}
$draft = $remark_body;
$draft = {body => $remark_body, markdown => $markdown};
}
}

View File

@ -11,10 +11,11 @@ sub create($self) {
$v = $self->validation if $self->req->method eq 'POST';
if ($v && $v->has_data) {
$v->required('author' )->size(1, 63);
$v->required('title' )->size(1, 127);
$v->required('body' )->size(2, $body_limit);
$v->optional('preview');
$v->required('author' )->size(1, 63);
$v->required('title' )->size(1, 127);
$v->required('body' )->size(2, $body_limit);
$v->optional('preview' );
$v->optional('markdown');
$v->csrf_protect;
if ($v->has_error('csrf_token')) {
@ -27,10 +28,11 @@ sub create($self) {
$self->stash(status => 400)
}
else {
my $thread_author = $v->param('author' );
my $thread_title = $v->param('title' );
my $thread_body = $v->param('body' );
my $preview = $v->param('preview');
my $thread_author = $v->param('author' );
my $thread_title = $v->param('title' );
my $thread_body = $v->param('body' );
my $preview = $v->param('preview' );
my $markdown = $v->param('markdown');
$self->session(author => $thread_author);
@ -38,7 +40,8 @@ sub create($self) {
my $new_thread_id = $self->thread->create(
$thread_author,
$thread_title,
$thread_body
$thread_body,
$markdown
);
return $self->redirect_to(
@ -46,7 +49,7 @@ sub create($self) {
);
}
$draft = $thread_body;
$draft = {body => $thread_body, markdown => $markdown};
}
}

View File

@ -18,7 +18,8 @@ sub by_page_for($self, $thread_id, $this_page = 1) {
SELECT remark_id AS id,
TO_CHAR(remark_date, ?) AS date,
remark_author AS author,
remark_body AS body
remark_body AS body,
markdown_status AS markdown
FROM remarks
WHERE thread_id = ?
AND NOT hidden_status
@ -27,9 +28,25 @@ sub by_page_for($self, $thread_id, $this_page = 1) {
END_SQL
}
sub create($self, $thread_id, $author, $body, $hidden = 0, $flagged = 0) {
sub create(
$self,
$thread_id,
$author,
$body,
$markdown //= 0,
$hidden = 0,
$flagged = 0
)
{
my $clean_body = $self->hr->process($body);
my @data = ($thread_id, $author, $clean_body, $hidden, $flagged);
my @data = (
$thread_id,
$author,
$clean_body,
$hidden,
$flagged,
$markdown
);
$self->pg->db->query(<<~'END_SQL', @data);
INSERT INTO remarks (
@ -37,9 +54,10 @@ sub create($self, $thread_id, $author, $body, $hidden = 0, $flagged = 0) {
remark_author,
remark_body,
hidden_status,
flagged_status
flagged_status,
markdown_status
)
VALUES (?, ?, ?, ?, ?);
VALUES (?, ?, ?, ?, ?, ?);
END_SQL
}
@ -69,7 +87,8 @@ sub last_for($self, $thread_id) {
SELECT remark_id AS id,
TO_CHAR(remark_date, ?) AS date,
remark_author AS author,
remark_body AS body
remark_body AS body,
markdown_status AS markdown
FROM remarks
WHERE thread_id = ?
ORDER BY remark_date
@ -85,7 +104,8 @@ sub by_id($self, $remark_id) {
TO_CHAR(remark_date, ?) AS date,
remark_author AS author,
remark_body AS body,
thread_id
thread_id,
markdown_status AS markdown
FROM remarks
WHERE remark_id = ?
AND NOT hidden_status;
@ -122,7 +142,8 @@ sub feed($self) {
$self->pg->db->query(<<~'END_SQL', $date_format)->hashes;
SELECT remark_id AS id,
TO_CHAR(remark_date, ?) AS date,
remark_body AS body
remark_body AS body,
markdown_status AS markdown
FROM remarks
WHERE NOT hidden_status
GROUP BY remark_id

View File

@ -8,9 +8,25 @@ has per_page => 5;
has date_format => 'Dy, FMDD Mon YYYY HH24:MI:SS TZHTZM';
sub create($self, $author, $title, $body, $hidden = 0, $flagged = 0) {
sub create(
$self,
$author,
$title,
$body,
$markdown //= 0,
$hidden = 0,
$flagged = 0
)
{
my $clean_body = $self->hr->process($body);
my @data = ($author, $title, $clean_body, $hidden, $flagged);
my @data = (
$author,
$title,
$clean_body,
$hidden,
$flagged,
$markdown
);
$self->pg->db->query(<<~'END_SQL', @data)->hash->{'thread_id'};
INSERT INTO threads (
@ -18,9 +34,10 @@ sub create($self, $author, $title, $body, $hidden = 0, $flagged = 0) {
thread_title,
thread_body,
hidden_status,
flagged_status
flagged_status,
markdown_status
)
VALUES (?, ?, ?, ?, ?)
VALUES (?, ?, ?, ?, ?, ?)
RETURNING thread_id;
END_SQL
# The indented heredoc got a little confused by this one...
@ -43,7 +60,8 @@ sub by_page($self, $this_page = 1) {
THEN 1
ELSE 0
END) AS remark_tally,
t.bump_tally AS bump_tally
t.bump_tally AS bump_tally,
t.markdown_status AS markdown
FROM threads AS t
LEFT JOIN remarks AS r
ON t.thread_id = r.thread_id
@ -87,7 +105,8 @@ sub by_id($self, $thread_id) {
t.thread_title AS title,
t.thread_body AS body,
COUNT(r.*) AS remark_tally,
t.bump_tally AS bump_tally
t.bump_tally AS bump_tally,
t.markdown_status AS markdown
FROM threads AS t
LEFT JOIN remarks AS r
ON t.thread_id = r.thread_id
@ -121,7 +140,8 @@ sub feed($self) {
SELECT thread_id AS id,
TO_CHAR(thread_date, ?) AS date,
thread_title AS title,
thread_body AS body
thread_body AS body,
markdown_status AS markdown
FROM threads
WHERE NOT hidden_status
GROUP BY thread_id

5
migrations/16/down.sql Normal file
View File

@ -0,0 +1,5 @@
ALTER TABLE threads
DROP COLUMN markdown_status;
ALTER TABLE remarks
DROP COLUMN markdown_status;

23
migrations/16/up.sql Normal file
View File

@ -0,0 +1,23 @@
ALTER TABLE threads
ADD COLUMN markdown_status BOOLEAN;
-- Since Markdown was default, set existing threads to true
UPDATE threads
SET markdown_status = TRUE;
-- Now we can make it NOT NULL
ALTER TABLE threads
ALTER COLUMN markdown_status
SET NOT NULL;
-- Do the same for remarks
ALTER TABLE remarks
ADD COLUMN markdown_status BOOLEAN;
UPDATE remarks
SET markdown_status = TRUE;
ALTER TABLE remarks
ALTER COLUMN markdown_status
SET NOT NULL;

View File

@ -130,3 +130,9 @@
padding: 1em;
background-color: var(--transparent);
}
.plain-text {
white-space: pre-wrap;
display: inline-block;
margin: 1em 0;
}

98
t/markdown.t Normal file
View File

@ -0,0 +1,98 @@
use Mojo::Base -strict;
use Test::More;
use Test::Mojo;
my $t = Test::Mojo->new('PostText');
# For CAPTCHA
my %good_captcha = (answer => 1, number => '');
my $bump_thread_url =
'/captcha/H4sIAImTzmQAA8soKSmw0tfPyU9OzMnILy6xMjYwMNDPKM1NzNMvyShKTUzRTyrNLdA3BAD5ek7T%0AKQAAAA==%0A';
my %markdown_thread = (
author => 'Anonymous',
title => 'hi',
body => '# ayy... lmao',
preview => 1,
markdown => 1
);
my %markdown_remark = (
author => 'Anonymous',
body => '# ayy... lmao',
preview => 1,
markdown => 1
);
my %plain_text_thread = (
author => 'Anonymous',
title => 'hi',
body => '# ayy... lmao',
preview => 1
);
my %plain_text_remark = (
author => 'Anonymous',
body => '# ayy... lmao',
preview => 1
);
# 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});
subtest 'Check the form + button', sub {
$t->get_ok('/human/remark/post/1')->status_is(200)
->element_exists('input[id="markdown"]');
$t->get_ok('/human/thread/post')->status_is(200)
->element_exists('input[id="markdown"]');
};
subtest 'Submit markdown input', sub {
$t->get_ok('/human/remark/post/1');
$markdown_remark{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/human/remark/post/1', form => \%markdown_remark)
->status_is(200)
->text_like('div.form-preview h1', qr/ayy\.\.\. lmao/);
$t->get_ok('/human/thread/post');
$markdown_thread{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/human/thread/post', form => \%markdown_thread)
->status_is(200)
->text_like('div.form-preview h1', qr/ayy\.\.\. lmao/);
};
subtest 'Submit plain text input', sub {
$t->get_ok('/human/remark/post/1');
$plain_text_remark{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/human/remark/post/1', form => \%plain_text_remark)
->status_is(200)
->text_like('span.plain-text', qr/# ayy\.\.\. lmao/);
$t->get_ok('/human/thread/post');
$plain_text_thread{'csrf_token'} =
$t->tx->res->dom->at('input[name="csrf_token"]')->val;
$t->post_ok('/human/thread/post', form => \%plain_text_thread)
->status_is(200)
->text_like('span.plain-text', qr/# ayy\.\.\. lmao/);
};
done_testing;

View File

@ -14,9 +14,13 @@
content="Remark on thread #<%= $thread->{'id'} %>.">
<% end %>
<form method="post" class="form-body">
<% if ($draft) { =%>
<% if (keys %{$draft}) { =%>
<div class="form-preview">
<%== markdown $draft =%>
<% if ($draft->{'markdown'}) { =%>
<%== markdown $draft->{'body'} =%>
<% } else { =%>
<span class="plain-text"><%= $draft->{'body'} =%></span>
<% } =%>
</div>
<% } =%>
<div class="form-field">
@ -51,6 +55,10 @@
<%= check_box bump => 1, id => 'bump', checked => undef %>
<%= label_for bump => 'Bump' %>
</div>
<div class="form-checkbox">
<%= check_box markdown => 1, id => 'markdown' %>
<%= label_for markdown => 'Markdown' %>
</div>
<div class="form-checkbox">
<%= check_box preview => 1, id => 'preview' %>
<%= label_for preview => 'Preview' %>
@ -72,7 +80,7 @@
</h4>
<h5 class="post__author"><%= $last_remark->{'author'} %></h5>
<div class="post__body">
<%== markdown $last_remark->{'body'} =%>
<span class="plain-text"><%= $last_remark->{'body'} =%></span>
</div>
<nav class="post__nav">
<%= link_to Thread => single_thread =>
@ -98,7 +106,7 @@
<h4 class="post__date"><%= $thread->{'date'} %></h4>
<h5 class="post__author"><%= $thread->{'author'} %></h5>
<div class="post__body">
<%== markdown $thread->{'body'} =%>
<span class="plain-text"><%= $thread->{'body'} =%></span>
</div>
<nav class="post__nav">
<%= link_to post_remark => {thread_id => $thread->{'id'}},

View File

@ -28,11 +28,19 @@
<summary>
<%= truncate_text $thread->{'body'} %>
</summary>
<% if ($thread->{'markdown'}) { =%>
<%== markdown $thread->{'body'} =%>
<% } else { =%>
<span class="plain-text"><%= $thread->{'body'} %></span>
<% } =%>
</details>
<% } else { =%>
<div class="post__body">
<% if ($thread->{'markdown'}) { =%>
<%== markdown $thread->{'body'} =%>
<% } else { =%>
<span class="plain-text"><%= $thread->{'body'} %></span>
<% } =%>
</div>
<% } =%>
<nav class="post__nav">

View File

@ -11,9 +11,15 @@
% end
<h2 class="page-title"><%= title %></h2>
<form method="post" class="form-body">
<% if ($draft) { =%>
<% if (keys %{$draft}) { =%>
<div class="form-preview">
<%== markdown $draft =%>
<% if ($draft->{'markdown'}) { =%>
<%== markdown $draft->{'body'} =%>
<% } else { =%>
<span class="plain-text">
<%= $draft->{'body'} =%>
</span>
<% } =%>
</div>
<% } =%>
<div class="form-field">
@ -57,6 +63,10 @@
rows => 6
) %>
</div>
<div class="form-checkbox">
<%= check_box markdown => 1, id => 'markdown' %>
<%= label_for markdown => 'Markdown' %>
</div>
<div class="form-checkbox">
<%= check_box preview => 1, id => 'preview' %>
<%= label_for preview => 'Preview' %>