forked from HPR/hpr_generator
		
	Fixing issue #140
site-generator:
    Cosmetic adjustments. Additions to the POD documentation.
    Additions to module list.
    Additions to work better with UTF-8.
    Addition of functions 'parse_csv' and 'xml_entity'
templates/queries-episodes-sqlite.tpl.html:
templates/shared-utils.tpl.html:
    Cosmetic changes
templates/rss-query-hpr-mysql.tpl.xml:
templates/rss-query-hpr-sqlite.tpl.xml:
templates/rss-query-hpr_total-mysql.tpl.xml:
templates/rss-query-hpr_total-sqlite.tpl.xml:
    Enhancements to allow the query to collect the audio length from the
    'assets' table. The audio file extension is passed as an argument to
    the 'execute' statement.
templates/rss.tpl.xml:
    Cosmetic changes
    Changed one 'php' URL to 'html'.
templates/shared-episode-summary.tpl.html:
    Change to 'display_tags' macro to turn the 'eps.tags' field into
    a list of links. This works, but needs further development because
    using the tag strings as anchor ids is not reliable.
templates/shared-item.tpl.xml:
    Cosmetic changes.
    Addition of filter 'HTML.strip' which is used as a means of removing
    HTML tags from '<itunes:summary>' strings.
    Using new filter 'xml_entity' which converts all non-ASCII
    characters in the notes to numeric hexadecimal entities for
    '<itunes:summary>'.
    The '<enclosure>' tag now uses 'episode.length' rather than
    'episode.duration' which has been extracted from the 'assets' table.
			
			
This commit is contained in:
		
							
								
								
									
										570
									
								
								site-generator
									
									
									
									
									
								
							
							
						
						
									
										570
									
								
								site-generator
									
									
									
									
									
								
							@@ -1,88 +1,92 @@
 | 
			
		||||
#!/usr/bin/perl
 | 
			
		||||
 | 
			
		||||
# {{{ POD documentation
 | 
			
		||||
 | 
			
		||||
=head1 NAME
 | 
			
		||||
 | 
			
		||||
	site-generator - HPR Site Generator
 | 
			
		||||
        site-generator - HPR Site Generator
 | 
			
		||||
 | 
			
		||||
=head1 SYNOPSIS
 | 
			
		||||
 | 
			
		||||
	site-generator [OPTION]... PAGE|PAGE=<comma separated list of ids>...
 | 
			
		||||
        site-generator [OPTION]... PAGE|PAGE=<comma separated list of ids>...
 | 
			
		||||
 | 
			
		||||
	-a, --all           generate all pages defined in configuration file
 | 
			
		||||
	-c, --configuration path to configuration file
 | 
			
		||||
	-l, --list          print list of configured pages
 | 
			
		||||
	-p, --preview       print generated pages to standard out
 | 
			
		||||
	-q, --quiet         suppress progress information while generating pages
 | 
			
		||||
	-v, --verbose       print extended progress information while generating pages
 | 
			
		||||
	--help              print this help message
 | 
			
		||||
        -a, --all           generate all pages defined in configuration file
 | 
			
		||||
        -c, --configuration path to configuration file
 | 
			
		||||
        -l, --list          print list of configured pages
 | 
			
		||||
        -p, --preview       print generated pages to standard out
 | 
			
		||||
        -q, --quiet         suppress progress information while generating pages
 | 
			
		||||
        -v, --verbose       print extended progress information while generating pages
 | 
			
		||||
        --help              print this help message
 | 
			
		||||
 | 
			
		||||
	Where I<PAGE> is a file name of a web page 
 | 
			
		||||
	or the special I<ALL> (to generate all pages).
 | 
			
		||||
        Where I<PAGE> is a file name of a web page
 | 
			
		||||
        or the special I<ALL> (to generate all pages).
 | 
			
		||||
 | 
			
		||||
	Examples:
 | 
			
		||||
        Examples:
 | 
			
		||||
 | 
			
		||||
		Generate two specific pages:
 | 
			
		||||
		site-generator index about
 | 
			
		||||
                Generate two specific pages:
 | 
			
		||||
                site-generator index about
 | 
			
		||||
 | 
			
		||||
		Generate the whole site:
 | 
			
		||||
		site-generator --all
 | 
			
		||||
                Generate the whole site:
 | 
			
		||||
                site-generator --all
 | 
			
		||||
 | 
			
		||||
		Generate pages based on the same template:
 | 
			
		||||
		site-generator correspondent=1,3,5..10
 | 
			
		||||
                Generate pages based on the same template:
 | 
			
		||||
                site-generator correspondent=1,3,5..10
 | 
			
		||||
 | 
			
		||||
		Generate two specific pages with a different configuration:
 | 
			
		||||
		site-generator --configuration=site_sqlite.cfg index about
 | 
			
		||||
                Generate two specific pages with a different configuration:
 | 
			
		||||
                site-generator --configuration=site_sqlite.cfg index about
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
=head1 DESCRIPTION
 | 
			
		||||
 | 
			
		||||
This is a site generator for the Hacker Public Radio website based upon the Perl Templates Toolkit.
 | 
			
		||||
This is a site generator for the Hacker Public Radio website based upon the
 | 
			
		||||
Perl Template Toolkit.
 | 
			
		||||
 | 
			
		||||
=head1 INSTALLATION
 | 
			
		||||
 | 
			
		||||
	With SQLite
 | 
			
		||||
	* Create the sqlite3 database from the hpr.sql MySQL dump file available on 
 | 
			
		||||
	  hackerpublicradio.org. The default name for the database file is "hpr.db" 
 | 
			
		||||
	  and should be located in the root of the project directory. The name and 
 | 
			
		||||
	  location can be set in the site.cfg file.
 | 
			
		||||
	* An "update-hpr.sh" helper script is available in the utils directory. This 
 | 
			
		||||
	  script will download the hpr.sql file, convert it to the SQLite hpr.db file, 
 | 
			
		||||
	  and regenerate the website using the site-generator. 
 | 
			
		||||
		1. `cd` into the root of the project directory
 | 
			
		||||
		2.  Run `./utils/update-hpr.sh`
 | 
			
		||||
	* SQLite v3.8.3 or greater is recommended. CTE WITH clauses are used in some template queries. 
 | 
			
		||||
	  Must convert WITH clauses to sub-queries when using earlier versions of SQLite.
 | 
			
		||||
        With SQLite
 | 
			
		||||
        * Create the sqlite3 database from the hpr.sql MySQL dump file available on
 | 
			
		||||
          hackerpublicradio.org. The default name for the database file is "hpr.db"
 | 
			
		||||
          and should be located in the root of the project directory. The name and
 | 
			
		||||
          location can be set in the site.cfg file.
 | 
			
		||||
        * An "update-hpr.sh" helper script is available in the utils directory. This
 | 
			
		||||
          script will download the hpr.sql file, convert it to the SQLite hpr.db file,
 | 
			
		||||
          and regenerate the website using the site-generator.
 | 
			
		||||
                1. `cd` into the root of the project directory
 | 
			
		||||
                2.  Run `./utils/update-hpr.sh`
 | 
			
		||||
        * SQLite v3.8.3 or greater is recommended. CTE WITH clauses are used in some template queries.
 | 
			
		||||
          Must convert WITH clauses to sub-queries when using earlier versions of SQLite.
 | 
			
		||||
 | 
			
		||||
	With MySQL
 | 
			
		||||
	* Create database hpr_hpr in the MySQL server from HPR dump file.
 | 
			
		||||
		- sudo mysql --host=localhost < hpr.sql
 | 
			
		||||
	* Create a user that will be used by the site-generator.
 | 
			
		||||
		- Suggested username: hpr-generator
 | 
			
		||||
		- CREATE USER 'hpr-generator'@'localhost' IDENTIFIED BY '<password>';
 | 
			
		||||
	* Limit the user's privileges to EXECUTE and SELECT
 | 
			
		||||
		- GRANT SELECT ON hpr_hpr.* TO 'hpr-generator'@'localhost';
 | 
			
		||||
		- GRANT EXECUTE ON `hpr_hpr`.* TO 'hpr-generator'@'localhost';
 | 
			
		||||
        With MySQL
 | 
			
		||||
        * Create database hpr_hpr in the MySQL server from HPR dump file.
 | 
			
		||||
                - sudo mysql --host=localhost < hpr.sql
 | 
			
		||||
        * Create a user that will be used by the site-generator.
 | 
			
		||||
                - Suggested username: hpr-generator
 | 
			
		||||
                - CREATE USER 'hpr-generator'@'localhost' IDENTIFIED BY '<password>';
 | 
			
		||||
        * Limit the user's privileges to EXECUTE and SELECT
 | 
			
		||||
                - GRANT SELECT ON hpr_hpr.* TO 'hpr-generator'@'localhost';
 | 
			
		||||
                - GRANT EXECUTE ON `hpr_hpr`.* TO 'hpr-generator'@'localhost';
 | 
			
		||||
 | 
			
		||||
	Install the needed Perl modules using preferred method (distribution packages, CPAN, etc.)
 | 
			
		||||
		* GetOpt
 | 
			
		||||
		* Pod::Usage
 | 
			
		||||
		* Config::Std
 | 
			
		||||
		* Template
 | 
			
		||||
		* Template::Plugin::File
 | 
			
		||||
		* Template::Plugin::DBI
 | 
			
		||||
		* DBI
 | 
			
		||||
		* Tie::DBI
 | 
			
		||||
		* DBD::SQLite or DBD:mysql
 | 
			
		||||
		* Date::Calc
 | 
			
		||||
        Install the needed Perl modules using preferred method (distribution packages, CPAN, etc.)
 | 
			
		||||
                * GetOpt
 | 
			
		||||
                * Pod::Usage
 | 
			
		||||
                * Config::Std
 | 
			
		||||
                * Template
 | 
			
		||||
                * Template::Plugin::File
 | 
			
		||||
                * Template::Plugin::DBI
 | 
			
		||||
                * DBI
 | 
			
		||||
                * Tie::DBI
 | 
			
		||||
                * DBD::SQLite or DBD:mysql
 | 
			
		||||
                * Date::Calc
 | 
			
		||||
                * Text::CSV_XS
 | 
			
		||||
 | 
			
		||||
=head1 AUTHOR
 | 
			
		||||
 | 
			
		||||
	Roan Horning <roan.horning@no-spam.gmail.com>
 | 
			
		||||
        Roan Horning <roan.horning@no-spam.gmail.com>
 | 
			
		||||
 | 
			
		||||
=head1 LICENSE
 | 
			
		||||
 | 
			
		||||
	site-generator -- a static website generator for HPR
 | 
			
		||||
	Copyright (C) 2022 Roan Horning
 | 
			
		||||
        site-generator -- a static website generator for HPR
 | 
			
		||||
        Copyright (C) 2022 Roan Horning
 | 
			
		||||
 | 
			
		||||
    This program is free software: you can redistribute it and/or modify
 | 
			
		||||
    it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
@@ -99,249 +103,319 @@ This is a site generator for the Hacker Public Radio website based upon the Perl
 | 
			
		||||
 | 
			
		||||
=cut
 | 
			
		||||
 | 
			
		||||
# }}}
 | 
			
		||||
 | 
			
		||||
use strict;
 | 
			
		||||
use warnings;
 | 
			
		||||
 | 
			
		||||
use Getopt::Long qw(:config auto_help);
 | 
			
		||||
use Pod::Usage;
 | 
			
		||||
use Config::Std;
 | 
			
		||||
use Text::CSV_XS;
 | 
			
		||||
use HTML::Entities qw(encode_entities_numeric);
 | 
			
		||||
use Template;
 | 
			
		||||
use Data::Dumper;
 | 
			
		||||
 | 
			
		||||
binmode STDOUT, ":encoding(UTF-8)";
 | 
			
		||||
binmode STDERR, ":encoding(UTF-8)";
 | 
			
		||||
 | 
			
		||||
exit main();
 | 
			
		||||
 | 
			
		||||
sub main {
 | 
			
		||||
 | 
			
		||||
	# Argument parsing
 | 
			
		||||
	my $all;
 | 
			
		||||
	my $configuration_path;
 | 
			
		||||
	my $preview;
 | 
			
		||||
	my $verbose;
 | 
			
		||||
	my $quiet;
 | 
			
		||||
	GetOptions(
 | 
			
		||||
		'all'             => \$all,
 | 
			
		||||
		'configuration=s' => \$configuration_path,
 | 
			
		||||
		'list'            => \&print_available_pages,
 | 
			
		||||
		'preview'         => \$preview,
 | 
			
		||||
		'verbose'         => \$verbose,
 | 
			
		||||
		'quiet'           => \$quiet,
 | 
			
		||||
	) or pod2usage(1);
 | 
			
		||||
	pod2usage(1) unless @ARGV || $all;
 | 
			
		||||
	my (@page_args) = @ARGV;
 | 
			
		||||
    # Argument parsing
 | 
			
		||||
    my $all;
 | 
			
		||||
    my $configuration_path;
 | 
			
		||||
    my $preview;
 | 
			
		||||
    my $verbose;
 | 
			
		||||
    my $quiet;
 | 
			
		||||
    GetOptions(
 | 
			
		||||
        'all'             => \$all,
 | 
			
		||||
        'configuration=s' => \$configuration_path,
 | 
			
		||||
        'list'            => \&print_available_pages,
 | 
			
		||||
        'preview'         => \$preview,
 | 
			
		||||
        'verbose'         => \$verbose,
 | 
			
		||||
        'quiet'           => \$quiet,
 | 
			
		||||
    ) or pod2usage(1);
 | 
			
		||||
    pod2usage(1) unless @ARGV || $all;
 | 
			
		||||
    my (@page_args) = @ARGV;
 | 
			
		||||
 | 
			
		||||
	if ($quiet) {
 | 
			
		||||
		$verbose = 'quiet';
 | 
			
		||||
	};
 | 
			
		||||
    if ($quiet) {
 | 
			
		||||
        $verbose = 'quiet';
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
	if (!$configuration_path) {
 | 
			
		||||
		$configuration_path = "site.cfg";
 | 
			
		||||
	}
 | 
			
		||||
    if (!$configuration_path) {
 | 
			
		||||
        $configuration_path = "site.cfg";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	my %config;
 | 
			
		||||
	if ( -f $configuration_path ) {
 | 
			
		||||
		# Load config file
 | 
			
		||||
		read_config $configuration_path => %config;
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		print STDOUT "Could not read configuration file: $configuration_path\n"; 
 | 
			
		||||
		exit 1;
 | 
			
		||||
	}
 | 
			
		||||
    my %config;
 | 
			
		||||
    if ( -f $configuration_path ) {
 | 
			
		||||
        # Load config file
 | 
			
		||||
        read_config $configuration_path => %config;
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        print STDOUT "Could not read configuration file: $configuration_path\n";
 | 
			
		||||
        exit 1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	my $tt = get_template_html($config{DBI}, $config{app_paths});
 | 
			
		||||
    my $tt = get_template_html($config{DBI}, $config{app_paths});
 | 
			
		||||
 | 
			
		||||
	# If command line option all is set, parse configuration file 
 | 
			
		||||
	# for all pages
 | 
			
		||||
	if ($all) {
 | 
			
		||||
		@page_args = keys %config;
 | 
			
		||||
    #
 | 
			
		||||
    # Define a TT² vmethod called 'csv_parse', it takes a scalar value and
 | 
			
		||||
    # returns an arrayref. Also define a filter called 'xml_entity' which
 | 
			
		||||
    # numerically encodes non-ASCII characters.
 | 
			
		||||
    #
 | 
			
		||||
    $tt->context->define_vmethod( 'scalar', 'csv_parse', \&parse_csv );
 | 
			
		||||
    $tt->context->define_filter( 'xml_entity', \&xml_entity );
 | 
			
		||||
 | 
			
		||||
		# Remove non page sections of the configuration file
 | 
			
		||||
		# from the generated list of pages.
 | 
			
		||||
		@page_args= grep { $_ ne 'DBI' } @page_args;
 | 
			
		||||
		@page_args= grep { $_ ne 'root_template' } @page_args;
 | 
			
		||||
		@page_args= grep { $_ ne 'app_paths' } @page_args;
 | 
			
		||||
    # If command line option all is set, parse configuration file
 | 
			
		||||
    # for all pages
 | 
			
		||||
    if ($all) {
 | 
			
		||||
        @page_args = keys %config;
 | 
			
		||||
 | 
			
		||||
	};
 | 
			
		||||
	foreach my $page_arg (@page_args) {
 | 
			
		||||
		my %parsed_arg = parse_page_arg($page_arg);
 | 
			
		||||
		if (exists($config{$parsed_arg{'page'}})) {
 | 
			
		||||
			my $page_config = $config{$parsed_arg{'page'}}; 
 | 
			
		||||
			$page_config->{'page'} = $parsed_arg{'page'};
 | 
			
		||||
        # Remove non page sections of the configuration file
 | 
			
		||||
        # from the generated list of pages.
 | 
			
		||||
        @page_args= grep { $_ ne 'DBI' } @page_args;
 | 
			
		||||
        @page_args= grep { $_ ne 'root_template' } @page_args;
 | 
			
		||||
        @page_args= grep { $_ ne 'app_paths' } @page_args;
 | 
			
		||||
 | 
			
		||||
			# Set page's root_template to the default root_template if the 
 | 
			
		||||
			# page root_template property is not set in the configuration file.
 | 
			
		||||
			if (exists $page_config->{'root_template'} == 0) {
 | 
			
		||||
				$page_config->{'root_template'} = $config{root_template}{content};
 | 
			
		||||
			}
 | 
			
		||||
    };
 | 
			
		||||
    foreach my $page_arg (@page_args) {
 | 
			
		||||
        my %parsed_arg = parse_page_arg($page_arg);
 | 
			
		||||
        if (exists($config{$parsed_arg{'page'}})) {
 | 
			
		||||
            my $page_config = $config{$parsed_arg{'page'}};
 | 
			
		||||
            $page_config->{'page'} = $parsed_arg{'page'};
 | 
			
		||||
 | 
			
		||||
			# Set all config root_template properties as default page config properties
 | 
			
		||||
			# except the previously set root_template content property
 | 
			
		||||
			my @root_args = grep { $_ ne 'content' } keys %{$config{root_template}};
 | 
			
		||||
			foreach my $root_arg (@root_args) {
 | 
			
		||||
				if (exists $page_config->{$root_arg} == 0) {
 | 
			
		||||
					$page_config->{$root_arg} = $config{root_template}{$root_arg};
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
            # Set page's root_template to the default root_template if the
 | 
			
		||||
            # page root_template property is not set in the configuration file.
 | 
			
		||||
            if (exists $page_config->{'root_template'} == 0) {
 | 
			
		||||
                $page_config->{'root_template'} = $config{root_template}{content};
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
			if ($page_config->{'multipage'} && $page_config->{'multipage'} eq 'true') {
 | 
			
		||||
				# Empty arrayref bug fixed, so count is reduced by 1
 | 
			
		||||
#				if (scalar @{$parsed_arg{'ids'}} == 1) {
 | 
			
		||||
				if (scalar @{$parsed_arg{'ids'}} == 0) {
 | 
			
		||||
				  @{$parsed_arg{'ids'}} = get_ids_from_db($tt, \$page_config);
 | 
			
		||||
				}
 | 
			
		||||
				foreach my $id (@{$parsed_arg{'ids'}}) {
 | 
			
		||||
					$page_config->{'id'} = $id;
 | 
			
		||||
					verbose ($verbose, "Generating page: $page_config->{'page'} with id: $id");
 | 
			
		||||
					generate_page($tt, \$page_config, $preview);
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				verbose ($verbose, "Generating page: $page_config->{'page'}");
 | 
			
		||||
				generate_page($tt, \$page_config, $preview);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			verbose (1, "\nWarning: Page $parsed_arg{'page'} is not defined in the configuration file.");
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
            # Set all config root_template properties as default page config properties
 | 
			
		||||
            # except the previously set root_template content property
 | 
			
		||||
            my @root_args = grep { $_ ne 'content' } keys %{$config{root_template}};
 | 
			
		||||
            foreach my $root_arg (@root_args) {
 | 
			
		||||
                if (exists $page_config->{$root_arg} == 0) {
 | 
			
		||||
                    $page_config->{$root_arg} = $config{root_template}{$root_arg};
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            if ($page_config->{'multipage'} && $page_config->{'multipage'} eq 'true') {
 | 
			
		||||
                # Empty arrayref bug fixed, so count is reduced by 1
 | 
			
		||||
                # was: if (scalar @{$parsed_arg{'ids'}} == 1) {
 | 
			
		||||
                if (scalar @{$parsed_arg{'ids'}} == 0) {
 | 
			
		||||
                    @{$parsed_arg{'ids'}} = get_ids_from_db($tt, \$page_config);
 | 
			
		||||
                }
 | 
			
		||||
                foreach my $id (@{$parsed_arg{'ids'}}) {
 | 
			
		||||
                    $page_config->{'id'} = $id;
 | 
			
		||||
                    verbose ($verbose, "Generating page: $page_config->{'page'} with id: $id");
 | 
			
		||||
                    generate_page($tt, \$page_config, $preview);
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                verbose ($verbose, "Generating page: $page_config->{'page'}");
 | 
			
		||||
                generate_page($tt, \$page_config, $preview);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
            verbose (1, "\nWarning: Page $parsed_arg{'page'} is not defined in the configuration file.");
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    verbose (1, "\nFinished processing the files.");
 | 
			
		||||
	return 0;
 | 
			
		||||
    return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub get_template_html (\%@)  {
 | 
			
		||||
	# For an HTML based Template file, define the 
 | 
			
		||||
	# template start and end tags to also function as 
 | 
			
		||||
	# HTML comments to make the template file valid HTML.
 | 
			
		||||
	#
 | 
			
		||||
	return Template->new({
 | 
			
		||||
			INCLUDE_PATH => $_[1]{templates_path},
 | 
			
		||||
			OUTPUT_PATH	 => $_[1]{output_path},
 | 
			
		||||
			EVAL_PERL    => 1,
 | 
			
		||||
			START_TAG	 => '<!--%',
 | 
			
		||||
			END_TAG		 => '%-->',
 | 
			
		||||
			PRE_CHOMP  => 1,
 | 
			
		||||
			POST_CHOMP => 1,
 | 
			
		||||
			CONSTANTS    => {
 | 
			
		||||
				database => $_[0]{database},
 | 
			
		||||
				driver   => $_[0]{driver},
 | 
			
		||||
				user     => $_[0]{user},
 | 
			
		||||
				password => $_[0]{password},
 | 
			
		||||
			}
 | 
			
		||||
		}) || die $Template::ERROR, "\n";
 | 
			
		||||
    # For an HTML based Template file, define the
 | 
			
		||||
    # template start and end tags to also function as
 | 
			
		||||
    # HTML comments to make the template file valid HTML.
 | 
			
		||||
    #
 | 
			
		||||
    return Template->new(
 | 
			
		||||
        {   INCLUDE_PATH => $_[1]{templates_path},
 | 
			
		||||
            OUTPUT_PATH  => $_[1]{output_path},
 | 
			
		||||
            ENCODING     => 'utf8',
 | 
			
		||||
            EVAL_PERL    => 1,
 | 
			
		||||
            START_TAG    => '<!--%',
 | 
			
		||||
            END_TAG      => '%-->',
 | 
			
		||||
            PRE_CHOMP    => 1,
 | 
			
		||||
            POST_CHOMP   => 1,
 | 
			
		||||
            CONSTANTS    => {
 | 
			
		||||
                database => $_[0]{database},
 | 
			
		||||
                driver   => $_[0]{driver},
 | 
			
		||||
                user     => $_[0]{user},
 | 
			
		||||
                password => $_[0]{password},
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    ) || die $Template::ERROR, "\n";
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub generate_page  {
 | 
			
		||||
	my ($tt, $config, $preview) = @_;
 | 
			
		||||
	my $html;
 | 
			
		||||
	if (!$preview) {
 | 
			
		||||
		$html = get_filename($$config); 
 | 
			
		||||
	}
 | 
			
		||||
	$tt->process($$config->{root_template}, $$config, $html)
 | 
			
		||||
	|| die $tt->error(), "\n";
 | 
			
		||||
sub generate_page {
 | 
			
		||||
    my ( $tt, $config, $preview ) = @_;
 | 
			
		||||
    my $html;
 | 
			
		||||
    if ( !$preview ) {
 | 
			
		||||
        $html = get_filename($$config);
 | 
			
		||||
    }
 | 
			
		||||
    $tt->process( $$config->{root_template},
 | 
			
		||||
        $$config, $html, { binmode => ':utf8' } )
 | 
			
		||||
        || die $tt->error(), "\n";
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub verbose {
 | 
			
		||||
	my ($verbose, $message) = @_;
 | 
			
		||||
	if ($verbose) {
 | 
			
		||||
		if ($verbose ne 'quiet') { 
 | 
			
		||||
			print STDOUT "$message\n";
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		STDOUT->autoflush(1);
 | 
			
		||||
		print STDOUT ".";
 | 
			
		||||
	};
 | 
			
		||||
        my ($verbose, $message) = @_;
 | 
			
		||||
        if ($verbose) {
 | 
			
		||||
                if ($verbose ne 'quiet') {
 | 
			
		||||
                        print STDOUT "$message\n";
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
                STDOUT->autoflush(1);
 | 
			
		||||
                print STDOUT ".";
 | 
			
		||||
        };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub parse_page_arg {
 | 
			
		||||
	my ($page_arg) =  @_;
 | 
			
		||||
	# Split page name from page ids if available.
 | 
			
		||||
	my ($page, $ids) = split(/=/, $page_arg);
 | 
			
		||||
	#my @ids = [];
 | 
			
		||||
	my @ids;
 | 
			
		||||
    my ($page_arg) =  @_;
 | 
			
		||||
    # Split page name from page ids if available.
 | 
			
		||||
    my ($page, $ids) = split(/=/, $page_arg);
 | 
			
		||||
    my @ids;
 | 
			
		||||
 | 
			
		||||
	if(!$ids) {
 | 
			
		||||
		$ids = "";
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		# Parse the page ids and push them onto @ids array 
 | 
			
		||||
		my @ids_by_comma = split(/\,/, $ids);
 | 
			
		||||
		foreach my $id_by_comma (@ids_by_comma) {
 | 
			
		||||
			my @ids_for_range = split(/\.\./, $id_by_comma);
 | 
			
		||||
			if ((scalar @ids_for_range) == 2) {
 | 
			
		||||
				push @ids, $ids_for_range[0]..$ids_for_range[1]; 
 | 
			
		||||
			}
 | 
			
		||||
			elsif ((scalar @ids_for_range) == 1) {
 | 
			
		||||
				push @ids, $ids_for_range[0];
 | 
			
		||||
			}
 | 
			
		||||
			else {
 | 
			
		||||
				verbose (1, "\nWarning: Page $page id range $id_by_comma could not be parsed.");
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return ('page' => $page, 'ids' => [@ids]);
 | 
			
		||||
    if(!$ids) {
 | 
			
		||||
        $ids = "";
 | 
			
		||||
    }
 | 
			
		||||
    else {
 | 
			
		||||
        # Parse the page ids and push them onto @ids array
 | 
			
		||||
        my @ids_by_comma = split(/\,/, $ids);
 | 
			
		||||
        foreach my $id_by_comma (@ids_by_comma) {
 | 
			
		||||
            my @ids_for_range = split(/\.\./, $id_by_comma);
 | 
			
		||||
            if ((scalar @ids_for_range) == 2) {
 | 
			
		||||
                push @ids, $ids_for_range[0]..$ids_for_range[1];
 | 
			
		||||
            }
 | 
			
		||||
            elsif ((scalar @ids_for_range) == 1) {
 | 
			
		||||
                push @ids, $ids_for_range[0];
 | 
			
		||||
            }
 | 
			
		||||
            else {
 | 
			
		||||
                verbose (1, "\nWarning: Page $page id range $id_by_comma could not be parsed.");
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return ('page' => $page, 'ids' => [@ids]);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub get_ids_from_db {
 | 
			
		||||
	# Use a template to generate a string of page identifiers.
 | 
			
		||||
	# The template should return the string in the form of 
 | 
			
		||||
	# <comma><identifier><comma><identifier>...
 | 
			
		||||
	#
 | 
			
		||||
	my ($tt, $config)  = @_;
 | 
			
		||||
	my $selected_ids = "";
 | 
			
		||||
	my $id_template = "ids-$$config->{'page'}.tpl.html";
 | 
			
		||||
        # Use a template to generate a string of page identifiers.
 | 
			
		||||
        # The template should return the string in the form of
 | 
			
		||||
        # <comma><identifier><comma><identifier>...
 | 
			
		||||
        #
 | 
			
		||||
        my ($tt, $config)  = @_;
 | 
			
		||||
        my $selected_ids = "";
 | 
			
		||||
        my $id_template = "ids-$$config->{'page'}.tpl.html";
 | 
			
		||||
 | 
			
		||||
	$tt->process($id_template, $$config, \$selected_ids)
 | 
			
		||||
	|| die $tt->error(), "\n";
 | 
			
		||||
        $tt->process($id_template, $$config, \$selected_ids)
 | 
			
		||||
        || die $tt->error(), "\n";
 | 
			
		||||
 | 
			
		||||
	# Starts with a newline and comma
 | 
			
		||||
	return split(/,/, substr($selected_ids, 2));
 | 
			
		||||
        # Starts with a newline and comma
 | 
			
		||||
        return split(/,/, substr($selected_ids, 2));
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub get_filename {
 | 
			
		||||
	my ($config) = @_;
 | 
			
		||||
	my $filename = "output.html";
 | 
			
		||||
	my $base_path = "";
 | 
			
		||||
        my ($config) = @_;
 | 
			
		||||
        my $filename = "output.html";
 | 
			
		||||
        my $base_path = "";
 | 
			
		||||
 | 
			
		||||
	if ($$config{'filename'}) {
 | 
			
		||||
		if (substr($$config{'filename'}, -1) eq '/') {
 | 
			
		||||
			$base_path = $$config{'filename'};
 | 
			
		||||
		}
 | 
			
		||||
		else {
 | 
			
		||||
			$filename = $$config{'filename'};
 | 
			
		||||
			my $padded_index = "";
 | 
			
		||||
			if (exists $$config{'id'} && $$config{'id'} ne "") {
 | 
			
		||||
				$padded_index = sprintf("%04d", $$config{'id'});
 | 
			
		||||
			}
 | 
			
		||||
			$filename =~ s/\[id\]/$padded_index/;
 | 
			
		||||
		    return $filename;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	# Default naming if full filename configuration is not supplied.
 | 
			
		||||
	if ($$config{'multipage'} && $$config{'multipage'} eq 'true') {
 | 
			
		||||
		my $padded_index = sprintf("%04d", $$config{'id'});
 | 
			
		||||
		$filename = "$base_path$$config{'page'}${padded_index}.html";
 | 
			
		||||
	}
 | 
			
		||||
	else {
 | 
			
		||||
		$filename = "$base_path$$config{'page'}.html";
 | 
			
		||||
	}
 | 
			
		||||
	return $filename;
 | 
			
		||||
        if ($$config{'filename'}) {
 | 
			
		||||
                if (substr($$config{'filename'}, -1) eq '/') {
 | 
			
		||||
                        $base_path = $$config{'filename'};
 | 
			
		||||
                }
 | 
			
		||||
                else {
 | 
			
		||||
                        $filename = $$config{'filename'};
 | 
			
		||||
                        my $padded_index = "";
 | 
			
		||||
                        if (exists $$config{'id'} && $$config{'id'} ne "") {
 | 
			
		||||
                                $padded_index = sprintf("%04d", $$config{'id'});
 | 
			
		||||
                        }
 | 
			
		||||
                        $filename =~ s/\[id\]/$padded_index/;
 | 
			
		||||
                    return $filename;
 | 
			
		||||
                }
 | 
			
		||||
        }
 | 
			
		||||
        # Default naming if full filename configuration is not supplied.
 | 
			
		||||
        if ($$config{'multipage'} && $$config{'multipage'} eq 'true') {
 | 
			
		||||
                my $padded_index = sprintf("%04d", $$config{'id'});
 | 
			
		||||
                $filename = "$base_path$$config{'page'}${padded_index}.html";
 | 
			
		||||
        }
 | 
			
		||||
        else {
 | 
			
		||||
                $filename = "$base_path$$config{'page'}.html";
 | 
			
		||||
        }
 | 
			
		||||
        return $filename;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
sub print_available_pages {
 | 
			
		||||
	# Load config file
 | 
			
		||||
	read_config "site.cfg" => my %config;
 | 
			
		||||
    # Load config file
 | 
			
		||||
    read_config "site.cfg" => my %config;
 | 
			
		||||
 | 
			
		||||
	my @page_args = sort (keys %config);
 | 
			
		||||
    my @page_args = sort ( keys %config );
 | 
			
		||||
 | 
			
		||||
	# Remove non page sections of the configuration file
 | 
			
		||||
	# from the generated list of pages.
 | 
			
		||||
	@page_args= grep { $_ ne 'DBI' } @page_args;
 | 
			
		||||
	@page_args= grep { $_ ne 'root_template' } @page_args;
 | 
			
		||||
    # Remove non page sections of the configuration file
 | 
			
		||||
    # from the generated list of pages.
 | 
			
		||||
    @page_args = grep { $_ ne 'DBI' } @page_args;
 | 
			
		||||
    @page_args = grep { $_ ne 'root_template' } @page_args;
 | 
			
		||||
 | 
			
		||||
	foreach my $page_arg (@page_args) {
 | 
			
		||||
		print "$page_arg\n";
 | 
			
		||||
	}
 | 
			
		||||
	exit;
 | 
			
		||||
    foreach my $page_arg (@page_args) {
 | 
			
		||||
        print "$page_arg\n";
 | 
			
		||||
    }
 | 
			
		||||
    exit;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#===  FUNCTION  ================================================================
 | 
			
		||||
#         NAME: parse_csv
 | 
			
		||||
#      PURPOSE: Parses a simple string containing CSV data
 | 
			
		||||
#   PARAMETERS: $csv_in         CSV string
 | 
			
		||||
#      RETURNS: An arrayref containing the parsed CSV elements
 | 
			
		||||
#  DESCRIPTION: The Text::CSV_XS module instance is created with the option
 | 
			
		||||
#               'allow_whitespace' to be forgiving of any spaces around the
 | 
			
		||||
#               CSV elements and to strip them. Also, 'allow_loose_quotes' is
 | 
			
		||||
#               forgiving of really messed up CSV.
 | 
			
		||||
#       THROWS: No exceptions
 | 
			
		||||
#     COMMENTS: None
 | 
			
		||||
#     SEE ALSO: N/A
 | 
			
		||||
#===============================================================================
 | 
			
		||||
sub parse_csv {
 | 
			
		||||
    my ($csv_in) = @_;
 | 
			
		||||
 | 
			
		||||
    my $csv = Text::CSV_XS->new(
 | 
			
		||||
        {   binary             => 1,
 | 
			
		||||
            auto_diag          => 1,
 | 
			
		||||
            allow_whitespace   => 1,
 | 
			
		||||
            allow_loose_quotes => 1
 | 
			
		||||
        }
 | 
			
		||||
    );
 | 
			
		||||
    my $status = $csv->parse($csv_in);
 | 
			
		||||
    unless ( $status ) {
 | 
			
		||||
        warn "Failed to parse '$csv_in'\n" ;
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    my @fields = $csv->fields();
 | 
			
		||||
 | 
			
		||||
    return \@fields;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#===  FUNCTION  ================================================================
 | 
			
		||||
#         NAME: xml_entity
 | 
			
		||||
#      PURPOSE: Static filter to encode Unicode for XML
 | 
			
		||||
#   PARAMETERS: $text           String to be processed
 | 
			
		||||
#      RETURNS: Processed text
 | 
			
		||||
#  DESCRIPTION:
 | 
			
		||||
#       THROWS: No exceptions
 | 
			
		||||
#     COMMENTS: None
 | 
			
		||||
#     SEE ALSO: N/A
 | 
			
		||||
#===============================================================================
 | 
			
		||||
sub xml_entity {
 | 
			
		||||
    my ($text) = @_;
 | 
			
		||||
 | 
			
		||||
    encode_entities_numeric( $text );
 | 
			
		||||
 | 
			
		||||
    return $text;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
# vim: syntax=perl:ts=8:sw=4:et:ai:tw=78:fo=tcrqn21:fdm=marker
 | 
			
		||||
 
 | 
			
		||||
@@ -2,14 +2,14 @@
 | 
			
		||||
        eps.id,
 | 
			
		||||
        eps.explicit,
 | 
			
		||||
        eps.date, eps.license, eps.title, eps.summary,
 | 
			
		||||
		eps.duration, eps.notes, eps.tags,
 | 
			
		||||
        eps.duration, eps.notes, eps.tags,
 | 
			
		||||
        hosts.hostid,
 | 
			
		||||
        hosts.host, hosts.email, hosts.local_image,
 | 
			
		||||
		miniseries.name AS series, miniseries.id AS seriesid
 | 
			
		||||
        miniseries.name AS series, miniseries.id AS seriesid
 | 
			
		||||
    FROM eps
 | 
			
		||||
	INNER JOIN hosts ON eps.hostid = hosts.hostid 
 | 
			
		||||
	INNER JOIN miniseries ON eps.series = miniseries.id
 | 
			
		||||
	WHERE eps.date <= date(\'now\')
 | 
			
		||||
	ORDER BY eps.id + 0 DESC'
 | 
			
		||||
        INNER JOIN hosts ON eps.hostid = hosts.hostid
 | 
			
		||||
        INNER JOIN miniseries ON eps.series = miniseries.id
 | 
			
		||||
        WHERE eps.date <= date(\'now\')
 | 
			
		||||
        ORDER BY eps.id + 0 DESC'
 | 
			
		||||
%-->
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,26 @@
 | 
			
		||||
<!--% USE DBI(constants.driver, constants.user, constants.password) %-->
 | 
			
		||||
<!--% query_hpr_feed = DBI.prepare('
 | 
			
		||||
	SELECT 
 | 
			
		||||
	eps.id, 
 | 
			
		||||
	eps.explicit, 
 | 
			
		||||
	DATE_FORMAT(eps.date, \'%H:%i:%S %d:%m:%Y\') AS \'date\',	
 | 
			
		||||
	eps.license, eps.duration,
 | 
			
		||||
	eps.title, eps.summary, eps.tags, 
 | 
			
		||||
	eps.notes,
 | 
			
		||||
	hosts.local_image,
 | 
			
		||||
	hosts.hostid, 
 | 
			
		||||
	hosts.host, hosts.email,
 | 
			
		||||
	miniseries.name AS series, miniseries.id AS seriesid
 | 
			
		||||
	FROM eps 
 | 
			
		||||
	INNER JOIN hosts ON eps.hostid = hosts.hostid
 | 
			
		||||
	INNER JOIN miniseries ON eps.series = miniseries.id
 | 
			
		||||
	WHERE eps.date < DATE_ADD(NOW(), INTERVAL 1 DAY)
 | 
			
		||||
	ORDER BY eps.date DESC 
 | 
			
		||||
	LIMIT 10
 | 
			
		||||
        SELECT
 | 
			
		||||
            eps.id,
 | 
			
		||||
            eps.explicit,
 | 
			
		||||
            DATE_FORMAT(eps.date, \'%H:%i:%S %d:%m:%Y\') AS \'date\',
 | 
			
		||||
            eps.license, eps.duration,
 | 
			
		||||
            eps.title, eps.summary, eps.tags,
 | 
			
		||||
            eps.notes,
 | 
			
		||||
            hosts.local_image,
 | 
			
		||||
            hosts.hostid,
 | 
			
		||||
            hosts.host, hosts.email,
 | 
			
		||||
            miniseries.name AS series, miniseries.id AS seriesid,
 | 
			
		||||
            assets.size AS length
 | 
			
		||||
        FROM eps
 | 
			
		||||
        INNER JOIN hosts ON eps.hostid = hosts.hostid
 | 
			
		||||
        INNER JOIN miniseries ON eps.series = miniseries.id
 | 
			
		||||
        INNER JOIN assets ON eps.id = assets.episode_id
 | 
			
		||||
        WHERE eps.date <= UTC_DATE()
 | 
			
		||||
        AND assets.extension = ?
 | 
			
		||||
        ORDER BY eps.date DESC
 | 
			
		||||
        LIMIT 10
 | 
			
		||||
')
 | 
			
		||||
%-->
 | 
			
		||||
<!--% feed_result = query_hpr_feed.execute() %-->
 | 
			
		||||
<!--% feed_result = query_hpr_feed.execute(media_file_extension) %-->
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,26 @@
 | 
			
		||||
<!--% USE DBI(constants.driver, constants.user, constants.password) %-->
 | 
			
		||||
<!--% query_hpr_feed = DBI.prepare('
 | 
			
		||||
	SELECT 
 | 
			
		||||
	eps.id, 
 | 
			
		||||
	eps.explicit, 
 | 
			
		||||
	strftime(\'%H:%M:%S %d:%m:%Y\', date(eps.date)) AS date,
 | 
			
		||||
	eps.license, eps.duration,
 | 
			
		||||
	eps.title, eps.summary, eps.tags, 
 | 
			
		||||
	eps.notes,
 | 
			
		||||
	hosts.local_image,
 | 
			
		||||
	hosts.hostid, 
 | 
			
		||||
	hosts.host, hosts.email,
 | 
			
		||||
	miniseries.name AS series, miniseries.id AS seriesid
 | 
			
		||||
	FROM eps 
 | 
			
		||||
	INNER JOIN hosts ON eps.hostid = hosts.hostid
 | 
			
		||||
	INNER JOIN miniseries ON eps.series = miniseries.id
 | 
			
		||||
	WHERE eps.date < date(\'now\', \'+1 days\')
 | 
			
		||||
	ORDER BY eps.date DESC 
 | 
			
		||||
	LIMIT 10
 | 
			
		||||
        SELECT
 | 
			
		||||
            eps.id,
 | 
			
		||||
            eps.explicit,
 | 
			
		||||
            strftime(\'%H:%M:%S %d:%m:%Y\', date(eps.date)) AS date,
 | 
			
		||||
            eps.license, eps.duration,
 | 
			
		||||
            eps.title, eps.summary, eps.tags,
 | 
			
		||||
            eps.notes,
 | 
			
		||||
            hosts.local_image,
 | 
			
		||||
            hosts.hostid,
 | 
			
		||||
            hosts.host, hosts.email,
 | 
			
		||||
            miniseries.name AS series, miniseries.id AS seriesid,
 | 
			
		||||
            assets.size AS length
 | 
			
		||||
        FROM eps
 | 
			
		||||
        INNER JOIN hosts ON eps.hostid = hosts.hostid
 | 
			
		||||
        INNER JOIN miniseries ON eps.series = miniseries.id
 | 
			
		||||
        INNER JOIN assets ON eps.id = assets.episode_id
 | 
			
		||||
        WHERE eps.date <= date(\'now\')
 | 
			
		||||
        AND assets.extension = ?
 | 
			
		||||
        ORDER BY eps.date DESC
 | 
			
		||||
        LIMIT 10
 | 
			
		||||
')
 | 
			
		||||
%-->
 | 
			
		||||
<!--% feed_result = query_hpr_feed.execute() %-->
 | 
			
		||||
<!--% feed_result = query_hpr_feed.execute(media_file_extension) %-->
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,25 @@
 | 
			
		||||
<!--% USE DBI(constants.driver, constants.user, constants.password) %-->
 | 
			
		||||
<!--% query_hpr_feed = DBI.prepare('
 | 
			
		||||
	SELECT 
 | 
			
		||||
	eps.id, 
 | 
			
		||||
	eps.explicit, 
 | 
			
		||||
	DATE_FORMAT(eps.date, \'%H:%i:%S %d:%m:%Y\') AS \'date\',
 | 
			
		||||
	eps.license, eps.duration,
 | 
			
		||||
	eps.title, eps.summary, eps.tags, 
 | 
			
		||||
	eps.notes,
 | 
			
		||||
	hosts.local_image,
 | 
			
		||||
	hosts.hostid, 
 | 
			
		||||
	hosts.host, hosts.email,
 | 
			
		||||
	miniseries.name AS series, miniseries.id AS seriesid
 | 
			
		||||
	FROM eps 
 | 
			
		||||
	INNER JOIN hosts ON eps.hostid = hosts.hostid
 | 
			
		||||
	INNER JOIN miniseries ON eps.series = miniseries.id
 | 
			
		||||
	WHERE eps.date < DATE_ADD(NOW(), INTERVAL 1 DAY)
 | 
			
		||||
	ORDER BY eps.date DESC 
 | 
			
		||||
        SELECT
 | 
			
		||||
            eps.id,
 | 
			
		||||
            eps.explicit,
 | 
			
		||||
            DATE_FORMAT(eps.date, \'%H:%i:%S %d:%m:%Y\') AS \'date\',
 | 
			
		||||
            eps.license, eps.duration,
 | 
			
		||||
            eps.title, eps.summary, eps.tags,
 | 
			
		||||
            eps.notes,
 | 
			
		||||
            hosts.local_image,
 | 
			
		||||
            hosts.hostid,
 | 
			
		||||
            hosts.host, hosts.email,
 | 
			
		||||
            miniseries.name AS series, miniseries.id AS seriesid,
 | 
			
		||||
            assets.size AS length
 | 
			
		||||
        FROM eps
 | 
			
		||||
        INNER JOIN hosts ON eps.hostid = hosts.hostid
 | 
			
		||||
        INNER JOIN miniseries ON eps.series = miniseries.id
 | 
			
		||||
        INNER JOIN assets ON eps.id = assets.episode_id
 | 
			
		||||
        WHERE eps.date < UTC_DATE()
 | 
			
		||||
        AND assets.extension = ?
 | 
			
		||||
        ORDER BY eps.date DESC
 | 
			
		||||
')
 | 
			
		||||
%-->
 | 
			
		||||
<!--% feed_result = query_hpr_feed.execute() %-->
 | 
			
		||||
<!--% feed_result = query_hpr_feed.execute(media_file_extension) %-->
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,22 +1,25 @@
 | 
			
		||||
<!--% USE DBI(constants.driver, constants.user, constants.password) %-->
 | 
			
		||||
<!--% query_hpr_feed = DBI.prepare('
 | 
			
		||||
	SELECT 
 | 
			
		||||
	eps.id, 
 | 
			
		||||
	eps.explicit, 
 | 
			
		||||
	strftime(\'%H:%M:%S %d:%m:%Y\', date(eps.date)) AS date,
 | 
			
		||||
	eps.license, eps.duration,
 | 
			
		||||
	eps.title, eps.summary, eps.tags, 
 | 
			
		||||
	eps.notes,
 | 
			
		||||
	hosts.local_image,
 | 
			
		||||
	hosts.hostid, 
 | 
			
		||||
	hosts.host, hosts.email,
 | 
			
		||||
	miniseries.name AS series, miniseries.id AS seriesid
 | 
			
		||||
	FROM eps 
 | 
			
		||||
	INNER JOIN hosts ON eps.hostid = hosts.hostid
 | 
			
		||||
	INNER JOIN miniseries ON eps.series = miniseries.id
 | 
			
		||||
	WHERE eps.date < date(\'now\', \'+1 days\')
 | 
			
		||||
	ORDER BY eps.date DESC 
 | 
			
		||||
        SELECT
 | 
			
		||||
            eps.id,
 | 
			
		||||
            eps.explicit,
 | 
			
		||||
            strftime(\'%H:%M:%S %d:%m:%Y\', date(eps.date)) AS date,
 | 
			
		||||
            eps.license, eps.duration,
 | 
			
		||||
            eps.title, eps.summary, eps.tags,
 | 
			
		||||
            eps.notes,
 | 
			
		||||
            hosts.local_image,
 | 
			
		||||
            hosts.hostid,
 | 
			
		||||
            hosts.host, hosts.email,
 | 
			
		||||
            miniseries.name AS series, miniseries.id AS seriesid,
 | 
			
		||||
            assets.size AS length
 | 
			
		||||
        FROM eps
 | 
			
		||||
        INNER JOIN hosts ON eps.hostid = hosts.hostid
 | 
			
		||||
        INNER JOIN miniseries ON eps.series = miniseries.id
 | 
			
		||||
        INNER JOIN assets ON eps.id = assets.episode_id
 | 
			
		||||
        WHERE eps.date <= date(\'now\')
 | 
			
		||||
        AND assets.extension = ?
 | 
			
		||||
        ORDER BY eps.date DESC
 | 
			
		||||
')
 | 
			
		||||
%-->
 | 
			
		||||
<!--% feed_result = query_hpr_feed.execute() %-->
 | 
			
		||||
<!--% feed_result = query_hpr_feed.execute(media_file_extension) %-->
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,7 +39,7 @@
 | 
			
		||||
  <image>
 | 
			
		||||
    <url>https://www.hackerpublicradio.org/images/hpr_feed_small.png</url>
 | 
			
		||||
    <title>Hacker Public Radio</title>
 | 
			
		||||
    <link>https://www.hackerpublicradio.org/about.php</link>
 | 
			
		||||
    <link>https://www.hackerpublicradio.org/about.html</link>
 | 
			
		||||
    <description>The Hacker Public Radio Old Microphone Logo</description>
 | 
			
		||||
    <height>164</height>
 | 
			
		||||
    <width>144</width>
 | 
			
		||||
 
 | 
			
		||||
@@ -12,7 +12,11 @@ from the series <em><a href="<!--% baseurl %-->series/<!--% zero_pad_left(series
 | 
			
		||||
<!--% END %-->
 | 
			
		||||
 | 
			
		||||
<!--% MACRO display_tags(tags) BLOCK %-->
 | 
			
		||||
	<span><label>Tags:</label> <em><!--% tags %--></em>.</span>
 | 
			
		||||
    <span><label>Tags:</label> <em>
 | 
			
		||||
        <!--% FOREACH tag IN tags.csv_parse %-->
 | 
			
		||||
        <a href="<!--% absolute_path(baseurl) %-->tags.html#<!--% tag.lower %-->"><!--% tag %--></a><!--% IF loop.count == loop.size %-->.<!--% ELSE %-->,<!--% END %-->
 | 
			
		||||
        <!--% END %--></em>
 | 
			
		||||
    </span>
 | 
			
		||||
<!--% END %-->
 | 
			
		||||
 | 
			
		||||
<!--% MACRO display_listen_in(eps_id, episode_type) BLOCK %-->
 | 
			
		||||
 
 | 
			
		||||
@@ -1,23 +1,24 @@
 | 
			
		||||
<!--% PROCESS 'shared-utils.tpl.html' %-->
 | 
			
		||||
<!--% MACRO display_item(episode, file_extension, audio_mime_type) BLOCK %-->
 | 
			
		||||
<!--% USE HTML.Strip %-->
 | 
			
		||||
<!--% IF audio_mime_type == ""  %-->
 | 
			
		||||
<!--% audio_mime_type = 'ogg' %-->
 | 
			
		||||
<!--% END %-->
 | 
			
		||||
<item>
 | 
			
		||||
	  <itunes:explicit><!--% display_explicit_feed(episode.explicit) %--></itunes:explicit>
 | 
			
		||||
    <itunes:explicit><!--% display_explicit_feed(episode.explicit) %--></itunes:explicit>
 | 
			
		||||
    <googleplay:explicit><!--% display_explicit_feed(episode.explicit) %--></googleplay:explicit>
 | 
			
		||||
	<title>HPR<!--% zero_pad_left(episode.id) %-->: <!--% episode.title %--></title>
 | 
			
		||||
    <title>HPR<!--% zero_pad_left(episode.id) %-->: <!--% episode.title %--></title>
 | 
			
		||||
    <author><!--% episode.email %--> (<!--% episode.host %-->)</author>
 | 
			
		||||
    <googleplay:author><!--% episode.email %--> (<!--% episode.host %-->)</googleplay:author>
 | 
			
		||||
    <itunes:author><!--% episode.email %--> (<!--% episode.host %-->)</itunes:author>
 | 
			
		||||
    <googleplay:image href="https://www.hackerpublicradio.org/images/hpr_feed_itunes.png"/>
 | 
			
		||||
	<link>https://www.hackerpublicradio.org/eps/hpr/<!--% zero_pad_left(episode.id) %-->/index.html</link>
 | 
			
		||||
    <link>https://www.hackerpublicradio.org/eps/hpr<!--% zero_pad_left(episode.id) %-->/index.html</link>
 | 
			
		||||
    <description><![CDATA[<!--% episode.notes %-->]]>
 | 
			
		||||
</description>
 | 
			
		||||
    <itunes:summary><![CDATA[<!--% episode.notes %-->]]>
 | 
			
		||||
    <itunes:summary><![CDATA[<!--% episode.notes.substr(0, 4000) | html_strip | xml_entity %-->]]>
 | 
			
		||||
</itunes:summary>
 | 
			
		||||
    <pubDate><!--% format_feed_date(episode.date) %--></pubDate>
 | 
			
		||||
    <enclosure url="http://hackerpublicradio.org/eps/hpr<!--% zero_pad_left(episode.id) %-->.<!--% file_extension %-->" length="<!--% episode.duration * 1000 %-->" type="audio/<!--% audio_mime_type %-->"/>
 | 
			
		||||
	<guid>http://hackerpublicradio.org/eps/hpr<!--% zero_pad_left(episode.id) %-->.<!--% file_extension %--></guid>
 | 
			
		||||
    <enclosure url="http://hackerpublicradio.org/eps/hpr<!--% zero_pad_left(episode.id) %-->.<!--% file_extension %-->" length="<!--% episode.length %-->" type="audio/<!--% audio_mime_type %-->"/>
 | 
			
		||||
    <guid>http://hackerpublicradio.org/eps/hpr<!--% zero_pad_left(episode.id) %-->.<!--% file_extension %--></guid>
 | 
			
		||||
  </item>
 | 
			
		||||
<!--% END %-->
 | 
			
		||||
 
 | 
			
		||||
@@ -1,11 +1,11 @@
 | 
			
		||||
<!--% MACRO zero_pad_left(word, pad_length) BLOCK %-->
 | 
			
		||||
	<!--% IF pad_length %-->
 | 
			
		||||
		<!--% zero_pad_format = "%0${pad_length}s" %-->
 | 
			
		||||
	<!--% ELSE %-->
 | 
			
		||||
		<!--% zero_pad_format = "%04s" %-->
 | 
			
		||||
	<!--% END %-->
 | 
			
		||||
	<!--% USE String(word) %-->
 | 
			
		||||
	<!--% String.format(zero_pad_format) %-->
 | 
			
		||||
        <!--% IF pad_length %-->
 | 
			
		||||
                <!--% zero_pad_format = "%0${pad_length}s" %-->
 | 
			
		||||
        <!--% ELSE %-->
 | 
			
		||||
                <!--% zero_pad_format = "%04s" %-->
 | 
			
		||||
        <!--% END %-->
 | 
			
		||||
        <!--% USE String(word) %-->
 | 
			
		||||
        <!--% String.format(zero_pad_format) %-->
 | 
			
		||||
<!--% END %-->
 | 
			
		||||
 | 
			
		||||
<!--% MACRO display_choice(choice, display_when_true, display_when_false) BLOCK %-->
 | 
			
		||||
@@ -17,7 +17,7 @@
 | 
			
		||||
<!--% seconds = duration_sec % 60 %-->
 | 
			
		||||
<!--% USE format %-->
 | 
			
		||||
<!--% minutes_only = format("%d") %-->
 | 
			
		||||
<!--% minutes =	minutes_only(duration_sec / 60) %-->
 | 
			
		||||
<!--% minutes = minutes_only(duration_sec / 60) %-->
 | 
			
		||||
<!--% hours_only = format("%d") %-->
 | 
			
		||||
<!--% hours = hours_only(minutes / 60) %-->
 | 
			
		||||
<!--% IF hours >= 1 %-->
 | 
			
		||||
@@ -57,23 +57,23 @@
 | 
			
		||||
<!--% END %-->
 | 
			
		||||
 | 
			
		||||
<!--% MACRO media_path(episode_id, episode_type, media_type, baseurl, media_baseurl) BLOCK %-->
 | 
			
		||||
	<!--% IF episode_type == "twat" %-->
 | 
			
		||||
		<!--% padding = 3 %-->
 | 
			
		||||
		<!--% media_folder = "eps/"; padding = 3 %-->
 | 
			
		||||
	<!--% ELSE %-->
 | 
			
		||||
		<!--% media_folder = "local/" %-->
 | 
			
		||||
	<!--% END %-->
 | 
			
		||||
	<!--% IF media_baseurl %-->
 | 
			
		||||
		<!--% transcription_types = "txt srt vtt" %-->
 | 
			
		||||
		<!--% USE String(transcription_types) %-->
 | 
			
		||||
		<!--% USE String(media_baseurl) %-->
 | 
			
		||||
			<!--% IF transcription_types.search(media_type) && media_baseurl.search('archive.org') %-->
 | 
			
		||||
			<!--% media_baseurl = "${media_baseurl}hpr\$eps_id/" %-->
 | 
			
		||||
		<!--% END %-->
 | 
			
		||||
		<!--% media_folder = "" %-->
 | 
			
		||||
		<!--% media_baseurl = media_baseurl.replace('\$eps_id', zero_pad_left(episode_id)) %-->
 | 
			
		||||
	<!--% END %-->
 | 
			
		||||
	<!--% media_basepath(baseurl, media_baseurl) %--><!--% media_folder %--><!--% episode_type %--><!--% zero_pad_left(episode_id, padding) %-->.<!--% media_type %-->
 | 
			
		||||
        <!--% IF episode_type == "twat" %-->
 | 
			
		||||
                <!--% padding = 3 %-->
 | 
			
		||||
                <!--% media_folder = "eps/"; padding = 3 %-->
 | 
			
		||||
        <!--% ELSE %-->
 | 
			
		||||
                <!--% media_folder = "local/" %-->
 | 
			
		||||
        <!--% END %-->
 | 
			
		||||
        <!--% IF media_baseurl %-->
 | 
			
		||||
                <!--% transcription_types = "txt srt vtt" %-->
 | 
			
		||||
                <!--% USE String(transcription_types) %-->
 | 
			
		||||
                <!--% USE String(media_baseurl) %-->
 | 
			
		||||
                        <!--% IF transcription_types.search(media_type) && media_baseurl.search('archive.org') %-->
 | 
			
		||||
                        <!--% media_baseurl = "${media_baseurl}hpr\$eps_id/" %-->
 | 
			
		||||
                <!--% END %-->
 | 
			
		||||
                <!--% media_folder = "" %-->
 | 
			
		||||
                <!--% media_baseurl = media_baseurl.replace('\$eps_id', zero_pad_left(episode_id)) %-->
 | 
			
		||||
        <!--% END %-->
 | 
			
		||||
        <!--% media_basepath(baseurl, media_baseurl) %--><!--% media_folder %--><!--% episode_type %--><!--% zero_pad_left(episode_id, padding) %-->.<!--% media_type %-->
 | 
			
		||||
<!--% END %-->
 | 
			
		||||
 | 
			
		||||
<!--% MACRO step_navigation(baseurl, links, folder) BLOCK %-->
 | 
			
		||||
@@ -93,7 +93,3 @@
 | 
			
		||||
    <!--% END %-->
 | 
			
		||||
    <a href="<!--% absolute_path(baseurl) %-->eps/<!--% folder %--><!--% zero_pad_left(links.latest) %-->/index.html" rel="last">Latest >></a></small>
 | 
			
		||||
<!--% END %-->
 | 
			
		||||
 | 
			
		||||
<!--
 | 
			
		||||
    vim: syntax=html:ts=8:sw=4:tw=78:et:ai:
 | 
			
		||||
-->
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user