From: Sebastian Date: Thu, 24 May 2012 12:58:39 +0000 (+0200) Subject: initial commit X-Git-Url: http://sraa.de/git/?a=commitdiff_plain;h=028d463547e862f9fe132c3a732d799e45424a65;p=kulturtankstelle.git initial commit 'music', 'art' are usable 'index', 'webpage', 'litterature' are not yet implemented or dummies works with (slightly modified) starter-package by Piratenpartei Bremen (see www.kulturtankstelle.net) --- 028d463547e862f9fe132c3a732d799e45424a65 diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..cddc9da --- /dev/null +++ b/README.txt @@ -0,0 +1,75 @@ +Kulturtankstelle +================ + +Kontakt: oder Mailingliste unter + + +Inhalte: getestet mit leicht verändertem Starter-Paket der Bremer Piraten + +- cgi-bin/kt: +--- Hauptprogramm der Kulturtankstelle +--- Perl, CGI-Script +--- verwendet HTML::Template, XML::Simple, (optional) CGI::Carp +--- Anpassungen nötig: +----- 'use lib' muss auf Modulpfad zeigen +----- $configfile muss auf config.xml zeigen + +- kulturtankstelle/config.xml: +--- zentrale Konfigurationsdatei der Kulturtankstelle + +--- enthält Pfade: +----- root: Programmdateien im Dateisystem +----- webroot: Programmdateien im HTTP-Server +----- content: Inhalte im Dateisystem +----- webcontent: Inhalte im HTTP-Server + +--- enthält Menüeinträge: +----- 'id' ist ein frei wählbarer Eintrag im Menü +----- 'type' ist ein vorgefertigter Seitentyp: +------- "index": Standardeintrag, derzeit nicht implementiert +------- "music": Albenübersicht und Albumdetails +------- "art": Übersicht aller Kollektionen und Details einer Kollektion +------- "litterature": eBooks, derzeit nicht implementiert +------- "webpage": lokal vorhandene Webseite, derzeit nicht implementiert +----- 'folder' enthält den Pfad relativ zum Ordner für die Inhalte + +--- enthält Übersetzungen aller im Programm verwendeten Strings + +- kulturtankstelle/style.css: +--- zentrales Stylesheet + +- kulturtankstelle/licenses/*: +--- Bilder und HTML-Lizenztexte der CC-Lizenzen +--- jeder Content-Teil darf aber auch eigene Lizenzen besitzen + +- kulturtankstelle/templates/*: +--- Templates aller Seiten +--- siehe Dokumentation zu HTML::Template für Aufbau + +- kulturtankstelle/modules/* +--- enthält die "Intelligenz" der Kulturtankstelle + +--- KTmain.pm: +----- read_config() => liest und parst config.xml +----- read_templates() => liest alle Templates ein +----- print_header() => zeigt kt_header.tmpl an +----- print_footer() => zeigt kt_footer.tmpl +----- print_menu() => zeigt kt_menu.tmpl an +----- print_language_selector() => zeigt kt_language_selector.tmpl an +----- print_content() => verweist auf print_content() im entsprechenden Modul +----- get_language() => wählt Sprache aus (URL, Browser-Settings oder 'de') +----- get_string() => übersetzt String +----- get_license() => erkennt bekannte Lizenzen und gibt Bild/URL zurück + +--- KTmusic.pm: +----- init() => initialisiert Musik-Teil +----- print_content() => verweist auf print_overview oder print_albumdetails +----- print_overview() => zeigt Albumliste +----- print_albumdetails() => zeigt Albumdetails +----- get_time() => zerlegt Sekundenanzahl in lesbaren String + +--- KTart.pm: +----- init() => initialisiert Kunst-Teil +----- print_content() => verweist auf print_overview oder print_details +----- print_overview() => zeigt Liste aller Kollektionen +----- print_details() => zeigt Details einer Kollektion diff --git a/cgi-bin/kt b/cgi-bin/kt new file mode 100755 index 0000000..f0d135e --- /dev/null +++ b/cgi-bin/kt @@ -0,0 +1,35 @@ +#!/usr/bin/perl -w +use strict; +use 5.010; + +# Standard perl modules +use CGI::Carp 'fatalsToBrowser'; # no more HTTP 500 errors :-) +use HTML::Template; +use XML::Simple; +use Data::Dumper; + +# Kulturtankstelle modules +use lib '/srv/www/kulturtankstelle/modules'; +use KTmain; +use KTmusic; +use KTart; + +# Global variables +my $configfile = '/srv/www/kulturtankstelle/config.xml'; +my $config; # XML configuration file +my $templates; # HTML templates + +# Initialization +print "Content-Type: text/html\r\n\r\n"; +$config = &KTmain::read_config($configfile) or die "Error reading config"; +$templates = &KTmain::read_templates($config) or die "Error reading templates"; + +&KTmusic::init($config); +&KTart::init($config); + +# Send out document +&KTmain::print_header($config, $templates->{header}); +&KTmain::print_menu($config, $templates->{menu}); +&KTmain::print_language_selector($config, $templates->{language_selector}); +&KTmain::print_content($config, $templates); +&KTmain::print_footer($config, $templates->{footer}); diff --git a/kulturtankstelle/config.xml b/kulturtankstelle/config.xml new file mode 100644 index 0000000..b2a6f90 --- /dev/null +++ b/kulturtankstelle/config.xml @@ -0,0 +1,71 @@ + + + + /srv/www/kulturtankstelle/ + /kulturtankstelle/ + /srv/www/data/ + /data/ + + + + + + + + + + + + + Startseite + Musik + Kunst + E-Books + Informationen + Lizenz + Stücke + Artist + Musik: Übersicht + Musik: Details + Track + Titel + Länge + [ Album herunterladen ] + Download + [ laden ] + Kunst: Übersicht + Kunst: Details + Künster + Stücke + Entstehungsjahr + Beschreibung + [ Kollection herunterladen ] + + + + start page + music + art + e-books + information + license + tracks + artist + music: overview + music: album details + track + title + length + download + [ get ] + [ fetch album ] + art: overview + art: collection details + artist + pieces + year + description + [ fetch collection ] + + + diff --git a/kulturtankstelle/header.jpg b/kulturtankstelle/header.jpg new file mode 100644 index 0000000..1414c33 Binary files /dev/null and b/kulturtankstelle/header.jpg differ diff --git a/kulturtankstelle/licenses/cc-by-nc-nd.png b/kulturtankstelle/licenses/cc-by-nc-nd.png new file mode 100644 index 0000000..072f8cd Binary files /dev/null and b/kulturtankstelle/licenses/cc-by-nc-nd.png differ diff --git a/kulturtankstelle/licenses/cc-by-nc-sa.png b/kulturtankstelle/licenses/cc-by-nc-sa.png new file mode 100644 index 0000000..ed028fe Binary files /dev/null and b/kulturtankstelle/licenses/cc-by-nc-sa.png differ diff --git a/kulturtankstelle/licenses/cc-by-nc.png b/kulturtankstelle/licenses/cc-by-nc.png new file mode 100644 index 0000000..54ebdfb Binary files /dev/null and b/kulturtankstelle/licenses/cc-by-nc.png differ diff --git a/kulturtankstelle/licenses/cc-by-nd.png b/kulturtankstelle/licenses/cc-by-nd.png new file mode 100644 index 0000000..35eca20 Binary files /dev/null and b/kulturtankstelle/licenses/cc-by-nd.png differ diff --git a/kulturtankstelle/licenses/cc-by-sa.png b/kulturtankstelle/licenses/cc-by-sa.png new file mode 100644 index 0000000..c67509f Binary files /dev/null and b/kulturtankstelle/licenses/cc-by-sa.png differ diff --git a/kulturtankstelle/licenses/cc-by.png b/kulturtankstelle/licenses/cc-by.png new file mode 100644 index 0000000..ecd2abc Binary files /dev/null and b/kulturtankstelle/licenses/cc-by.png differ diff --git a/kulturtankstelle/modules/KTart.pm b/kulturtankstelle/modules/KTart.pm new file mode 100644 index 0000000..47d2c79 --- /dev/null +++ b/kulturtankstelle/modules/KTart.pm @@ -0,0 +1,311 @@ +#!/usr/bin/perl -w +use strict; +use 5.010; + +package KTart; + +# package global art data +# $art->{$id}->{titles} is array of collection titles +# $art->{$id}->{collections} is hash of collection information hashes +my $art; + +# initialize KTart module (fill $art) +# in: $config +# out: true or undef +sub init +{ + my $c; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + # include every menu entry with type='art' + foreach my $entry (@{$c->{menu}->{entry}}) { + next unless ($entry->{id} eq 'art'); + + # fetch all collection folders + my $basepath = $c->{dirs}->{content} . $entry->{folder}; + unless(opendir(COLLECTIONS, $basepath)) { + say "Error: ".(caller 0)[3]. + ": can't open $basepath: $!"; + return; + } + my @folders = readdir(COLLECTIONS); + closedir(COLLECTIONS); + + # read and parse $folder/info.xml + foreach my $folder(sort @folders) { + next if $folder =~ /^\.+$/; + + # parse collection information file + my $infofile = "$basepath/$folder/info.xml"; + my $collection = eval { XML::Simple::XMLin( + $infofile, KeyAttr => []) }; + if($@) { + say "Error: ".(caller 0)[3].": $infofile: $@"; + next; + } + + # TODO: check data structures more thorough + unless($collection->{collectionname}) { + say "Error: ".(caller 0)[3]. + "$infofile contains no collectionname"; + next; + } + + # fill in collection title, folder and all other data + my $title = $collection->{collectionname}; + push (@{$art->{$entry->{id}}->{titles}}, $title); + $art->{$entry->{id}}->{collections}->{$title} = + $collection; + $art->{$entry->{id}}->{collections}->{$title}->{path} = + $folder; + } + } +} + +# check for art area overview or collection detail page +# in: $config, $entry (menu entry from $config), $templates +# out: nothing +sub print_content +{ + my $c; + my $entry; + my $templates; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($entry = shift) { + say "Error: ".(caller 0)[3].": no entry"; + return; + } + + unless($templates = shift) { + say "Error: ".(caller 0)[3].": no templates"; + return; + } + + # check for 'collection' parameter in QUERY_STRING + if($ENV{QUERY_STRING}) { + foreach(split(/;/, $ENV{QUERY_STRING})) { + (my $param, my $val) = split(/=/, $_); + next unless ($param =~ /^collection$/); + + # found -> print collection detail page + &KTart::print_details( + $c, + $entry, + $templates, + $val, + ); + return; + } + } + + # not found -> print collection overview + &KTart::print_overview( + $c, + $entry, + $templates, + ); +} + +# print collection overview +# in: $config, $entry (menu entry from $config), $templates +# out: nothing +sub print_overview +{ + my $c; + my $entry; + my $templates; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($entry = shift) { + say "Error: ".(caller 0)[3].": no entry"; + return; + } + + unless($templates = shift) { + say "Error: ".(caller 0)[3].": no templates"; + return; + } + + # set global variables + $templates->{art_overview}->param( + PAGETITLE => &KTmain::get_string($c, 'art_contents'), + SCRIPT => $ENV{SCRIPT_NAME}, + LANGUAGE => &KTmain::get_language($c), + MENUID => $entry->{id}, + MENUTITLE => &KTmain::get_string($c, $entry->{id}), + PIECES => &KTmain::get_string($c, 'art_pieces'), + ARTIST => &KTmain::get_string($c, 'art_artist'), + LICENSE => &KTmain::get_string($c, 'license'), + ); + + # set per-collection variables + my @collections = @{$art->{$entry->{id}}->{titles}}; + my @loopdata; + for my $index (0 .. $#collections) { + my %data; + my $info = $art->{$entry->{id}}->{collections}->{$collections[$index]}; + + $data{COLLECTIONID} = $index; + $data{COLLECTIONTITLE} = $collections[$index]; + $data{COLLECTIONCOVER} = $c->{dirs}->{webcontent} . + $entry->{folder} . + $info->{path} . + "/cover.jpg"; + $data{PIECESCOUNT} = $info->{collectionimagecount}; + $data{ARTISTNAME} = $info->{collectionartist}; + + # fill in license data + (my $url, my $img) = + &KTmain::get_license($c, $info->{collectionlicense}); + if(($url) && ($img)) { + $data{LICENSEURL} = $url; + $data{LICENSETEXT} = ""; + } else { + $data{LICENSEURL} = $c->{dirs}->{webcontent} . + $entry->{folder} . + $info->{path} . "/" . + $info->{collectionlicense}; + $data{LICENSETEXT} = + &KTmain::get_string($c, 'license'); + } + + push(@loopdata, \%data); + } + $templates->{art_overview}->param(COLLECTIONLOOP => \@loopdata); + + print $templates->{art_overview}->output; +} + +# print collection details +# in: $config, $entry, $templates, $collectionid +# ($collectionid: index in $art->{id]->{titles} array) +# out: nothing +sub print_details +{ + my $c; + my $entry; + my $templates; + my $collectionid; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($entry = shift) { + say "Error: ".(caller 0)[3].": no entry"; + return; + } + + unless($templates = shift) { + say "Error: ".(caller 0)[3].": no templates"; + return; + } + + unless(defined($collectionid = shift)) { + say "Error: ".(caller 0)[3].": no albumid"; + return; + } + + my $title = $art->{$entry->{id}}->{titles}->[$collectionid]; + my $info = $art->{$entry->{id}}->{collections}->{$title}; + + # set global variables + $templates->{art_details}->param( + # general + PAGETITLE => &KTmain::get_string($c, 'art_details'), + SCRIPT => $ENV{SCRIPT_NAME}, + LANGUAGE => &KTmain::get_language($c), + MENUID => $entry->{id}, + + # translations + MENUTITLE => &KTmain::get_string($c, $entry->{id}), + PIECES => &KTmain::get_string($c, 'art_pieces'), + ARTIST => &KTmain::get_string($c, 'art_artist'), + LICENSE => &KTmain::get_string($c, 'license'), +# TITLE => &KTmain::get_string($c, 'art_title'), +# LENGTH => &KTmain::get_string($c, 'art_length'), + DESCRIPTION => &KTmain::get_string($c, 'art_description'), + YEAR => &KTmain::get_string($c, 'art_year'), + FETCHCOLLECTION => &KTmain::get_string($c, 'art_fetchcollection'), +# DOWNLOAD => &KTmain::get_string($c, 'art_download'), +# DOWNLOADTEXT => &KTmain::get_string($c, 'art_downloadtext'), + + # collection specific + COLLECTIONID => $collectionid, + COLLECTIONTITLE => $title, + PIECESCOUNT => $info->{collectionimagecount}, + ARTISTNAME => $info->{collectionartist}, + LICENSETEXT => $info->{collectionlicense}, + COLLECTIONCOVER => $c->{dirs}->{webcontent} . + $entry->{folder} . + $info->{path} . + "/cover.jpg", + # TODO: dynamically create zipfile + FETCHCOLLECTIONLINK => "javascript:alert(\'not implemented\')", + ); + + # fill in license data + (my $url, my $img) = + &KTmain::get_license($c, $info->{albumlicense}); + if(($url) && ($img)) { + $templates->{art_details}->param( + LICENSEURL => $url, + LICENSETEXT => "", + ); + } else { + $templates->{art_details}->param( + LICENSEURL => $c->{dirs}->{webcontent} . + $entry->{folder} . + $info->{path} . "/" . + $info->{collectionlicense}, + LICENSETEXT => + &KTmain::get_string($c, 'license'), + ); + } + + # set collection-specific variables + my @loopdata; + foreach my $image (@{$info->{image}}) { + my %imagedata; + + $imagedata{IMAGETITLE} = $image->{title}; + $imagedata{IMAGEFILE} = $c->{dirs}->{webcontent} . + $entry->{folder} . + $info->{path} . "/" . + $image->{filename}; + + $imagedata{IMAGEFILE_THUMB} = $c->{dirs}->{webcontent} . + $entry->{folder} . + $info->{path} . "/" . + # FIXME: thumbnail filename! + substr($image->{filename}, 0, -4). + "_tn.jpg"; + + $imagedata{IMAGE_THUMBWIDTH} = $image->{thumbnailsizex}; + $imagedata{IMAGE_THUMBHEIGHT} = $image->{thumbnailsizey}; + $imagedata{ARTISTNAME} = $image->{artist}; + $imagedata{CREATIONYEAR} = $image->{year}; + $imagedata{DESCRIPTIONTEXT} = $image->{description}; + + push(@loopdata, \%imagedata); + } + $templates->{art_details}->param(IMAGELOOP => \@loopdata); + + print $templates->{art_details}->output; +} + +1; diff --git a/kulturtankstelle/modules/KTmain.pm b/kulturtankstelle/modules/KTmain.pm new file mode 100644 index 0000000..fa4ed5e --- /dev/null +++ b/kulturtankstelle/modules/KTmain.pm @@ -0,0 +1,400 @@ +#!/usr/bin/perl -w +use strict; +use 5.010; + +package KTmain; + +# read and parse xml configuration file +# in: $filename - filename of XML file +# out: $config - hash reference with parsed contents +sub read_config +{ + my $filename; + my $config; + + unless($filename = shift) { + say "Error: ".(caller 0)[3].": no configuration file"; + return; + } + + $config = eval { main::XMLin("$filename", KeyAttr => [ ] ); }; + if($@) { + say "Error: ".(caller 0)[3].": parsing failed: $@"; + return; + } + + return $config; +} + +# read all template files from template directory ($c->{dirs}->{templates}) +# in: $config - global configuration +# out: $templates - hash reference with template objects +sub read_templates +{ + my $c; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no configuration"; + return; + } + + unless($c->{dirs}->{root}) { + say "Error: ".(caller 0)[3].": no template directory"; + return; + } + + my $dir = "$c->{dirs}->{root}/templates"; + unless(opendir(TEMPLATES, $dir)) { + say "Error: ".(caller 0)[3].": $!"; + return; + } + + my %t; + while(readdir(TEMPLATES)) { + next unless /^kt_([0-9a-zA-Z_]+)\.tmpl$/; + $t{$1} = HTML::Template->new( + filename => "$dir/$_", + loop_context_vars => 1, + global_vars => 1, + ); + } + closedir(TEMPLATES); + + return \%t; +} + +# print header from template +# in: $config, $template +# out: nothing +sub print_header +{ + my $c; + my $t; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($t = shift) { + say "Error: ".(caller 0)[3].": no template"; + return; + } + + $t->param(WEBROOT => "$c->{dirs}->{webroot}"); + print $t->output; +} + +# print footer from template +# in: $config, $template +# out: nothing +sub print_footer +{ + my $c; + my $t; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($t = shift) { + say "Error: ".(caller 0)[3].": no template"; + return; + } + + print $t->output; +} + +# print menu from template +# in: $config, $template +# out: nothing +sub print_menu +{ + my $c; + my $t; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($t = shift) { + say "Error: ".(caller 0)[3].": no template"; + return; + } + + my @entries; + foreach(@{$c->{menu}->{entry}}) { + push (@entries, { + TEXT => &KTmain::get_string($c, $_->{id}), + URL => "$ENV{SCRIPT_NAME}?". + "lang=".&KTmain::get_language($c).";". + "id=$_->{id}", + }); + } + + $t->param(MENU_ENTRIES => [ @entries ]); + print $t->output; +} + +# print language selector +# in: $config, $template +# out: nothing +sub print_language_selector +{ + my $c; + my $t; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($t = shift) { + say "Error: ".(caller 0)[3].": no template"; + return; + } + + # fetch all request parameters, filter "lang" parameter + my @newparams; + if($ENV{REQUEST_URI}) { + (undef, my $params) = split(/\?/, $ENV{REQUEST_URI}); + foreach(split(/;/, $params)) { + (my $param, my $val) = split(/=/, $_); + next if $param =~ /^lang$/; + $val = "" unless $val; + push(@newparams, "$param=$val"); + } + } + + # find all languages + my $script = $ENV{SCRIPT_NAME}; + my @languages; + foreach my $lang (sort keys $c->{translation}) { + push (@languages, { + LANG => $lang, + URI => "$script?lang=$lang;".join(";", @newparams), + }); + + my $uri = "$script?lang=$lang;".join(";", @newparams); + } + $t->param(LANGUAGES => [ @languages ]); + print $t->output; +} + +# select content printing function based on 'id' +# in: $config and $templates (all of them) +# out: nothing +sub print_content +{ + my $c; + my $templates; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no configuration"; + return; + } + + unless($templates = shift) { + say "Error: ".(caller 0)[3].": no templates"; + return; + } + + # get page id + my $id; + if($ENV{QUERY_STRING}) { + foreach(split(/;/, $ENV{QUERY_STRING})) { + (my $param, my $val) = split(/=/, $_); + next unless ($param =~ /^id$/); + $id = $val; + } + } + + # get page type + my $entry; + foreach(@{$c->{menu}->{entry}}) { + if($id eq $_->{id}) { + $entry = $_; + } + } + + # switch/case depending on page type + given($entry->{type}) { + when ('music') { + # music area + &KTmusic::print_content( + $c, + $entry, + $templates); + } + + when ('art') { + # art area + &KTart::print_content( + $c, + $entry, + $templates); + } + + when ('litterature') { + # litterature area + &KTlitterature::print_content( + $c, + $entry, + $templates); + } + + when ('webpage') { + # webpage area + say "WEBPAGE at $entry->{folder}"; + } + + default { + # fallback: index page from template + print $templates->{index}->output; + } + } +} + +sub get_language +{ + my $c; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + # check QUERY_STRING for "lang=" + if($ENV{QUERY_STRING}) { + foreach(split(/;/, $ENV{QUERY_STRING})) { + (my $param, my $val) = split(/=/); + next unless ($param =~ /^lang$/); + + if($c->{translation}->{$val}) { + return $val; + } + } + } + + # check HTTP_ACCEPT_LANGUAGE + if($ENV{HTTP_ACCEPT_LANGUAGE}) { + my %ll; + foreach(split(/,/, $ENV{HTTP_ACCEPT_LANGUAGE})) { + (my $l, my $q) = split(/;q=/); + $q = 1 unless $q; + $ll{$l} = $q; + } + + # sort languages by q values, descending + foreach( sort { $ll{$b} <=> $ll{$a} } (keys %ll) ) { + if($c->{translation}->{$_}) { + return $_; + } + } + } + + # fallback + return "de"; +} + +# get translated string +# in: $config, $string +# out: string or undef +sub get_string +{ + my $c; + my $s; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($s = shift) { + say "Error: ".(caller 0)[3].": no string"; + return; + } + + my $lang = &KTmain::get_language($c); + + if($c->{translation}->{$lang}->{$s}) { + return $c->{translation}->{$lang}->{$s}; + } else { + return "!$s"; + } +} + +# return license and image file url for known licenses +# in: $config, license text (i.e. 'cc__by_nc_sa') +# out: [url, imageurl] if known license, [undef, undef] else +sub get_license +{ + my $config; + my $license; + + unless($config = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($license = shift) { + return (undef, undef); + } + + given($license) { + # CC license: see http://creativecommons.org/licenses/ + when('cc__by') { # CC: Attribution + return ( + $config->{dirs}->{webroot}. + "/licenses/cc-by.html", + $config->{dirs}->{webroot}. + "/licenses/cc-by.png", + ); + } + + when('cc__by_nc') { # CC: Attribution, NonCommercial + return ( + $config->{dirs}->{webroot}. + "/licenses/cc-by-nc.html", + $config->{dirs}->{webroot}. + "/licenses/cc-by-nc.png", + ); + } + + when('cc__by_nd') { # CC: Attribution, NoDerivs + $config->{dirs}->{webroot}. + "/licenses/cc-by-nd.html", + $config->{dirs}->{webroot}. + "/licenses/cc-by-nd.png", + } + + when('cc__by_sa') { # CC: Attribution, ShareAlike + $config->{dirs}->{webroot}. + "/licenses/cc-by-sa.html", + $config->{dirs}->{webroot}. + "/licenses/cc-by-sa.png", + } + + when('cc__by_nc_nd') { # CC: Attrib, NonCommercial, NoDerivs + $config->{dirs}->{webroot}. + "/licenses/cc-by-nc-nd.html", + $config->{dirs}->{webroot}. + "/licenses/cc-by-nc-nd.png", + } + + when('cc__by_nc_sa') { # CC: Attrib, NonCommercial, ShareAlike + $config->{dirs}->{webroot}. + "/licenses/cc-by-nc-sa.html", + $config->{dirs}->{webroot}. + "/licenses/cc-by-nc-sa.png", + } + + default { # anything else + return [undef, undef]; + } + } +} + +1; diff --git a/kulturtankstelle/modules/KTmusic.pm b/kulturtankstelle/modules/KTmusic.pm new file mode 100644 index 0000000..0704624 --- /dev/null +++ b/kulturtankstelle/modules/KTmusic.pm @@ -0,0 +1,321 @@ +#!/usr/bin/perl -w +use strict; +use 5.010; + +package KTmusic; + +# package global music data +# $music->{$id}->{titles} is array of album titles +# $music->{$id}->{albums} is hash of album information hashes +my $music; + +# initialize KTmusic module (create $music) +# in: $config +# out: true or undef +sub init +{ + my $c; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + # include every menu entry with type='music' + foreach my $entry (@{$c->{menu}->{entry}}) { + next unless ($entry->{id} eq 'music'); + + # fetch all album folders + my $basepath = $c->{dirs}->{content} . $entry->{folder}; + unless(opendir(ALBUMS, $basepath)) { + say "Error: ".(caller 0)[3]. + ": can't open $basepath: $!"; + return; + } + my @folders = readdir(ALBUMS); + closedir(ALBUMS); + + # read and parse $folder/info.xml + foreach my $folder(sort @folders) { + next if $folder =~ /^\.+$/; + + # parse album information file + my $infofile = "$basepath/$folder/info.xml"; + my $album = eval { XML::Simple::XMLin( + $infofile, KeyAttr => []) }; + if($@) { + say "Error: ".(caller 0)[3].": $infofile: $@"; + next; + } + + # TODO: check data structures more thorough + unless($album->{albumtitle}) { + say "Error: ".(caller 0)[3]. + "$infofile contains no album title"; + next; + } + + # fill in album title, folder and all other data + my $title = $album->{albumtitle}; + push (@{$music->{$entry->{id}}->{titles}}, $title); + $music->{$entry->{id}}->{albums}->{$title} = $album; + $music->{$entry->{id}}->{albums}->{$title}->{path} = + $folder; + } + } +} + +# check for music area overview or album detail page +# in: $config, $entry (menu entry from $config), $templates +# out: nothing +sub print_content +{ + my $c; + my $entry; + my $templates; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($entry = shift) { + say "Error: ".(caller 0)[3].": no entry"; + return; + } + + unless($templates = shift) { + say "Error: ".(caller 0)[3].": no templates"; + return; + } + + # check for 'albumid' parameter in QUERY_STRING + if($ENV{QUERY_STRING}) { + foreach(split(/;/, $ENV{QUERY_STRING})) { + (my $param, my $val) = split(/=/, $_); + next unless ($param =~ /^albumid$/); + + # found -> print album detail page + &KTmusic::print_albumdetails( + $c, + $entry, + $templates, + $val, + ); + return; + } + } + + # not found -> print album overview + &KTmusic::print_overview( + $c, + $entry, + $templates, + ); +} + +# print album overview +# in: $config, $entry (menu entry from $config), $templates +# out: nothing +sub print_overview +{ + my $c; + my $entry; + my $templates; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($entry = shift) { + say "Error: ".(caller 0)[3].": no entry"; + return; + } + + unless($templates = shift) { + say "Error: ".(caller 0)[3].": no templates"; + return; + } + + # set global variables + $templates->{music_overview}->param( + PAGETITLE => &KTmain::get_string($c, 'music_contents'), + SCRIPT => $ENV{SCRIPT_NAME}, + LANGUAGE => &KTmain::get_language($c), + MENUID => $entry->{id}, + MENUTITLE => &KTmain::get_string($c, $entry->{id}), + TRACKS => &KTmain::get_string($c, 'music_tracks'), + ARTIST => &KTmain::get_string($c, 'music_artist'), + LICENSE => &KTmain::get_string($c, 'license'), + ); + + # set per-album variables + my @albums = @{$music->{$entry->{id}}->{titles}}; + my @loopdata; + for my $index (0 .. $#albums) { + my %albumdata; + my $albuminfo = + $music->{$entry->{id}}->{albums}->{$albums[$index]}; + + $albumdata{ALBUMID} = $index; + $albumdata{ALBUMTITLE} = $albums[$index]; + $albumdata{ALBUMCOVER} = $c->{dirs}->{webcontent} . + $entry->{folder} . + $albuminfo->{path} . + "/cover.jpg"; + $albumdata{TRACKCOUNT} = $albuminfo->{albumtrackcount}; + $albumdata{ARTISTNAME} = $albuminfo->{albumartist}; + $albumdata{LICENSETEXT} = $albuminfo->{albumlicense}; + + # fill in license data + (my $url, my $img) = + &KTmain::get_license($c, $albuminfo->{albumlicense}); + if(($url) && ($img)) { + $albumdata{LICENSEURL} = $url; + $albumdata{LICENSETEXT} = ""; + } else { + $albumdata{LICENSEURL} = $c->{dirs}->{webcontent} . + $entry->{folder} . + $albuminfo->{path} . "/" . + $albuminfo->{albumlicense}; + $albumdata{LICENSETEXT} = + &KTmain::get_string($c, 'license'); + } + + push(@loopdata, \%albumdata); + } + $templates->{music_overview}->param(ALBUMLOOP => \@loopdata); + + print $templates->{music_overview}->output; +} + +# print album details +# in: $config, $entry, $templates, $albumid +# ($albumid: index in $music->{id]->{titles} array) +# out: nothing +sub print_albumdetails +{ + my $c; + my $entry; + my $templates; + my $albumid; + + unless($c = shift) { + say "Error: ".(caller 0)[3].": no config"; + return; + } + + unless($entry = shift) { + say "Error: ".(caller 0)[3].": no entry"; + return; + } + + unless($templates = shift) { + say "Error: ".(caller 0)[3].": no templates"; + return; + } + + unless(defined($albumid = shift)) { + say "Error: ".(caller 0)[3].": no albumid"; + return; + } + + my $albumtitle = $music->{$entry->{id}}->{titles}->[$albumid]; + my $albuminfo = $music->{$entry->{id}}->{albums}->{$albumtitle}; + + # set global variables + $templates->{music_details}->param( + # general + PAGETITLE => &KTmain::get_string($c, 'music_details'), + SCRIPT => $ENV{SCRIPT_NAME}, + LANGUAGE => &KTmain::get_language($c), + MENUID => $entry->{id}, + + # translations + MENUTITLE => &KTmain::get_string($c, $entry->{id}), + TRACKS => &KTmain::get_string($c, 'music_tracks'), + ARTIST => &KTmain::get_string($c, 'music_artist'), + LICENSE => &KTmain::get_string($c, 'license'), + TRACK => &KTmain::get_string($c, 'music_track'), + TITLE => &KTmain::get_string($c, 'music_title'), + LENGTH => &KTmain::get_string($c, 'music_length'), + DOWNLOAD => &KTmain::get_string($c, 'music_download'), + DOWNLOADTEXT => &KTmain::get_string($c, 'music_downloadtext'), + FETCHALBUM => &KTmain::get_string($c, 'music_fetchalbum'), + + # album specific + ALBUMID => $albumid, + ALBUMTITLE => $albumtitle, + TRACKCOUNT => $albuminfo->{albumtrackcount}, + ARTISTNAME => $albuminfo->{albumartist}, + LICENSETEXT => $albuminfo->{albumlicense}, + ALBUMCOVER => $c->{dirs}->{webcontent} . + $entry->{folder} . + $albuminfo->{path} . + "/cover.jpg", + # TODO: dynamically create zipfile + FETCHALBUMLINK => "javascript:alert(\'not implemented\')", + ); + + # fill in license data + (my $url, my $img) = + &KTmain::get_license($c, $albuminfo->{albumlicense}); + if(($url) && ($img)) { + $templates->{music_details}->param( + LICENSEURL => $url, + LICENSETEXT => "", + ); + } else { + $templates->{music_details}->param( + LICENSEURL => $c->{dirs}->{webcontent} . + $entry->{folder} . + $albuminfo->{path} . "/" . + $albuminfo->{albumlicense}, + LICENSETEXT => + &KTmain::get_string($c, 'license'), + ); + } + + # set track-specific variables + my @loopdata; + foreach my $track (@{$albuminfo->{song}}) { + my %trackdata; + + $trackdata{TRACKTITLE} = $track->{title}, + $trackdata{TRACKLENGTH} = &get_time($track->{playtime}), + $trackdata{TRACKPATH} = $c->{dirs}->{webcontent} . + $entry->{folder} . + $albuminfo->{path} . '/' . + $track->{filename}, + + push(@loopdata, \%trackdata); + } + $templates->{music_details}->param(TRACKLOOP => \@loopdata); + + print $templates->{music_details}->output; +} + +# turn number of seconds into human-readable format m:ss or h:mm:ss +# in: $num_seconds +# out: string or undef +sub get_time +{ + my $seconds; + my $output; + + unless($seconds = shift) { + say "Error: ".(caller 0)[3].": no seconds"; + return; + } + + (my $sec, my $min, my $hour) = gmtime($seconds); + if($hour) { + $output = sprintf("%u:%02u:%02u", $hour, $min, $sec); + } else { + $output = sprintf("%u:%02u", $min, $sec); + } + return $output; +} + +1; diff --git a/kulturtankstelle/style.css b/kulturtankstelle/style.css new file mode 100644 index 0000000..0271d51 --- /dev/null +++ b/kulturtankstelle/style.css @@ -0,0 +1,168 @@ +/* stylesheet fuer kulturtankstelle */ + +/* main */ + +body { + margin: 0; + padding: 0; + font-family: sans-serif; +} + +a { + font-weight: bold; + text-decoration: none; + color: black; +} + +a:hover { + color: #FF6600; +} + +/* menu */ + +.menu { + background-image: url(header.jpg); + background-repeat: no-repeat; + background-color: gray; + width: 150px; + height: 100%; + padding-top: 134px; + float: left; +} + +.menu ul { + list-style: none; + padding: 5px; +} + +.menu ul a { + display: block; + width: 100%; + border: 1px solid black; + border-left-color: white; + border-top-color: white; + background-color: silver; +} + +.menu ul a:hover { + width: 100%; + border-color: white; + border-left-color: black; + border-top-color: black; + background-color: silver; +} + +/* content */ + +.content { + position: absolute; + left: 150px; + width: 80%; + margin: 0; + padding-top: 20px; + padding-right: 10px; + padding-bottom: 20px; + padding-left: 10px; +} + +.content_header { + font-size: 16px; + font-weight: bold; + padding-top: 50px; + padding-bottom: 50px; +} + +.content_linkpath { + font-size: 10px; +} + +.language_selector { + text-align: right; + font-size: 12pt; +} + +/* music */ + +.music_cover { + border: 1px solid black; + float: left; + margin-right: 10px; + width: 100px; + height: 100px; +} + +.music_album { + height: 105px; + font-size: 12px; +} + +.music_albumtitle { + font-size: 16px; + font-weight: bold; + padding-bottom: 20px; +} + +.music_content table { + border-style: solid; + border-width: 1px; +} + +.music_content tr#rowhead { + background-color: #c0c0c0; +} + +.music_content tr#row0 { + background-color: #ffffff; +} + +.music_content tr#row1 { + background-color: #e0e0e0; +} + +.music_content td { + padding-left: 5px; + padding-right: 20px; +} + +.music_content table a { + font-weight: normal; + text-decoration: underline; +} + +/* art */ + +.art_cover { + border: 1px solid black; + float: left; + margin-right: 10px; + width: 100px; + height: 100px; +} + +.art_collection { + height: 105px; + font-size: 12px; +} + +.art_collectiontitle { + font-size: 16px; + font-weight: bold; + padding-bottom: 20px; +} + +.art_content { + font-size: 12px; +} + +.art_image { + border: 1px solid black; + background-color: #333333; + padding: 10px; +} + +.art_imagetitle { + font-size: 16px; + font-weight: bold; + padding-bottom: 20px; +} + diff --git a/kulturtankstelle/templates/kt_art_details.tmpl b/kulturtankstelle/templates/kt_art_details.tmpl new file mode 100644 index 0000000..772d2ca --- /dev/null +++ b/kulturtankstelle/templates/kt_art_details.tmpl @@ -0,0 +1,40 @@ +
+
+ +
+ +
+ Kulturtankstelle >> + >> + +
+ +
+ + <TMPL_VAR NAME=COLLECTIONTITLE> + + +
+ :
+ :
+ :

+
+

+ +
+ +
+ +
+ + +
+ :
+ :
+ :
+
+
+
+
+
+ diff --git a/kulturtankstelle/templates/kt_art_overview.tmpl b/kulturtankstelle/templates/kt_art_overview.tmpl new file mode 100644 index 0000000..406d94a --- /dev/null +++ b/kulturtankstelle/templates/kt_art_overview.tmpl @@ -0,0 +1,27 @@ +
+
+ +
+ +
+ Kulturtankstelle >> + +
+ + +
+ + <TMPL_VAR NAME=COLLECTIONTITLE> + + +
+ +
+ + :
+ :
+ :
+

+
+
+ diff --git a/kulturtankstelle/templates/kt_footer.tmpl b/kulturtankstelle/templates/kt_footer.tmpl new file mode 100644 index 0000000..b605728 --- /dev/null +++ b/kulturtankstelle/templates/kt_footer.tmpl @@ -0,0 +1,2 @@ + + diff --git a/kulturtankstelle/templates/kt_header.tmpl b/kulturtankstelle/templates/kt_header.tmpl new file mode 100644 index 0000000..50b4f65 --- /dev/null +++ b/kulturtankstelle/templates/kt_header.tmpl @@ -0,0 +1,7 @@ + + + Kulturtankstelle + + + + diff --git a/kulturtankstelle/templates/kt_index.tmpl b/kulturtankstelle/templates/kt_index.tmpl new file mode 100644 index 0000000..24ea4e1 --- /dev/null +++ b/kulturtankstelle/templates/kt_index.tmpl @@ -0,0 +1,7 @@ +
+
+ Kulturtankstelle +
+ + Hier kommt ein schöner Willkommengruß hin! +
diff --git a/kulturtankstelle/templates/kt_language_selector.tmpl b/kulturtankstelle/templates/kt_language_selector.tmpl new file mode 100644 index 0000000..d8dfe03 --- /dev/null +++ b/kulturtankstelle/templates/kt_language_selector.tmpl @@ -0,0 +1,3 @@ +
+ +
diff --git a/kulturtankstelle/templates/kt_menu.tmpl b/kulturtankstelle/templates/kt_menu.tmpl new file mode 100644 index 0000000..7039a82 --- /dev/null +++ b/kulturtankstelle/templates/kt_menu.tmpl @@ -0,0 +1,7 @@ + diff --git a/kulturtankstelle/templates/kt_music_details.tmpl b/kulturtankstelle/templates/kt_music_details.tmpl new file mode 100644 index 0000000..8d5e6ef --- /dev/null +++ b/kulturtankstelle/templates/kt_music_details.tmpl @@ -0,0 +1,50 @@ +
+
+ +
+ +
+ Kulturtankstelle >> + >> + +
+ +
+ + <TMPL_VAR NAME=ALBUMTITLE> + + +
+ :
+ :
+ :

+
+

+ + +
+ + + + + + + + + + + + + + + + + + + + +
+
+
+
+ diff --git a/kulturtankstelle/templates/kt_music_overview.tmpl b/kulturtankstelle/templates/kt_music_overview.tmpl new file mode 100644 index 0000000..ffe2a3a --- /dev/null +++ b/kulturtankstelle/templates/kt_music_overview.tmpl @@ -0,0 +1,27 @@ +
+
+ +
+ +
+ Kulturtankstelle >> + +
+ + +
+ + <TMPL_VAR NAME=ALBUMTITLE> + + +
+ +
+ + :
+ :
+ :
+

+
+
+