--- /dev/null
+Kulturtankstelle
+================
+
+Kontakt: <svenska-pirat@arcor.de> oder Mailingliste unter
+ <kulturtankstelle@lists.piratenpartei-bremen.de>
+
+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
--- /dev/null
+#!/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});
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<kulturtankstelle>
+ <dirs>
+ <root>/srv/www/kulturtankstelle/</root>
+ <webroot>/kulturtankstelle/</webroot>
+ <content>/srv/www/data/</content>
+ <webcontent>/data/</webcontent>
+ </dirs>
+
+ <menu>
+ <entry id="startpage" type="index" />
+ <entry id="music" type="music" folder="music/" />
+ <entry id="art" type="art" folder="art/" />
+ <entry id="ebooks" type="litterature" folder="litterature/" />
+ <entry id="information" type="webpage" folder="information/" />
+ </menu>
+
+ <translation>
+ <de>
+ <startpage>Startseite</startpage>
+ <music>Musik</music>
+ <art>Kunst</art>
+ <ebooks>E-Books</ebooks>
+ <information>Informationen</information>
+ <license>Lizenz</license>
+ <music_tracks>Stücke</music_tracks>
+ <music_artist>Artist</music_artist>
+ <music_contents>Musik: Übersicht</music_contents>
+ <music_details>Musik: Details</music_details>
+ <music_track>Track</music_track>
+ <music_title>Titel</music_title>
+ <music_length>Länge</music_length>
+ <music_fetchalbum>[ Album herunterladen ]</music_fetchalbum>
+ <music_download>Download</music_download>
+ <music_downloadtext>[ laden ]</music_downloadtext>
+ <art_contents>Kunst: Übersicht</art_contents>
+ <art_details>Kunst: Details</art_details>
+ <art_artist>Künster</art_artist>
+ <art_pieces>Stücke</art_pieces>
+ <art_year>Entstehungsjahr</art_year>
+ <art_description>Beschreibung</art_description>
+ <art_fetchcollection>[ Kollection herunterladen ]</art_fetchcollection>
+ </de>
+
+ <en>
+ <startpage>start page</startpage>
+ <music>music</music>
+ <art>art</art>
+ <ebooks>e-books</ebooks>
+ <information>information</information>
+ <license>license</license>
+ <music_tracks>tracks</music_tracks>
+ <music_artist>artist</music_artist>
+ <music_contents>music: overview</music_contents>
+ <music_details>music: album details</music_details>
+ <music_track>track</music_track>
+ <music_title>title</music_title>
+ <music_length>length</music_length>
+ <music_download>download</music_download>
+ <music_downloadtext>[ get ]</music_downloadtext>
+ <music_fetchalbum>[ fetch album ]</music_fetchalbum>
+ <art_contents>art: overview</art_contents>
+ <art_details>art: collection details</art_details>
+ <art_artist>artist</art_artist>
+ <art_pieces>pieces</art_pieces>
+ <art_year>year</art_year>
+ <art_description>description</art_description>
+ <art_fetchcollection>[ fetch collection ]</art_fetchcollection>
+ </en>
+ </translation>
+</kulturtankstelle>
--- /dev/null
+#!/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} = "<img src=\"$img\">";
+ } 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 => "<img src=\"$img\">",
+ );
+ } 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;
--- /dev/null
+#!/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;
--- /dev/null
+#!/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} = "<img src=\"$img\">";
+ } 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 => "<img src=\"$img\">",
+ );
+ } 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;
--- /dev/null
+/* 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;
+}
+
--- /dev/null
+<div class="content">
+ <div class="content_header">
+ <TMPL_VAR NAME=PAGETITLE>
+ </div>
+
+ <div class="content_linkpath">
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>">Kulturtankstelle</a> >>
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>;id=<TMPL_VAR NAME=MENUID>"><TMPL_VAR NAME=MENUTITLE></a> >>
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>;id=<TMPL_VAR NAME=MENUID>;collection=<TMPL_VAR NAME=COLLECTIONID>"><TMPL_VAR NAME=COLLECTIONTITLE></a>
+ </div>
+
+ <div class="art_collection">
+ <a href="<TMPL_VAR NAME=COLLECTIONCOVER>">
+ <img class="art_cover" src="<TMPL_VAR NAME=COLLECTIONCOVER>" height="100" width="100" alt="<TMPL_VAR NAME=COLLECTIONTITLE>">
+ </a>
+
+ <div class="art_collectiontitle"><TMPL_VAR NAME=COLLECTIONTITLE></div>
+ <TMPL_VAR NAME=ARTIST>: <TMPL_VAR NAME=ARTISTNAME><br />
+ <TMPL_VAR NAME=PIECES>: <TMPL_VAR NAME=PIECESCOUNT><br />
+ <TMPL_VAR NAME=LICENSE>: <a href="<TMPL_VAR NAME=LICENSEURL>"><TMPL_VAR NAME=LICENSETEXT></a><br /><br />
+ <a href="<TMPL_VAR NAME=FETCHCOLLECTIONLINK>"><TMPL_VAR NAME=FETCHCOLLECTION></a><br />
+ </div><br />
+
+ <div class="art_content">
+ <TMPL_LOOP NAME=IMAGELOOP>
+ <div class="art_imagetitle">
+ <TMPL_VAR NAME=IMAGETITLE>
+ </div>
+ <a href="<TMPL_VAR NAME=IMAGEFILE>">
+ <img src="<TMPL_VAR NAME=IMAGEFILE_THUMB>" class="art_image" width="<TMPL_VAR NAME=IMAGE_THUMBWIDTH>" height="<TMPL_VAR NAME=IMAGE_THUMBHEIGHT>">
+ </a><br />
+ <TMPL_VAR NAME=ARTIST>: <TMPL_VAR NAME=ARTISTNAME><br />
+ <TMPL_VAR NAME=YEAR>: <TMPL_VAR NAME=CREATIONYEAR><br />
+ <TMPL_VAR NAME=DESCRIPTION>:<br />
+ <TMPL_VAR NAME=DESCRIPTIONTEXT><br />
+ <br />
+ </TMPL_LOOP>
+ </div>
+ </div>
+</div>
--- /dev/null
+<div class="content">
+ <div class="content_header">
+ <TMPL_VAR NAME=PAGETITLE>
+ </div>
+
+ <div class="content_linkpath">
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>">Kulturtankstelle</a> >>
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>;id=<TMPL_VAR NAME=MENUID>"><TMPL_VAR NAME=MENUTITLE></a>
+ </div>
+
+ <TMPL_LOOP NAME=COLLECTIONLOOP>
+ <div class="art_collection">
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>;id=<TMPL_VAR NAME=MENUID>;collection=<TMPL_VAR NAME=COLLECTIONID>">
+ <img class="art_cover" src="<TMPL_VAR NAME=COLLECTIONCOVER>" height="100" width="100" alt="<TMPL_VAR NAME=COLLECTIONTITLE>">
+ </a>
+
+ <div class="art_collectiontitle">
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>;id=<TMPL_VAR NAME=MENUID>;collection=<TMPL_VAR NAME=COLLECTIONID>"><TMPL_VAR NAME=COLLECTIONTITLE></a>
+ </div>
+
+ <TMPL_VAR NAME=ARTIST>: <TMPL_VAR NAME=ARTISTNAME><br />
+ <TMPL_VAR NAME=PIECES>: <TMPL_VAR NAME=PIECESCOUNT><br />
+ <TMPL_VAR NAME=LICENSE>: <a href="<TMPL_VAR NAME=LICENSEURL>"><TMPL_VAR NAME=LICENSETEXT></a><br />
+ </div><br />
+ </TMPL_LOOP>
+</div>
+
--- /dev/null
+ </body>
+</html>
--- /dev/null
+<html>
+ <head>
+ <title>Kulturtankstelle</title>
+ <link href="<TMPL_VAR NAME=WEBROOT>/style.css" rel="stylesheet" type="text/css" />
+ </head>
+
+ <body>
--- /dev/null
+<div class="content">
+ <div class="content_header">
+ Kulturtankstelle
+ </div>
+
+ Hier kommt ein schöner Willkommengruß hin!
+</div>
--- /dev/null
+<div class="language_selector"><TMPL_LOOP NAME=LANGUAGES>
+ <a href="<TMPL_VAR NAME=URI>"><TMPL_VAR NAME=LANG></a></TMPL_LOOP>
+</div>
--- /dev/null
+<div class="menu">
+ <ul><TMPL_LOOP NAME="MENU_ENTRIES">
+ <li>
+ <a href="<TMPL_VAR NAME=URL>"><TMPL_VAR ESCAPE=HTML NAME=TEXT></a>
+ </li></TMPL_LOOP>
+ </ul>
+</div>
--- /dev/null
+<div class="content">
+ <div class="content_header">
+ <TMPL_VAR NAME=PAGETITLE>
+ </div>
+
+ <div class="content_linkpath">
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>">Kulturtankstelle</a> >>
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>;id=<TMPL_VAR NAME=MENUID>"><TMPL_VAR NAME=MENUTITLE></a> >>
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>;id=<TMPL_VAR NAME=MENUID>;albumid=<TMPL_VAR NAME=ALBUMID>"><TMPL_VAR NAME=ALBUMTITLE></a>
+ </div>
+
+ <div class="music_album">
+ <a href="<TMPL_VAR NAME=ALBUMCOVER>">
+ <img class="music_cover" src="<TMPL_VAR NAME=ALBUMCOVER>" height="100" width="100" alt="<TMPL_VAR NAME=ALBUMTITLE>">
+ </a>
+
+ <div class="music_albumtitle"><TMPL_VAR NAME=ALBUMTITLE></div>
+ <TMPL_VAR NAME=TRACKS>: <TMPL_VAR NAME=TRACKCOUNT><br />
+ <TMPL_VAR NAME=ARTIST>: <TMPL_VAR NAME=ARTISTNAME><br />
+ <TMPL_VAR NAME=LICENSE>: <a href="<TMPL_VAR NAME=LICENSEURL>"><TMPL_VAR NAME=LICENSETEXT></a><br /><br />
+ <a href="<TMPL_VAR NAME=FETCHALBUMLINK>"><TMPL_VAR NAME=FETCHALBUM></a><br />
+ </div><br />
+
+
+ <div class="music_content">
+ <table>
+ <tr id="rowhead">
+ <th><TMPL_VAR NAME=TRACK></th>
+ <th><TMPL_VAR NAME=TITLE></th>
+ <th><TMPL_VAR NAME=LENGTH></th>
+ <th><TMPL_VAR NAME=DOWNLOAD></th>
+ </tr>
+
+ <TMPL_LOOP NAME=TRACKLOOP>
+ <TMPL_IF NAME="__odd__">
+ <tr id="row1">
+ <TMPL_ELSE>
+ <tr id="row2">
+ </TMPL_IF>
+ <td><TMPL_VAR NAME="__counter__"></td>
+ <td><TMPL_VAR NAME=TRACKTITLE></td>
+ <td><TMPL_VAR NAME=TRACKLENGTH></td>
+ <td><a href="<TMPL_VAR NAME=TRACKPATH>">
+ <TMPL_VAR NAME=DOWNLOADTEXT></a></td>
+ </tr>
+ </TMPL_LOOP>
+ </table>
+ </div>
+ </div>
+</div>
--- /dev/null
+<div class="content">
+ <div class="content_header">
+ <TMPL_VAR NAME=PAGETITLE>
+ </div>
+
+ <div class="content_linkpath">
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>">Kulturtankstelle</a> >>
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>;id=<TMPL_VAR NAME=MENUID>"><TMPL_VAR NAME=MENUTITLE></a>
+ </div>
+
+ <TMPL_LOOP NAME=ALBUMLOOP>
+ <div class="music_album">
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>;id=<TMPL_VAR NAME=MENUID>;albumid=<TMPL_VAR NAME=ALBUMID>">
+ <img class="music_cover" src="<TMPL_VAR NAME=ALBUMCOVER>" height="100" width="100" alt="<TMPL_VAR NAME=ALBUMTITLE>">
+ </a>
+
+ <div class="music_albumtitle">
+ <a href="<TMPL_VAR NAME=SCRIPT>?lang=<TMPL_VAR NAME=LANGUAGE>;id=<TMPL_VAR NAME=MENUID>;albumid=<TMPL_VAR NAME=ALBUMID>"><TMPL_VAR NAME=ALBUMTITLE></a>
+ </div>
+
+ <TMPL_VAR NAME=ARTIST>: <TMPL_VAR NAME=ARTISTNAME><br />
+ <TMPL_VAR NAME=TRACKS>: <TMPL_VAR NAME=TRACKCOUNT><br />
+ <TMPL_VAR NAME=LICENSE>: <a href="<TMPL_VAR NAME=LICENSEURL>"><TMPL_VAR NAME=LICENSETEXT></a><br />
+ </div><br />
+ </TMPL_LOOP>
+</div>
+