~duncan-bayne/halp

1d6ab9de1f418fd6707372d4062ba76f26f04d19 — Duncan Bayne 22 days ago 8342b90
WIP
M dev-config/halp-config.pl => dev-config/halp-config.pl +1 -2
@@ 8,13 8,12 @@

    gemini => {
	host => '0.0.0.0',
	input_path => '/home/duncan/code/halp/t/fixtures/www',
	input_path => '/home/duncan/code/duncan.bayne.id.au/gemini',
	output_path => '/home/duncan/code/halp/tmp/gemini'
    },

    web => {
	host => '0.0.0.0',
	# input_path => '/home/duncan/code/halp/t/fixtures/www',
	input_path => '/home/duncan/code/duncan.bayne.id.au/www',
	output_path => '/home/duncan/code/halp/tmp/www'
    }

D gemini/index.tpl => gemini/index.tpl +0 -1
@@ 1,1 0,0 @@
Content. {$path}

A gemini/site/.static/images/atom.png => gemini/site/.static/images/atom.png +0 -0
A gemini/site/.static/images/bvbrows.gif => gemini/site/.static/images/bvbrows.gif +0 -0
A gemini/site/.static/images/favicon.png => gemini/site/.static/images/favicon.png +0 -0
A gemini/site/.static/images/halp-icon.jpg => gemini/site/.static/images/halp-icon.jpg +0 -0
A gemini/site/.static/images/mothracompat.gif => gemini/site/.static/images/mothracompat.gif +0 -0
A gemini/site/.static/images/valid-atom.png => gemini/site/.static/images/valid-atom.png +0 -0
A gemini/site/.static/images/valid-html5.png => gemini/site/.static/images/valid-html5.png +0 -0
A gemini/site/.static/images/vcss-blue => gemini/site/.static/images/vcss-blue +0 -0
A gemini/templates/directory_listing.gmi.tpl => gemini/templates/directory_listing.gmi.tpl +7 -0
@@ 0,0 1,7 @@
{$directory_description}

{
  foreach $i (@items) {
    $OUT .= "=> $i->{href} $i->{title}\n";
  }
}

A gemini/templates/menu.gmi.tpl => gemini/templates/menu.gmi.tpl +5 -0
@@ 0,0 1,5 @@
{
  foreach $i (@items) {
    $OUT .= "=> $i->{href} $i->{title}\n"
  }
}
\ No newline at end of file

A gemini/templates/page.gmi.tpl => gemini/templates/page.gmi.tpl +6 -0
@@ 0,0 1,6 @@
# { $title }
{ $content }
___
{ $menu }
{ $atom_feed }
=> https://git.sr.ht/~duncan-bayne/halp Brought to you by Halp

A gemini/templates/sitemap.gmi.tpl => gemini/templates/sitemap.gmi.tpl +6 -0
@@ 0,0 1,6 @@
<ul id="sitemap">
{
  foreach $i (@items) {
    $OUT .= "=> $i->{href} $i->{title}\n";
  }
}

M halp.pl => halp.pl +2 -2
@@ 46,8 46,8 @@ if ($gemini) {
	author => $halp_config{author},
	domain => $halp_config{domain},
	host => $halp_config{gemini}{host},
	input_path => $halp_config{web}{input_path},
	output_path => $halp_config{web}{output_path}
	input_path => $halp_config{gemini}{input_path},
	output_path => $halp_config{gemini}{output_path}
	);

    $gemini_server->generate();

M lib/Halp/AtomFeed.pm => lib/Halp/AtomFeed.pm +6 -5
@@ 12,29 12,30 @@ use Exporter 'import';
our @EXPORT = qw(feed_for);

sub feed_for {
    my ($local_path, $web_path, $domain, $author) = @_;
    my ($protocol, $local_path, $input_path, $output_path, $domain, $author) = @_;

    my $feed_directory = dirname($local_path);
    my $relative_feed_directory = rel2abs($input_path, $feed_directory);
    my $updated_at = `cd $feed_directory; git log --reverse --pretty='format:%ad' --date=iso8601-strict -- | head -1`;

    my $feed = XML::Atom::SimpleFeed->new(
	title   => directory_title($feed_directory),
	link    => "https://$domain/$feed_path",
	link    => "$protocol://$domain/$relative_feed_directory",
	updated => $updated_at
	);

    my @files = directory_listing($local_path, 1);
    my @files = directory_listing($input_path, $local_path, 1);

    foreach my $file (@files) {
	my $full_path = $file->{filename};
	if (-f $full_path) {
	    my $contents = read_file($full_path);
	    $feed_directory = dirname($full_path);
	    my $relative_path = rel2abs($feed_directory, $web_path);
	    my $relative_path = rel2abs($feed_directory, $output_path);
	    $updated_at = `cd $feed_directory; git log --reverse --pretty='format:%ad' --date=iso8601-strict -- $relative_path | head -1`;
	    chomp($updated_at);

	    my $url = "https://$domain/$file->{href}";
	    my $url = "$protocol://$domain/$file->{href}";
	    $feed->add_entry(
		title     => file_title($full_path),
		link      => $url,

M lib/Halp/ContentUtils.pm => lib/Halp/ContentUtils.pm +5 -6
@@ 8,24 8,23 @@ use Halp::ContentUtils;
use HTTP::Server::Simple::CGI;
use Text::Template;
use Time::Piece;
use XML::Atom::SimpleFeed;
use utf8;
use XML::Atom::SimpleFeed;

use Exporter 'import';
our @EXPORT = qw(directory_listing directory_title file_title);

sub directory_listing {
    my ($local_path, $descend) = @_;
    my ($input_path, $local_path, $descend) = @_;
    my @files;
    my $href;
    opendir(my $dh, $local_path);
    while (my $file = readdir $dh) {
	next if $file =~ /^\./;

	$href = File::Spec->abs2rel($file, $self->{input_path});

	my $title = $file;
	my $full_path = File::Spec->catfile($local_path, $file);
	my $href = File::Spec->abs2rel($full_path, $input_path);

	if (-f $full_path) {
	    $title = file_title($full_path);


@@ 34,7 33,7 @@ sub directory_listing {
	if (-d $full_path) {
	    $title = directory_title($full_path);
	    if ($descend) {
		push @files, directory_listing($full_path, 1);
		push @files, directory_listing($input_path, $full_path, 1);
	    }
	}



@@ 42,7 41,7 @@ sub directory_listing {
    }
    closedir($dh);

    return sort { $a->{href} cmp $b->{href} } @files;
    return sort { $b->{href} cmp $a->{href} } @files;
}

sub directory_title {

M lib/Halp/GeminiGenerator.pm => lib/Halp/GeminiGenerator.pm +139 -6
@@ 3,20 3,153 @@ package Halp::GeminiGenerator;
use strict;
use warnings;

use Cwd;
use Data::Dump qw(dump);
use File::Basename;
use File::Spec;
use IO::Socket::INET;
use IO::Socket::SSL;
use Text::Template;
use File::Copy::Recursive "dircopy";
use File::Slurp;
use File::Spec qw(abs2rel);
use Halp::AtomFeed;
use Halp::ContentUtils;

sub new {
    my ($class, %args) = @_;
    my $self = bless \%args, $class;
    my $self = {};

    return $self;
    die "input_path $args{input_path} must be absolute" unless File::Spec->file_name_is_absolute($args{input_path});
    die "output_path $args{output_path} must be absolute" unless File::Spec->file_name_is_absolute($args{output_path});

    $self->{author} = $args{author};
    $self->{domain} = $args{domain};
    $self->{host} = $args{host};
    $self->{input_path} = $args{input_path};
    $self->{output_path} = $args{output_path};

    $self->{template_path} = File::Spec->catfile(cwd(), "gemini/templates");
    $self->{default_site_path} = File::Spec->catfile(cwd(), "gemini/site");

    return bless $self, $class;
}

sub generate_menu {
    my ($self, $request_path) = @_;

    my $template_pathname = File::Spec->catfile($self->{template_path}, 'menu.gmi.tpl');
    my $template = Text::Template->new(SOURCE => $template_pathname);

    my @items = ({ href => '/', title => 'Home' });
    my @listing = directory_listing($self->{input_path}, $self->{input_path}, 0);
    push(@items, @listing);

    my $result = $template->fill_in(HASH => { 'items' => \@items } );
    return $result;
}

sub wrap_page_template {
    my ($self, $title, $content, $show_atom_link, $source_filename) = @_;

    my $menu = $self->generate_menu($self->{input_path});
    my $template_pathname = File::Spec->catfile($self->{template_path}, 'page.gmi.tpl');
    my $template = Text::Template->new(SOURCE => $template_pathname);
    my $atom_url = File::Spec->catfile(dirname(File::Spec->abs2rel($source_filename, $self->{input_path})), "feed.xml");

    my $footer = '';
    if (-f $source_filename) {
	my $footer_pathname = File::Spec->catfile(dirname($source_filename), '.footer.gmi');
	if (-f $footer_pathname) {
	    $footer = read_file($footer_pathname);
	}
    }

    my $atom_feed = '';
    if ($show_atom_link) {
	$atom_feed = "=> $atom_url Atom Feed"
    }

    my $result = $template->fill_in(HASH => {'title' => $title, 'content' => $content, 'menu' => $menu, 'atom_feed' => $atom_feed, 'footer' => $footer});

    return $result;
}

sub generate_file {
    my ($self, $source_file) = @_;

    my $relative_file = File::Spec->abs2rel($source_file, $self->{input_path});
    my $output_file = File::Spec->catfile($self->{output_path}, $relative_file);

    my ($extension) = $source_file =~ /\.([^.]*)$/;
    my $content = read_file($source_file, binmode => ':bytes');

    if ($extension eq "gmi") {
	$content = $self->wrap_page_template(file_title($source_file), $content, 0, $source_file);
    }

    open my $content_file, '>', $output_file or die "Can't open '$output_file'";
    binmode $content_file;
    print $content_file $content;
    close $content_file;
}

sub generate_directory {
    my ($self, $source_path) = @_;

    my $relative_path = File::Spec->abs2rel($source_path, $self->{input_path});
    my $output_path = File::Spec->catfile($self->{output_path}, $relative_path);

    if (!-e $output_path) {
	mkdir($output_path) or die "Unable to create directory '$output_path' - $!";
    }

    my $title = directory_title($source_path);

    my $directory_description = "";
    my $description_pathname = File::Spec->catfile($source_path, '.description.gmi');
    if (-f $description_pathname) {
	$directory_description = read_file($description_pathname);
	$directory_description =~ s/\s+$//;
    }

    my $template_pathname = File::Spec->catfile($self->{template_path}, 'directory_listing.gmi.tpl');
    my $template = Text::Template->new(SOURCE => $template_pathname);
    my @items = directory_listing($self->{input_path}, $source_path, 0);
    my $result = $template->fill_in(HASH => {'path' => $source_path,
						 'directory_description' => $directory_description,
						 'items' => \@items});
    $result = $self->wrap_page_template($title, $result, 1, $source_path);

    my $index_file = File::Spec->catfile($output_path, "index.gmi");
    open my $file, '>', $index_file or die "Can't open '$index_file'";
    print $file $result;
    close $file;

    my $atom_file = File::Spec->catfile($output_path, "feed.xml");
    my $atom_feed = feed_for("gemini", $source_path, $self->{input_path}, $self->{output_path}, $self->{domain}, $self->{author});
    open $file, '>', $atom_file or die "Can't open '$index_file'";
    print $file $atom_feed;
    close $file;

    my @files = <"$source_path/*" "$source_path/.*">;
    foreach $file (@files) {
	my $relative_file = basename($file);

	if ($relative_file =~ m/^\./) {
	    # Ignore dotfiles
	} elsif ($relative_file =~ m/^\.+$/) {
	    # Ignore . and ..
	} elsif (-d $file) {
	    $self->generate_directory($file);
	} elsif (-f $file) {
	    $self->generate_file($file);
	}
    }
}

sub generate {
    my ($self) = @_;

    print("dircopy($self->{default_site_path}, $self->{output_path}) or die $!;\n");
    dircopy($self->{default_site_path}, $self->{output_path}) or die $!;
    $self->generate_directory($self->{input_path});
}

1;

M lib/Halp/WebGenerator.pm => lib/Halp/WebGenerator.pm +11 -9
@@ 1,18 1,20 @@
package Halp::WebGenerator;

use strict;
use warnings;

use Cwd;
use Data::Dump qw(dump);
use File::Basename;
use File::Copy::Recursive "dircopy";
use File::Slurp;
use File::Spec;
use Halp::AtomFeed;
use Halp::ContentUtils;

use base qw(HTTP::Server::Simple::CGI);

sub new {
    my ($class, %args) = @_;
    my $self = $class->SUPER::new();
    my $self = {};

    die "input_path $args{input_path} must be absolute" unless File::Spec->file_name_is_absolute($args{input_path});
    die "output_path $args{output_path} must be absolute" unless File::Spec->file_name_is_absolute($args{output_path});


@@ 26,7 28,7 @@ sub new {
    $self->{template_path} = File::Spec->catfile(cwd(), "www/templates");
    $self->{default_site_path} = File::Spec->catfile(cwd(), "www/site");

    return $self;
    return bless $self, $class;
}

sub generate_menu {


@@ 36,7 38,7 @@ sub generate_menu {
    my $template = Text::Template->new(SOURCE => $template_pathname);

    my @items = ({ href => '/', title => 'Home' });
    my @listing = directory_listing($self->{input_path}, 0);
    my @listing = directory_listing($self->{input_path}, $self->{input_path}, 0);
    push(@items, @listing);

    my $result = $template->fill_in(HASH => { 'items' => \@items, 'current' => \@items[0] } );


@@ 46,7 48,7 @@ sub generate_menu {
sub wrap_page_template {
    my ($self, $title, $content, $show_atom_link, $source_filename) = @_;

    my $menu = $self->generate_menu($source_filename);
    my $menu = $self->generate_menu($self->{input_path});
    my $template_pathname = File::Spec->catfile($self->{template_path}, 'page.html.tpl');
    my $template = Text::Template->new(SOURCE => $template_pathname);
    my $atom_url = File::Spec->catfile(dirname(File::Spec->abs2rel($source_filename, $self->{input_path})), "feed.xml");


@@ 117,7 119,7 @@ sub generate_directory {

    my $template_pathname = File::Spec->catfile($self->{template_path}, 'directory_listing.html.tpl');
    my $template = Text::Template->new(SOURCE => $template_pathname);
    my @items = directory_listing($source_path, 0);
    my @items = directory_listing($self->{input_path}, $source_path, 0);
    my $result = $template->fill_in(HASH => {'path' => $source_path,
						 'directory_description' => $directory_description,
						 'items' => \@items});


@@ 129,7 131,7 @@ sub generate_directory {
    close $file;

    my $atom_file = File::Spec->catfile($output_path, "feed.xml");
    my $atom_feed = feed_for($source_path, $self->{web_path}, $self->{domain}, $self->{author});
    my $atom_feed = feed_for("https", $source_path, $self->{web_path}, $self->{domain}, $self->{author});
    open my $file, '>', $atom_file or die "Can't open '$index_file'";
    print $file $atom_feed;
    close $file;


@@ 138,7 140,7 @@ sub generate_directory {
    foreach $file (@files) {
	my $relative_file = basename($file);

	if (-f $file && $relative_file =~ m/^\./) {
	if ($relative_file =~ m/^\./) {
	    # Ignore dotfiles
	} elsif ($relative_file =~ m/^\.+$/) {
	    # Ignore . and ..

D www/templates/404.html.tpl => www/templates/404.html.tpl +0 -3
@@ 1,3 0,0 @@
<p>
  {$path} was not found.
</p>

D www/templates/405.html.tpl => www/templates/405.html.tpl +0 -3
@@ 1,3 0,0 @@
<p>
  Method not allowed.
</p>