Skip to content
GitLab
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Adam Matoušek
BLO👏GÍ👏SEK
Commits
be1a95e4
Verified
Commit
be1a95e4
authored
Sep 17, 2022
by
Adam Matoušek
Browse files
Are these “small commits” in the room with us?
parent
4a2137b0
Changes
17
Hide whitespace changes
Inline
Side-by-side
gib/main
View file @
be1a95e4
...
...
@@ -33,9 +33,45 @@ out article dirs
dep articles/$(articles)
cmd gib.nochange /bin/mkdir -p $(articles)
# assemble comments (this must be quite high, because a comment index is generated)
for a $(articles)
out $(a)/comments.html
dep article dirs
dep $(sources:articles/$(a)/*.comment)
dep templates/comments.tt
cmd $(tool) mkcomments $(a)
# selected password for each article
for a $(articles)
out $(a)/password
dep $(a)/comments.html
use transient
out universal_passwords
dep $(srcdir)/articles/passwords
cmd $(tool) into $(out) sed s/^/*\t/ $(dep)
out _comment_passwords
dep $(articles)/password
dep universal_passwords
cmd $(tool) into $(out) cat $(dep)
add deployed-files $(out)
# index of valid comments for each article
for a $(articles)
out $(a)/comment_index
dep $(a)/comments.html
use transient
out _comment_index
dep $(articles)/comment_index
cmd $(tool) into $(out) cat $(dep)
add deployed-files $(out)
# page indices (used to generate index/archive pages)
out archives.gib
dep $(sources:articles/*/meta)
dep $(articles)/comment_index
cmd $(tool) build_index
out index
...
...
@@ -57,14 +93,6 @@ dep templates/nav.tt
dep index
cmd $(tool) mknav
# assemble comments
for a $(articles)
out $(a)/comments.html
dep article dirs
dep $(sources:articles/$(a)/*.comment)
dep templates/comments.tt
cmd $(tool) mkcomments $(a)
# TODO: md2html or sth
# copy ready-made (templated) .htmls
...
...
@@ -150,17 +178,33 @@ dep templates/archive.tt
cmd $(tool) mkarchive $(a)
add deployed-files $(out)
# link other resources
set resources style.css _comment.cgi
# other resources
set copied-resources robots.txt
set executables _comment.cgi
set minified-resources style.css blogisek.js
set extra-templated-pages comment-queued.html comment-accepted.html geoblocked.html
set resources $(copied-resources) $(minified-resources) $(extra-templated-pages)
add deployed-files $(resources)
for res $(resources)
for res $(
copied-
resources)
$(executables)
out $(res)
dep $(srcdir)/resources/$(res)
cmd /bin/ln -f $(dep) $(out)
add deployed-files $(resources) $(resized-images)
for res $(minified-resources)
out $(res)
dep $(srcdir)/resources/$(res)
cmd /usr/bin/minify -o $(out) $(dep)
set all $(articles-html) $(images-all) $(archives)/index.html $(resources)
for res $(extra-templated-pages)
out $(res)
dep resources/$(res)
dep nav.html
dep templates/page.tt
cmd $(tool) mkpage resources/$(res) $(out)
set all $(deployed-files) $(executables)
set install
for f $(deployed-files)
...
...
@@ -168,3 +212,10 @@ out install $(f)
dep $(f)
cmd /usr/bin/install -D -m 0644 $(dep) $(prefix)/$(f)
add install $(out)
for f $(executables)
out install $(f)
dep $(f)
cmd /usr/bin/install -D -m 0755 $(dep) $(prefix)/$(f)
add install $(out)
resources/_comment.cgi
View file @
be1a95e4
#!/usr/bin/perl
# TODO Rewrite. This is a dumpster fire.
# Expects a comment in the POST parameters
use
strict
;
...
...
@@ -8,10 +10,10 @@ use utf8;
use
open
qw/:std :encoding(utf-8)/
;
use
CGI::
Simple
;
use
File::
Slurper
qw/read_text/
;
use
Data::
Dumper
;
use
File::
Slurper
qw/read_text read_lines/
;
use
POSIX
'
strftime
';
use
File::
Temp
qw/tempfile/
;
use
Unicode::
Normalize
;
$
CGI::Simple::
POST_MAX
=
8192
;
# 8 Kib
$
CGI::Simple::
DISABLE_UPLOADS
=
1
;
...
...
@@ -25,17 +27,24 @@ my $dir = "_comments";
my
$suffix
=
"
.comment
";
my
$queue_limit
=
50
;
sub
errorpage
{
print
$q
->
header
(
@header
,
-
status
=>
$_
[
0
]
);
print_errorpage
(
$_
[
0
]
);
exit
0
;
# dbg
use
Data::
Dumper
;
print
Dumper
@_
;
}
unless
(
-
t
STDIN
||
(
$ENV
{
REQUEST_METHOD
}
//
''
)
eq
'
POST
'
)
{
my
$status
=
'
405 Method Not Allowed
';
print
$q
->
header
(
@header
,
-
status
=>
$status
);
print_errorpage
(
$status
);
exit
0
;
exit
errorpage
(
'
405 Method Not Allowed
'
);
}
# When no 'article' parameter has been given, try to extract the
# article id (slug) from the URI
my
$article_slug
=
$ENV
{
REQUEST_URI
}
=~
s,/comment(\?.*)?$,,rn
;
$article_slug
=~
s,^(https?://[^/]*)?/,,n
;
my
%c
=
(
article
=>
scalar
$q
->
param
(
'
article
'
)
//
$article_slug
,
author
=>
scalar
$q
->
param
(
'
author
'
),
...
...
@@ -47,27 +56,34 @@ my %c = (
unless
(
$c
{
article
}
&&
$c
{
article
}
=~
m,^[-/a-z0-9]+$,
&&
$c
{
author
}
&&
0
<
length
$c
{
author
}
<
64
&&
no_weird_chars
(
$c
{
author
}
)
&&
$c
{
body
}
&&
0
<
length
$c
{
body
}
<
8000
&&
no_weird_chars
(
$c
{
body
}
)
&&
(
!
$c
{
parent
}
||
$c
{
parent
}
=~
/^[0-9]{14}\w{4}$/
)
)
&&
(
!
$c
{
parent
}
||
$c
{
parent
}
=~
/^[0-9]{14}\w{4}$/
)
)
# TODO: other ID formats
{
my
$status
=
'
400 Bad Request
';
print
$q
->
header
(
@header
,
-
status
=>
$status
);
print_errorpage
(
$status
);
exit
0
;
exit
errorpage
(
'
400 Bad Request
'
);
}
my
$comment_index
=
load_index
(
'
_comment_index
'
);
my
$password_index
=
load_index
(
'
_comment_passwords
'
);
unless
(
$password_index
->
{
$c
{
article
}}
# comments are enabled
&&
(
!
$c
{
parent
}
||
$comment_index
->
{
$c
{
article
}}{
$c
{
parent
}}
)
)
# parent exists
{
exit
errorpage
(
'
400 Bad Request
'
);
}
sanitise
(
$c
{
author
}
);
sanitise
(
$c
{
password
}
);
my
$passed
=
$c
{
password
}
eq
'
strasnetajneheslo
';
# TODO
my
$password
=
password_normalise
(
$c
{
password
}
);
my
$passed
=
$password
&&
(
# index might contain empty password, if comments are enabled and no password is provided
$password_index
->
{
$c
{
article
}}{
$password
}
# article password
||
$password_index
->
{'
*
'}{
$password
}
# global passwords
);
# Deny if the queue is full, unless the password is correct
my
@ls
=
glob
(
"
$dir
/*
$suffix
"
);
if
(
!
$passed
&&
@ls
>=
$queue_limit
)
{
my
$status
=
'
503 Service unavailable
';
print
$q
->
header
(
@header
,
-
status
=>
$status
);
print_errorpage
(
$status
);
exit
0
;
exit
errorpage
(
'
503 Service unavailable
'
);
}
$c
{
parent
}
||=
'
root
';
# top-level comments
...
...
@@ -84,7 +100,7 @@ my ($fh, $filename) = tempfile( "${slug}XXXX", DIR => $dir, SUFFIX => $suffix );
my
$commentid
=
$filename
=~
s/^\Q$dir\E\/(.+)\Q$suffix\E$/$1/r
;
unless
(
-
t
STDIN
)
{
my
$gid
=
getgr
gid
"
adamat
";
my
$gid
=
getgr
nam
"
adamat
";
chown
-
1
,
$gid
,
$filename
;
chmod
0440
,
$filename
;
}
...
...
@@ -101,12 +117,13 @@ Parent: $c{parent}
$c{body}
EOF
binmode
$fh
,
'
:encoding(utf-8)
';
print
$fh
$comment
;
close
$fh
;
print
$q
->
header
(
-
status
=>
'
303 See Other
',
-
location
=>
'
/comment
ed.html
',
-
location
=>
$passed
?
'
/comment-accepted.html
'
:
'
/comment-queu
ed.html
',
);
exit
0
;
...
...
@@ -136,4 +153,24 @@ sub sanitise {
}
}
sub
password_normalise
{
$_
=
lc
NFKD
(
shift
);
# unicode normalisation, lowerspace
s/[^-a-z0-9 ]//g
;
return
$_
;
}
sub
load_index
{
my
@content
=
read_lines
(
shift
);
my
$normalise
=
shift
;
my
%index
;
foreach
(
@content
)
{
my
@fields
=
split
/ *\t */
;
next
unless
@fields
>=
2
;
# comment ids aren't any wierder than normalised passwords, anyway
my
$d
=
$normalise
?
password_normalise
(
$fields
[
1
]
)
:
$fields
[
1
];
$index
{
$fields
[
0
]}{
$d
}
=
1
;
}
return
\
%index
;
}
# vim: sts=4 et ts=8 sw=4
resources/blogisek.js
0 → 100644
View file @
be1a95e4
var
ttc
=
document
.
getElementById
(
'
tooltipcloser
'
);
var
dim
=
document
.
getElementById
(
'
dimmer
'
).
classList
;
ttc
.
onclick
=
(
ev
)
=>
{
let
ttip
=
ttc
.
ttip
;
ttip
&&
ttip
.
classList
.
remove
(
'
open
'
);
ttc
.
hidden
=
true
;
dim
.
remove
(
'
on
'
);
};
for
(
e
of
document
.
getElementsByClassName
(
'
was
'
)
)
{
e
.
dataset
.
tooltip
=
e
.
title
;
e
.
title
=
''
;
e
.
onclick
=
(
ev
)
=>
{
let
classes
=
ev
.
target
.
classList
;
let
wasopen
=
classes
.
contains
(
'
open
'
);
if
(
ttc
.
ttip
)
{
classes
.
remove
(
'
open
'
);
}
ttc
.
ttip
=
ev
.
target
;
classes
.
toggle
(
'
open
'
,
!
wasopen
);
ttc
.
hidden
=
wasopen
;
dim
.
toggle
(
'
on
'
,
!
wasopen
);
};
}
resources/comment-accepted.html
0 → 100644
View file @
be1a95e4
<div
class=
pane
>
<h1>
Komentář byl přijat.
</h1>
<p>
Vypadáte jako šunka
<sup
class=
was
title=
'Oproti konservě s lančmítem'
>
was
</sup>
,
takže s jeho zveřejněním nebudeme otálet a při příštím vygenerování webu
si ho po sobě konečně budete moci přečíst a zhrozit se nad překlepy.
</p>
<p>
Díky! Komentáře mám moc rád, a takové, které nemusím třídit, ještě radši.
</p>
</div>
resources/comment-queued.html
0 → 100644
View file @
be1a95e4
<div
class=
pane
>
<h1>
Komentář byl odeslán
</h1>
<p>
Nejsem si jist vaším člověčenstvím, takže si nějakou dobu pobude ve
frontě, než ručně ověřím, že se nejedná o konservu s lančmítem.
</p>
<p>
Díky za komentář! Pro co jiného bych ty články psal.
</p>
</div>
resources/geoblocked.html
0 → 100644
View file @
be1a95e4
<div
class=
pane
>
<h1>
Geoblocked :(
</h1>
<p>
Komentáře jsou povoleny pouze z počítačů, které se dle GeoIP nachází v Česku
nebo jiných zemích, kde mám zrovna kamarády.
</p><p>
Jistě, zamítání podle přibližné zeměpisné polohy je nepěkné, ale velkou část
spamu to efektivně odráží. A myslím, že si můžu dovolit předpokládat, že
moje nepočetná čtenářská základna tímto omezením dotčena nebude.
</p><p>
Pokud tím dotčeni jste – je mi to hrozně líto; stojím o vaše komentáře!
</p><p>
Pokud nevydržíte s komentováním do návratu do vlasti (a já vím, že je to
hrozná otrava), můžete mi komentář poslat e-mailem (me zavináč přezdívka
tečka dev) a rovnou vás můžu přidat do seznamu výjimek.
</p><p>
O text komentáře patrně nepřijdete, mělo by snad stačit stisknout
v prohlížeči tlačítko „Zpět“.
</p>
</div>
resources/robots.txt
0 → 100644
View file @
be1a95e4
User-agent: *
Disallow: /*.jpg$
Disallow: /*.jpeg$
resources/style.css
View file @
be1a95e4
...
...
@@ -4,9 +4,10 @@ html, body {
padding
:
0
;
font-family
:
serif
;
color
:
black
;
background-color
:
#d
dd
;
background-color
:
#d
fdcd7
;
font-size
:
12pt
;
-webkit-text-size-adjust
:
none
;
-moz-text-size-adjust
:
none
;
text-size-adjust
:
none
;
}
...
...
@@ -14,12 +15,20 @@ h1, h2, h3, h4, h5, h6 {
font-family
:
sans-serif
;
}
#comments
.reply
input
:not
(
:checked
)
+
label
,
#reply-root
:not
(
:checked
)
+
label
::after
,
a
{
color
:
#137
;
text-decoration
:
none
;
color
:
#138
;
}
a
.nocolor
{
color
:
inherit
;
}
#comments
.reply
input
:not
(
:checked
)
+
label
:hover
,
#reply-root
:not
(
:checked
)
+
label
:hover::after
,
a
:hover
{
text-decoration
:
underline
;
cursor
:
pointer
;
}
#sidebar
{
...
...
@@ -30,16 +39,19 @@ a:hover {
#sidebar
>
header
blockquote
{
display
:
none
;
}
#sidebar
>
header
h1
{
margin
:
0
;
/* only for tiny */
}
#comments
.quiz
em
,
#sidebar
a
{
color
:
#751
;
}
#navbutton
{
display
:
block
;
color
:
#13
8
;
color
:
#13
7
;
font-weight
:
bold
;
border
:
0.2em
solid
#13
8
;
border
:
0.2em
solid
#13
7
;
background-color
:
white
;
aspect-ratio
:
1
;
padding
:
0.5em
;
...
...
@@ -71,12 +83,9 @@ a:hover {
list-style
:
none
;
line-height
:
2
;
}
#navigation
a
{
color
:
#542
;
}
#navigation
li
small
{
color
:
#
876
;
font-size
:
100
%
;
color
:
#
333
;
font-size
:
92
%
;
}
#content
{
...
...
@@ -86,16 +95,19 @@ a:hover {
}
#content
>
main
>
article
{
#content
>
main
>
article
,
.pane
{
background-color
:
white
;
position
:
relative
;
}
#content
>
main
::before
{
#content
>
main
>
header
::before
{
content
:
""
;
display
:
table
;
}
#content
>
main
>
header
{
padding
:
0
1em
;
#content
>
main
>
header
,
#comments
>
h2
{
padding
:
0
1rem
;
}
#content
>
main
>
header
h1
{
line-height
:
initial
;
...
...
@@ -103,8 +115,7 @@ a:hover {
margin-top
:
0
;
/* only small */
}
main
+
footer
{
margin-top
:
1em
;
#content
>
footer
{
padding
:
1em
;
background-color
:
rgba
(
0
,
0
,
0
,
0.08
);
color
:
#333
;
...
...
@@ -120,14 +131,30 @@ article .banner img {
object-fit
:
cover
;
object-position
:
center
center
;
}
article
a
.banner
{
background-color
:
#446ab6
;
}
article
a
.banner
img
{
filter
:
contrast
(
80%
)
brightness
(
130%
)
grayscale
(
90%
)
opacity
(
80%
);
transition
:
filter
250ms
;
}
article
a
.banner
:hover
img
,
article
a
.banner
:focus
img
,
article
a
.banner
:active
img
{
filter
:
none
;
}
.article-text
header
+
footer
{
margin-top
:
-1em
;
}
.article-text
footer
ul
{
padding
:
0
;
font-size
:
0
;
}
.article-text
footer
li
{
font-size
:
1rem
;
font-family
:
sans-serif
;
display
:
block
;
float
:
left
;
display
:
inline
;
}
.article-text
footer
li
+
li
{
margin-left
:
1em
;
...
...
@@ -135,28 +162,21 @@ article .banner img {
border-left
:
0.1em
solid
#888
;
}
.article-text
>
p
,
.article-text
>
footer
,
.article-text
>
header
{
margin-left
:
1em
;
margin-right
:
1em
;
.pane
>
*,
.article-text
>
*
{
margin-left
:
1rem
;
margin-right
:
1rem
;
}
.article-text
>
p
{
text-align
:
justify
;
hyphens
:
auto
;
}
.article-text
footer
::after
{
content
:
""
;
display
:
block
;
clear
:
both
;
-moz-hyphens
:
auto
;
-webkit-hyphens
:
auto
;
}
.article-text
.readmore
a
{
display
:
block
;
padding
:
0.5em
1em
;
margin-top
:
2em
;
border-top
:
0.2em
dashed
#ddd
;
text-align
:
right
;
font-family
:
sans-serif
;
...
...
@@ -173,6 +193,188 @@ article .banner img {
margin-top
:
-1em
;
}
.article-text
.readmore
,
.article-text
#readmore
{
margin-left
:
0
;
margin-right
:
0
;
}
/* I hate margin collapsing very much. */
.article-text
::before
,
.article-text
::after
,
.pane
::before
,
.pane
::after
{
content
:
""
;
display
:
table
;
}
/* Tooltips */
sup
.was
{
font-style
:
italic
;
text-decoration
:
underline
dotted
;
color
:
#137
;
background-color
:
#ffedf0
;
}
.was
::before
{
content
:
attr
(
title
)
attr
(
data-tooltip
);
position
:
absolute
;
background-color
:
pink
;
color
:
black
;
font-size
:
0.9rem
;
font-style
:
initial
;
visibility
:
hidden
;
min-width
:
10em
;
right
:
0
;
width
:
100%
;
padding
:
0.5em
;
box-sizing
:
border-box
;
transform
:
translateY
(
-120%
);
z-index
:
100
;
opacity
:
0%
;
transition-duration
:
200ms
;
transition-property
:
opacity
transform
;
}
.was
:hover::before
,
.was.open
::before
{
visibility
:
visible
;
transform
:
translateY
(
-100%
);
opacity
:
100%
;
}
#tooltipcloser
,
#dimmer
{
position
:
fixed
;
left
:
0
;
top
:
0
;
width
:
100vw
;
height
:
100vh
;
-webkit-user-select
:
none
;
user-select
:
none
;
opacity
:
0%
;
}
#tooltipcloser
{
z-index
:
120
;
}
#dimmer
{
z-index
:
20
;
background-color
:
#03a
;
transition
:
opacity
150ms
,
visibility
300ms
;
visibility
:
hidden
;
}
#dimmer
.on
{
visibility
:
visible
;
opacity
:
12%
;
}
.article-text
figure
{
margin
:
2em
auto
;
}
.article-text
figure
figcaption
{
margin
:
1em
;
font-style
:
italic
;
text-align
:
center
;
}
.article-text
figure
img
{
display
:
block
;
transition
:
filter
250ms
;
margin
:
0
auto
;
}
.article-text
figure
a
:hover
img
{
filter
:
brightness
(
115%
)
contrast
(
90%
);
}
.article-text
figure
.reasonable
,
.article-text
figure
.wide
{
width
:
100%
;
}
.article-text
figure
.reasonable
img
{
max-width
:
100%
;
max-height
:
75vh
;
}
/* Comments */
#comments
ol
{
list-style
:
none
;
padding
:
0
;
}
#comments
ol
+
h3
{
margin-top
:
2rem
;
margin-bottom
:
0
;
/* if 'ol' was present, then the radio-text will be too */
}
#comments
li
>
ol
>
li
{
margin-left
:
2rem
}
#comments
li
li
{
border-top
:
solid
0.1em
#aaa
}
#comments
article
footer
{
margin-top
:
1em
;
font-family
:
sans-serif
;
margin-bottom
:
1em
;
}
#comments
article
small
{
margin-left
:
0.5em
;
}
#comments
.reply
{
text-align
:
right
;
margin-bottom
:
0.5em
;
}
#comments
.reply
input
,
#reply-root
{
display
:
none
;
}
#comments
.reply
input
:checked
+
label
,
#reply-root
+
label
{
visibility
:
hidden
;
font-size
:
0
;
}
#comments
.reply
input
:checked
+
label
::after
,
#reply-root
+
label
::after
{
visibility
:
visible
;
color
:
#751
;
content
:
"Odpovídáte sem"
;
font-size
:
1rem
;
}
#reply-root
:checked
+
label
::after
{
content
:
"Vkládáte samostatný komentář"
;
}