Add announcement page
This commit is contained in:
parent
91512b48be
commit
905e885041
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="TemplateDataLanguageMappings">
|
||||
<file url="file://$PROJECT_DIR$/templates/main/announcements.rss.ep" dialect="XML" />
|
||||
</component>
|
||||
</project>
|
|
@ -8,7 +8,8 @@ WriteMakefile(
|
|||
PREREQ_PM => {
|
||||
'Mojolicious' => '9.27',
|
||||
'Email::MIME' => '1.952',
|
||||
'Email::Sender::Simple' => '2.500'
|
||||
'Email::Sender::Simple' => '2.500',
|
||||
'File::ChangeNotify' => '0.31',
|
||||
},
|
||||
test => {TESTS => 't/*.t'}
|
||||
);
|
||||
|
|
|
@ -32,6 +32,10 @@ sub startup ($self) {
|
|||
$r->post('/account')->to('Account#update_account');
|
||||
|
||||
$r->get('/users')->to('Main#user_pages');
|
||||
|
||||
$r->get('/announcements')->to('Main#announcements');
|
||||
$r->get('/announcements.rss')->to('Main#announcement_feed');
|
||||
$r->get('/announcement/#filename')->to('Main#get_announcement');
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -5,6 +5,11 @@ use experimental 'signatures';
|
|||
|
||||
use Mojo::Base 'Mojolicious::Controller', -signatures;
|
||||
use Net::LDAPS;
|
||||
use File::ChangeNotify;
|
||||
use File::stat;
|
||||
use File::Basename;
|
||||
use Time::Piece;
|
||||
use Scalar::Util qw(looks_like_number);
|
||||
|
||||
sub index ($self) {
|
||||
$self->render();
|
||||
|
@ -25,6 +30,9 @@ Mojo::IOLoop->recurring(60 => sub {
|
|||
$cache->set(users => []);
|
||||
});
|
||||
|
||||
my $acache = Mojo::Cache->new(max_keys => 1);
|
||||
my $filewatcher;
|
||||
|
||||
sub user_pages ($self) {
|
||||
my $config = $self->config;
|
||||
my $connStr = $config->{'ldap'}->{'uri'};
|
||||
|
@ -60,4 +68,166 @@ sub user_pages ($self) {
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
sub announcements ($self) {
|
||||
if (!defined $acache->get('announcements')) {
|
||||
setup_filewatch($self->config->{'announce'}->{'path'});
|
||||
} else {
|
||||
check_files();
|
||||
}
|
||||
|
||||
my %announcements = %{$acache->get('announcements')};
|
||||
my $page = $self->param('page');
|
||||
if (!defined $page) {
|
||||
$page = 0;
|
||||
}
|
||||
|
||||
if (!looks_like_number($page) || $page < 0) {
|
||||
return $self->reply->not_found;
|
||||
}
|
||||
|
||||
|
||||
my $total_items = scalar(keys %announcements);
|
||||
my $items_per_page = 5;
|
||||
|
||||
my $start_index = $page * $items_per_page;
|
||||
my $end_index = $start_index + $items_per_page-1;
|
||||
|
||||
if ($start_index >= $total_items) {
|
||||
return $self->reply->not_found;
|
||||
}
|
||||
|
||||
my $next_page = -1;
|
||||
if ($end_index >= $total_items) {
|
||||
$end_index = $total_items-1;
|
||||
} elsif ($end_index != $total_items-1){
|
||||
$next_page = $page + 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
$self->stash(
|
||||
announcements => \%announcements,
|
||||
total_items => $total_items,
|
||||
items_per_page => $items_per_page,
|
||||
page => $page,
|
||||
start_index => $start_index,
|
||||
end_index => $end_index,
|
||||
next_page => $next_page,
|
||||
);
|
||||
$self->render();
|
||||
}
|
||||
|
||||
sub get_announcement ($self) {
|
||||
if (!defined $acache->get('announcements')) {
|
||||
setup_filewatch($self->config->{'announce'}->{'path'});
|
||||
} else {
|
||||
check_files();
|
||||
}
|
||||
|
||||
my $announcements = $acache->get('announcements');
|
||||
my $filename = $self->param('filename');
|
||||
if (!defined $announcements->{$filename}) {
|
||||
return $self->reply->not_found;
|
||||
}
|
||||
my $entry = $announcements->{$filename};
|
||||
$self->render(text => $entry->{'raw'}, format => 'txt');
|
||||
}
|
||||
|
||||
sub announcement_feed ($self) {
|
||||
if (!defined $acache->get('announcements')) {
|
||||
setup_filewatch($self->config->{'announce'}->{'path'});
|
||||
} else {
|
||||
check_files();
|
||||
}
|
||||
|
||||
my $announcements = $acache->get('announcements');
|
||||
my @chosen = sort (keys %$announcements);
|
||||
|
||||
if (scalar(@chosen) > 5) {
|
||||
@chosen = @chosen[0..4];
|
||||
}
|
||||
|
||||
my @elements;
|
||||
|
||||
for my $key (@chosen) {
|
||||
push @elements, $announcements->{$key};
|
||||
}
|
||||
|
||||
$self->stash(items => \@elements);
|
||||
$self->render('main/announcements', format => 'rss');
|
||||
}
|
||||
|
||||
sub setup_filewatch($path) {
|
||||
opendir my $dir, $path or die "Could not open directory!";
|
||||
my @files = readdir $dir;
|
||||
closedir $dir;
|
||||
|
||||
my %announcements;
|
||||
|
||||
for my $filename (@files) {
|
||||
if ($filename eq "." || $filename eq "..") {
|
||||
next;
|
||||
}
|
||||
|
||||
my $entry = get_entry($path.$filename);
|
||||
if (!defined $entry) {
|
||||
say "Invalid entry at $filename";
|
||||
next;
|
||||
}
|
||||
|
||||
$announcements{$filename} = $entry;
|
||||
}
|
||||
$acache->set(announcements => \%announcements);
|
||||
|
||||
$filewatcher = File::ChangeNotify->instantiate_watcher(
|
||||
directories => [$path],
|
||||
);
|
||||
}
|
||||
|
||||
sub check_files {
|
||||
for my $event ($filewatcher->new_events) {
|
||||
if ($event->path =~ /\.txt$/) {
|
||||
my $filename = basename($event->path);
|
||||
if ($event->type eq 'create' || $event->type eq 'modify') {
|
||||
my $entry = get_entry($event->path);
|
||||
if (!defined $entry) {
|
||||
say "Invalid entry at $filename";
|
||||
delete $acache->get('announcements')->{$filename};
|
||||
next;
|
||||
}
|
||||
$acache->get('announcements')->{$filename} = get_entry($event->path);
|
||||
} elsif ($event->type eq 'delete') {
|
||||
delete $acache->get('announcements')->{$filename};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub get_entry($fullpath) {
|
||||
open FH, '<', $fullpath or die "Could not open file! $!";
|
||||
read FH, my $announce_content, -s FH;
|
||||
close FH;
|
||||
my $filestat = stat($fullpath);
|
||||
my $announce_text;
|
||||
my $announce_title;
|
||||
if ($announce_content =~ /-----BEGIN PGP SIGNED MESSAGE-----\nHash: \w+\n\n(.+?)\n\n(.*)\n-----BEGIN PGP SIGNATURE-----/s) {
|
||||
$announce_title = $1;
|
||||
$announce_text = $2;
|
||||
}
|
||||
|
||||
if (!defined $announce_title || !defined $announce_content || length $announce_title == 0 || length $announce_content == 0) {
|
||||
return undef;
|
||||
}
|
||||
|
||||
|
||||
return {
|
||||
raw => $announce_content,
|
||||
text => $announce_text,
|
||||
date => $filestat->mtime,
|
||||
filename => basename($fullpath),
|
||||
title => $announce_title,
|
||||
};
|
||||
}
|
||||
|
||||
1;
|
||||
|
|
|
@ -140,6 +140,10 @@ textarea {
|
|||
padding: 10px;
|
||||
}
|
||||
|
||||
pre {
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
#header-content a:link {
|
||||
text-decoration: none;
|
||||
color: black;
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title><%= title %></title>
|
||||
<link rel="icon" type="image/x-icon" href="favicon.ico">
|
||||
<link rel="stylesheet" href="css/main.css">
|
||||
<link rel="icon" type="image/x-icon" href="/favicon.ico">
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<link rel="alternate" type="application/rss+xml" title="UNIX.dog Announcements" href="https://unix.dog/announcements.rss" >
|
||||
|
||||
<meta property="og:title" content="<%= title %>">
|
||||
<meta property="og:type" content="webpage">
|
||||
|
@ -19,11 +20,12 @@
|
|||
<body>
|
||||
<header>
|
||||
<div id="header-content">
|
||||
<img src="favicon.ico" width="32" height="32" alt="UNIX.dog"/>
|
||||
<img src="/favicon.ico" width="32" height="32" alt="UNIX.dog"/>
|
||||
<h1><a href="/">UNIX.dog</a></h1>
|
||||
<div id="nav">
|
||||
<a href="/users">Users</a>
|
||||
<a href="/account">Account</a>
|
||||
<a href="/announcements">Announcements</a>
|
||||
<a href="/services">Services</a>
|
||||
<a href="/register">Register</a>
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
% layout 'default';
|
||||
% title 'UNIX.dog Announcements';
|
||||
% use Time::Piece;
|
||||
<h1>
|
||||
Announcements
|
||||
</h1>
|
||||
<p>
|
||||
These announcements are also avaiable over RSS. Just add .rss to the path here.
|
||||
<br>
|
||||
Also, all announcements are signed with the following PGP key:
|
||||
</p>
|
||||
<pre>67A3 9437 8618 B72E B4D5 CC69 B0EB E117 49B9 9367 alpha@unix.dog</pre>
|
||||
<p>
|
||||
so you can verify their authenticity. When a new announcement is out, you should
|
||||
also recieve an email if you're registered.
|
||||
</p>
|
||||
<% my @sorted = sort (keys %$announcements); %>
|
||||
<% foreach my $key (@sorted[$start_index..$end_index]) { %>
|
||||
<% my $value = $announcements->{$key}; %>
|
||||
<article>
|
||||
<h2><%= $value->{"title"} %></h2>
|
||||
<p>
|
||||
Posted <%= localtime($value->{"date"})->strftime() %>.
|
||||
<a href="/announcement/<%= $value->{"filename"} %>">Download PGP signature.</a>
|
||||
<p>
|
||||
<pre><%= $value->{"text"} %></pre>
|
||||
</article>
|
||||
<% } %>
|
||||
<% if ($next_page != -1) { %>
|
||||
<a href="/announcements?page=<%= $next_page %>">Next page...</a>
|
||||
<% } %>
|
|
@ -0,0 +1,26 @@
|
|||
% use Time::Piece;
|
||||
<?xml version="1.0" encoding="utf-8" ?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>UNIX.dog Announcements</title>
|
||||
<link>https://unix.dog/announcements</link>
|
||||
<atom:link href="https://unix.dog/announcements.rss" rel="self" type="application/rss+xml"/>
|
||||
<description>UNIX.dog Announcements</description>
|
||||
<language>en-us</language>
|
||||
<image>
|
||||
<title>UNIX.dog Announcements</title>
|
||||
<url>https://unix.dog/favicon.ico</url>
|
||||
<link>https://unix.dog/announcements</link>
|
||||
</image>
|
||||
% foreach my $item (@$items) {
|
||||
<item>
|
||||
<title><![CDATA[<%= $item->{'title'} %>]]></title>
|
||||
<link>https://unix.dog/announcement/<%= $item->{'filename'} %></link>
|
||||
<guid isPermaLink="true">https://unix.dog/announcement/<%= $item->{'filename'} %></guid>
|
||||
<description><![CDATA[<%== $item->{'text'} %>]]></description>
|
||||
<author>Alpha</author>
|
||||
<pubDate><%= localtime($item->{'date'})->strftime("%a, %d %b %Y %H:%M:%S %z") %></pubDate>
|
||||
</item>
|
||||
% }
|
||||
</channel>
|
||||
</rss>
|
|
@ -12,3 +12,7 @@ ldap:
|
|||
uri: 'ldaps://127.0.0.1'
|
||||
bindDN: 'here'
|
||||
password: 'here'
|
||||
|
||||
# Leading slash
|
||||
announce:
|
||||
path: '/srv/announce/'
|
||||
|
|
Loading…
Reference in New Issue