summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore3
-rw-r--r--.perlcriticrc4
-rw-r--r--.perltidyrc21
-rw-r--r--.tidyallrc7
-rwxr-xr-xbin/events2md.pl262
-rwxr-xr-xbin/ftppush.sh20
-rw-r--r--config/ikiwiki.setup.in45
-rw-r--r--config/include.mk1
8 files changed, 361 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index 5f6f3a9..41d6988 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,8 @@
-*/.sass-cache/
+/.sass-cache/
/build/
/content/
/styling/
/forms/
/config/ikiwiki*.setup
/.ikiwiki*/
+/js/
diff --git a/.perlcriticrc b/.perlcriticrc
new file mode 100644
index 0000000..4e08e03
--- /dev/null
+++ b/.perlcriticrc
@@ -0,0 +1,4 @@
+[-Subroutines::ProhibitSubroutinePrototypes]
+
+[TestingAndDebugging::RequireUseStrict]
+equivalent_modules = strictures
diff --git a/.perltidyrc b/.perltidyrc
new file mode 100644
index 0000000..2e08554
--- /dev/null
+++ b/.perltidyrc
@@ -0,0 +1,21 @@
+# use best practices, except use of stdout
+--perl-best-practices
+--no-standard-output
+--no-standard-error-output
+
+# use TAB for lead indentation
+--tabs
+--entab-leading-whitespace=4
+-nola
+
+# indent only already indented comments
+--indent-spaced-block-comments
+
+# put brace on new line for named subroutines
+--opening-sub-brace-on-new-line
+
+# preserve horisontally styled lists
+--break-at-old-comma-breakpoints
+
+# overwrite (we use CVS), and leave backup only on error
+--backup-file-extension=/~
diff --git a/.tidyallrc b/.tidyallrc
new file mode 100644
index 0000000..4bcb16c
--- /dev/null
+++ b/.tidyallrc
@@ -0,0 +1,7 @@
+[PerlTidy]
+select = **/*.{pl,pm,t}
+;select = bin/*
+argv = --profile=$ROOT/.perltidyrc
+
+[PerlCritic]
+select = lib/**/*.pm
diff --git a/bin/events2md.pl b/bin/events2md.pl
new file mode 100755
index 0000000..c694d9a
--- /dev/null
+++ b/bin/events2md.pl
@@ -0,0 +1,262 @@
+#!/usr/bin/perl
+
+use v5.14;
+use utf8;
+use open qw(:std :encoding(UTF-8));
+use strictures;
+use autodie;
+
+use POSIX qw(locale_h);
+use locale;
+use Encode qw(decode_utf8); # TODO: modernize CalDAV access instead
+use Net::Netrc;
+use List::Util qw(first);
+
+use IO::Interactive::Tiny;
+use Log::Any qw($log);
+use Log::Any::Adapter;
+
+use URI;
+use IO::Prompter;
+use Cal::DAV;
+use Data::ICal;
+use iCal::Parser;
+use DateTime;
+use Try::Tiny;
+use Path::Tiny;
+
+if ( IO::Interactive::Tiny::is_interactive() ) {
+ Log::Any::Adapter->set( 'Screen', default_level => 'info' );
+}
+
+# set defaults and parse command-line options
+my ( $BASE_URI, $CALENDAR_URI, $OUTPUT_FILE );
+$BASE_URI = $ENV{CAL_DAV_URL_BASE};
+$CALENDAR_URI = $ENV{CAL_DAV_URL_CALENDAR};
+$BASE_URI ||= shift @ARGV
+ if @ARGV;
+$CALENDAR_URI ||= shift @ARGV
+ if @ARGV;
+$OUTPUT_FILE = shift @ARGV
+ if @ARGV;
+
+# use system locale to format DateTime objects parsed from iCal data
+DateTime->DefaultLocale( setlocale(LC_TIME) );
+
+# resolve calendar URIs
+my ( $base_uri, $calendar_uri, $calendar );
+$base_uri = URI->new($BASE_URI)
+ if ($BASE_URI);
+$base_uri
+ or $log->fatal('required base URI not provided') && exit 2;
+$base_uri->scheme
+ or $base_uri->scheme('file');
+if ( $base_uri->scheme eq 'http' or $base_uri->scheme eq 'https' ) {
+ $log->infof( 'will use base URI %s', $base_uri );
+ $calendar_uri = URI->new( $CALENDAR_URI || $base_uri );
+ $calendar_uri and $calendar_uri->authority
+ or $log->fatal('bad calendar URI: must be an internet URI') && exit 2;
+ $base_uri->eq($calendar_uri) and $calendar_uri = undef
+ or $log->infof( 'will use calendar URI %s', $calendar_uri );
+
+ # resolve credentials
+ $log->debug('resolve credentials...');
+ my ( $mach, $user, $pass );
+ ( $user, $pass ) = split ':', $base_uri->userinfo
+ if $base_uri->userinfo;
+ $user ||= $ENV{CAL_DAV_USER};
+ $pass ||= $ENV{CAL_DAV_PASS};
+ $mach = Net::Netrc->lookup( $base_uri->host, $user )
+ if !$user or !$pass;
+ if ($mach) {
+ $user ||= $mach->login;
+ $pass ||= $mach->password;
+ $log->infof( 'will use .netrc provided credentials for user %s', $user );
+ }
+ elsif ( IO::Interactive::Tiny::is_interactive() ) {
+ $log->warn('will ask for missing info - this will fail in headless mode');
+ $user ||= prompt 'Enter your username';
+ $pass ||= prompt 'Enter your password', -echo => '*';
+ }
+ $log->debugf( 'resolved credentials for user %s', $user );
+
+ # fetch and parse CalDAV calendar data
+ $log->debug('fetch and parse CalDAV calendar data...');
+ $calendar = Cal::DAV->new(
+ user => $user,
+ pass => $pass,
+ url => $base_uri,
+ );
+ $calendar->get($calendar_uri)
+ if $calendar_uri;
+}
+elsif ( $base_uri->scheme eq 'file' ) {
+ defined $base_uri->file
+ or $log->fatal('bad base URI: cannot open file') && exit 2;
+ $log->infof( 'will use base URI %s', $base_uri );
+
+ # parse local calendar data
+ $log->debug('parse local calendar data...');
+ my $path = path( $base_uri->file );
+ if ( $path->is_file ) {
+ $calendar = Data::ICal->new( data => $path->slurp_raw );
+ }
+ else {
+ my $data;
+ $path->visit( sub { $data .= $_->slurp_raw if $_->is_file } );
+ $calendar = Data::ICal->new( data => $data );
+ }
+}
+if ( $log->is_trace ) {
+ use DDP;
+ p $calendar;
+}
+
+# index calendar entries
+$log->debug('index calendar entries...');
+my %calendar_entries;
+for ( @{ $calendar->entries } ) {
+ if ( 'VEVENT' eq $_->ical_entry_type ) {
+ my $uid = try { $_->property('uid')->[0]->value };
+ $uid ||= Data::ICal::Entry::Event->new()->property('uid')->[0]->value;
+ $calendar_entries{VEVENT}{$uid} = $_;
+ }
+ else {
+ # TODO
+ next;
+ }
+}
+if ( $log->is_trace ) {
+ use DDP;
+ p %calendar_entries;
+}
+
+# TODO: if list is empty and no calendar uri was explicitly supplied,
+# warn on stdout with list of abailable collections using this sequence:
+# 1. PROPFIND on base-URL for {DAV:}current-user-principal
+# 2. PROPFIND for calendar-home-set property in caldav namespace
+# 3. PROPFIND with depth: 1
+# as documented at <https://stackoverflow.com/a/11673483>
+
+# serialize calendar events
+$log->debug('serialize calendar events...');
+my $start = DateTime->now;
+my $end = $start->clone->add( months => 1 );
+my $parser = iCal::Parser->new( start => $start, end => $end );
+my $events = $parser->parse_strings( $calendar->as_string );
+if ( $log->is_trace ) {
+ use DDP;
+ p $events;
+}
+my $output_path;
+if ($OUTPUT_FILE) {
+ $output_path = path($OUTPUT_FILE);
+ $output_path->parent->mkpath;
+ $output_path->remove;
+}
+for my $year (
+ map { $events->{events}{$_} }
+ sort { $a <=> $b } keys %{ $events->{events} }
+ )
+{
+ for my $month (
+ map { $year->{$_} }
+ sort { $a <=> $b } keys %$year
+ )
+ {
+ for my $day (
+ map { $month->{$_} }
+ sort { $a <=> $b } keys %$month
+ )
+ {
+ my @events = sort {
+ DateTime->compare( $a->[1], $b->[1] )
+ || DateTime->compare( $a->[2], $b->[2] )
+ || get_property_string( $a->[0], 'summary' )
+ cmp get_property_string( $b->[0], 'summary' )
+ } map {
+ [ $calendar_entries{VEVENT}{$_},
+ $day->{$_}{DTSTART}, $day->{$_}{DTEND}
+ ]
+ } keys %$day;
+ for (@events) {
+ print_event( $_->[0], $_->[1], $_->[2], $output_path, );
+ }
+ }
+ }
+}
+
+sub print_event
+{
+ my ( $entry, $start, $end, $path ) = @_;
+
+ if ( $log->is_trace ) {
+ use DDP;
+ p $entry;
+ p $start;
+ p $end;
+ p $path;
+ }
+ my $summary = get_property_string( $entry, 'summary' );
+ my $description = get_property_string( $entry, 'description' );
+ $description =~ s/\n\n[Pp]ris:\s*((?!\n).+)\s*\z//m;
+ my $price = $1;
+ my @attendees;
+ if ( $entry->property('attendee') ) {
+ for ( @{ $entry->property('attendee') } ) {
+ push @attendees, decode_utf8 $_->parameters->{'CN'}
+ || $_->value =~ s/^mailto://r;
+ }
+ }
+ my $location = get_property_string( $entry, 'location' );
+ my $time_begin = ucfirst( $start->strftime('%A') );
+ $time_begin .= $start->strftime(' %e. %B kl. %k.%M');
+ my $time_end = $end->strftime('%k.%M');
+ my %attachments;
+ if ( $entry->property('attach') ) {
+ for ( @{ $entry->property('attach') } ) {
+ my $uri = try { URI->new( $_->value ) }
+ or next;
+ $uri->authority and $uri->host
+ or next;
+ push @{ $attachments{ $uri->host } }, $uri;
+ }
+ }
+
+ $_ = "### $time_begin.";
+ $_ .= " $summary"
+ if $summary;
+ $_ .= "\n$description";
+ $_ .= " \nMed " . join( ' og ', @attendees ) . '.'
+ if @attendees;
+ $_ .= " \n**Mødested:** $location"
+ if $location;
+ $_ .= " \n**Tid:** ${time_begin}-${time_end}."
+ if $time_begin and $time_end;
+ $_ .= " \n**Pris:** $price"
+ if $price;
+ $_ .= " \n[Køb billet på Billetto]($attachments{'billetto.dk'}[0])"
+ if $attachments{'billetto.dk'};
+ $_ .= " \n[Læs mere her]($attachments{'byvandring.nu'}[0])"
+ if $attachments{'byvandring.nu'};
+ $_ .= "\n\n---\n\n";
+
+ if ($path) {
+ $path->append_utf8($_);
+ }
+ else {
+ print $_;
+ }
+}
+
+sub get_property_string
+{
+ my ( $entry, $key ) = @_;
+
+ return ''
+ unless $entry->property($key);
+
+ return decode_utf8 $entry->property($key)->[0]->value;
+}
+
+1;
diff --git a/bin/ftppush.sh b/bin/ftppush.sh
new file mode 100755
index 0000000..3db24ce
--- /dev/null
+++ b/bin/ftppush.sh
@@ -0,0 +1,20 @@
+#!/bin/sh
+
+# Expects a ~/.netrc entry like this:
+# machine byvandring.nu
+# login byvandring.nu
+# password replaceme
+
+set -e
+
+WPUT="wput --reupload --dont-continue -v"
+
+quiet="-b -o $HOME/wputlog"
+case "$1" in
+ -v)
+ quiet="";;
+esac
+WPUT="$WPUT $quiet"
+
+$WPUT --basename=$HOME/public_websites/bynu.biks.dk/ $HOME/public_websites/bynu.biks.dk/ ftp://byvandring.nu/public_html/ || true
+$WPUT /usr/share/javascript/leaflet/ ftp://byvandring.nu/public_html/ || true
diff --git a/config/ikiwiki.setup.in b/config/ikiwiki.setup.in
index ed57570..30e7c0e 100644
--- a/config/ikiwiki.setup.in
+++ b/config/ikiwiki.setup.in
@@ -9,4 +9,47 @@
# edit this file.
#
# name of the wiki
-wikiname: Example website
+wikiname: Byvandring Nu
+adminemail: paul@hartvigson.dk
+adminuser:
+- paul
+- jonas
+- siri
+url: http://bynu.biks.dk/
+cgiurl: http://bynu.biks.dk/ikiwiki.cgi
+add_plugins:
+- lockedit
+- sidebar2
+- copyright
+- linkmap
+- attachment
+- pagestats
+- httpauth
+- edittemplate
+disable_plugins:
+- openid
+- passwordauth
+- search
+- theme
+- htmlscrubber
+- sidebar
+discussion: 0
+timeformat: '%A d. %e. %B'
+locale: da_DK.UTF-8
+timezone: Europe/Copenhagen
+historyurl: 'https://source.couchdesign.dk/bynu/content.git/log/[[file]]'
+diffurl: 'https://source.couchdesign.dk/bynu/content.git/diff/[[file]]?id=[[sha1_commit]]'
+locked_pages: '*'
+multimarkdown: 1
+#allowed_attachments: maxsize(2mb) and ((*.png and mimetype(image/png)) or (*.gif and mimetype(image/gif)) or (*.jpg and mimetype(image/jpeg)))
+allowed_attachments: (*.png or *.gif or *.jpg)
+tagbase: tag
+tag_autocreate: 0
+tag_autocreate_commit: 0
+global_sidebars: [
+ "sidebar", "sidebar", "*",
+ "farbar", "farbar", "*",
+ "topbar", "topbar", "*",
+ "footer", "footer", "*",
+ "branding", "branding", "*"
+ ]
diff --git a/config/include.mk b/config/include.mk
new file mode 100644
index 0000000..5c9fbfc
--- /dev/null
+++ b/config/include.mk
@@ -0,0 +1 @@
+IKIWIKI_UNDERLAYS += $(CURDIR)/js/build