Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 17 additions & 8 deletions lib/Plerd/Post.pm
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ use HTML::SocialMeta;
use Try::Tiny;
use JSON;
use Path::Class::File;
use Digest::MD5 qw( md5_hex );
use Encode qw( encode_utf8 );

use Plerd::SmartyPants;
use Web::Mention;
Expand Down Expand Up @@ -194,15 +196,22 @@ sub _build_published_filename {
$filename =~ s/\..*$/.html/;
}
else {
$filename = $self->title;
my $stripper = HTML::Strip->new( emit_spaces => 0 );
$filename = $stripper->parse( $filename );
$filename =~ s/\s+/-/g;
$filename =~ s/--+/-/g;
$filename =~ s/[^\w\-]+//g;
$filename = lc $filename;
$filename = $self->date->ymd( q{-} ) . q{-} . $filename;
$filename .= '.html';
my $slug = $stripper->parse( $self->title );
$slug =~ s/\s+/-/g;
$slug =~ s/--+/-/g;
$slug =~ s/[^\w\-]+//g;
$slug = lc $slug;

# A title made entirely of characters that can't appear in a filename
# (e.g. emoji) slugs down to nothing, which would make every such post
# on a given day collide on "YYYY-MM-DD-.html". Fall back to a short
# hash of the title, so distinct titles get distinct, stable filenames.
unless ( length $slug ) {
$slug = substr( md5_hex( encode_utf8( $self->title ) ), 0, 10 );
}

$filename = $self->date->ymd( q{-} ) . q{-} . $slug . '.html';
}

return $filename;
Expand Down
71 changes: 71 additions & 0 deletions t/emoji_title.t
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
use warnings;
use strict;
use utf8;
use Test::More;
use Path::Class::Dir;
use Path::Class::File;
use URI;

use FindBin;
use lib "$FindBin::Bin/../lib";

use_ok( 'Plerd' );
use Plerd::Init;

# Regression test for GitHub issue #52: a post titled entirely with
# characters that can't appear in a filename (e.g. emoji) used to slug down
# to nothing, so every such post on a given day collided on
# "YYYY-MM-DD-.html" and overwrote its predecessor. Distinct titles must
# produce distinct, non-empty published filenames.

my $blog_dir = Path::Class::Dir->new( "$FindBin::Bin/emoji_title_blog" );
$blog_dir->rmtree;
Plerd::Init::initialize( $blog_dir->stringify, 0 );

my $source_dir = Path::Class::Dir->new( $blog_dir, 'source' );

# Two emoji-only titles, same publication day, source filenames carry no date
# prefix so the title-based filename logic runs.
Path::Class::File->new( $source_dir, 'smile.md' )->spew(
iomode => '>:encoding(utf8)',
"title: \x{1F600}\ntime: 2021-01-19T10:00:00\n\nBody one.\n",
);
Path::Class::File->new( $source_dir, 'hats.md' )->spew(
iomode => '>:encoding(utf8)',
"title: \x{1F3A9}\x{1F3A9}\ntime: 2021-01-19T11:00:00\n\nBody two.\n",
);

my $plerd = Plerd->new(
path => $blog_dir->stringify,
title => 'Test Blog',
author_name => 'Nobody',
author_email => 'nobody@example.com',
base_uri => URI->new( 'http://blog.example.com/' ),
);

$plerd->publish_all;

my %filename;
for my $post ( @{ $plerd->posts } ) {
$filename{ $post->title } = $post->published_filename;
}

my @names = values %filename;
isnt( $names[0], $names[1],
'Two emoji-only titles get distinct published filenames.' );

unlike( $_, qr/-\.html$/, 'Published filename is not empty-slugged.' )
for @names;

# Both posts' HTML files should actually exist on disk (neither overwrote the
# other).
for my $title ( keys %filename ) {
ok(
-e Path::Class::File->new( $blog_dir, 'docroot', $filename{ $title } ),
"HTML file exists for title '$title' ($filename{ $title })."
);
}

$blog_dir->rmtree;

done_testing();
Loading