833 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			833 lines
		
	
	
		
			25 KiB
		
	
	
	
		
			Perl
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env perl
 | 
						|
#===============================================================================
 | 
						|
#
 | 
						|
#         FILE: edit_episode
 | 
						|
#
 | 
						|
#        USAGE: ./edit_episode [-h] [-debug=N] [-config=FILE] [-[no]update]
 | 
						|
#                       [-[no]title] [-[no]summary] [-[no]tags] [-[no]notes]
 | 
						|
#                       [-[no]ctitle] [-[no]ctext] [-cnumber=N] shownumber
 | 
						|
#
 | 
						|
#  DESCRIPTION: A simple command-line editor for the HPR database
 | 
						|
#
 | 
						|
#      OPTIONS: ---
 | 
						|
# REQUIREMENTS: ---
 | 
						|
#         BUGS: ---
 | 
						|
#        NOTES: Had to revert to MySQL due to a problem with DBD::MariaDB
 | 
						|
#       AUTHOR: Dave Morriss (djm), Dave.Morriss@gmail.com
 | 
						|
#      VERSION: 0.1.3
 | 
						|
#      CREATED: 2015-06-17 23:17:50
 | 
						|
#     REVISION: 2022-02-16 20:07:45
 | 
						|
#
 | 
						|
#===============================================================================
 | 
						|
 | 
						|
use 5.010;
 | 
						|
use strict;
 | 
						|
use warnings;
 | 
						|
use utf8;
 | 
						|
 | 
						|
use Getopt::Long;
 | 
						|
use Pod::Usage;
 | 
						|
 | 
						|
use Config::General;
 | 
						|
use File::Temp;
 | 
						|
use File::Slurper qw{ read_text };
 | 
						|
use SQL::Abstract;
 | 
						|
use DBI;
 | 
						|
 | 
						|
use Data::Dumper;
 | 
						|
 | 
						|
#
 | 
						|
# Version number (manually incremented)
 | 
						|
#
 | 
						|
our $VERSION = '0.1.3';
 | 
						|
 | 
						|
#
 | 
						|
# Script and directory names
 | 
						|
#
 | 
						|
( my $PROG = $0 ) =~ s|.*/||mx;
 | 
						|
( my $DIR  = $0 ) =~ s|/?[^/]*$||mx;
 | 
						|
$DIR = '.' unless $DIR;
 | 
						|
 | 
						|
#-------------------------------------------------------------------------------
 | 
						|
# Declarations
 | 
						|
#-------------------------------------------------------------------------------
 | 
						|
#
 | 
						|
# Constants and other declarations
 | 
						|
#
 | 
						|
my $basedir    = "$ENV{HOME}/HPR/Database";
 | 
						|
my $configfile = "$basedir/.hpr_db.cfg";
 | 
						|
 | 
						|
#
 | 
						|
# Declarations
 | 
						|
#
 | 
						|
my ( $dbh, $sth1, $h1, $rc );
 | 
						|
my (%changes);
 | 
						|
 | 
						|
#
 | 
						|
# Enable Unicode mode
 | 
						|
#
 | 
						|
binmode STDOUT, ":encoding(UTF-8)";
 | 
						|
binmode STDERR, ":encoding(UTF-8)";
 | 
						|
 | 
						|
#
 | 
						|
# Options and arguments
 | 
						|
#
 | 
						|
my %options;
 | 
						|
Options( \%options );
 | 
						|
 | 
						|
#
 | 
						|
# Default help
 | 
						|
#
 | 
						|
pod2usage( -msg => "Version $VERSION\n", -exitval => 1 )
 | 
						|
    if ( $options{'help'} );
 | 
						|
 | 
						|
#
 | 
						|
# Collect options
 | 
						|
#
 | 
						|
my $DEBUG = ( $options{'debug'} ? $options{'debug'} : 0 );
 | 
						|
 | 
						|
my $cfgfile
 | 
						|
    = ( defined( $options{config} ) ? $options{config} : $configfile );
 | 
						|
 | 
						|
my $updatedb = ( defined( $options{'updatedb'} ) ? $options{'updatedb'} : 0 );
 | 
						|
 | 
						|
my $title   = ( defined( $options{'title'} )   ? $options{'title'}   : 0 );
 | 
						|
my $summary = ( defined( $options{'summary'} ) ? $options{'summary'} : 0 );
 | 
						|
my $tags    = ( defined( $options{'tags'} )    ? $options{'tags'}    : 0 );
 | 
						|
my $notes   = ( defined( $options{'notes'} )   ? $options{'notes'}   : 0 );
 | 
						|
my $ctitle  = ( defined( $options{'ctitle'} )  ? $options{'ctitle'}  : 0 );
 | 
						|
my $ctext   = ( defined( $options{'ctext'} )   ? $options{'ctext'}   : 0 );
 | 
						|
my $cnumber = $options{'cnumber'};
 | 
						|
 | 
						|
die "Select one of -title, -summary, -tags, -notes, -ctitle and -ctext\n"
 | 
						|
    unless ( $title || $summary || $tags || $notes || $ctitle || $ctext );
 | 
						|
 | 
						|
die "Needs a comment number (-cnumber=N)\n"
 | 
						|
    if ( ( $ctitle || $ctext ) && ( !$cnumber ) );
 | 
						|
 | 
						|
#
 | 
						|
# Get the arg
 | 
						|
#
 | 
						|
my $show = shift;
 | 
						|
pod2usage( -msg => "Specify the show number\n", -exitval => 1 ) unless $show;
 | 
						|
 | 
						|
#
 | 
						|
# Sanity check
 | 
						|
#
 | 
						|
die "Unable to find $cfgfile\n" unless ( -e $cfgfile );
 | 
						|
 | 
						|
#
 | 
						|
# Load configuration data
 | 
						|
#
 | 
						|
my $conf = new Config::General(
 | 
						|
    -ConfigFile      => $cfgfile,
 | 
						|
    -InterPolateVars => 1,
 | 
						|
    -ExtendedAccess  => 1
 | 
						|
);
 | 
						|
my %config = $conf->getall();
 | 
						|
 | 
						|
#
 | 
						|
# Connect to the database
 | 
						|
#
 | 
						|
my $dbhost = $config{database}->{host} // '127.0.0.1';
 | 
						|
my $dbport = $config{database}->{port} // 3306;
 | 
						|
my $dbname = $config{database}->{name};
 | 
						|
my $dbuser = $config{database}->{user};
 | 
						|
my $dbpwd  = $config{database}->{password};
 | 
						|
 | 
						|
#$dbh = DBI->connect( "DBI:MariaDB:database=$dbname;host=$dbhost;port=$dbport",
 | 
						|
#    $dbuser, $dbpwd, { AutoCommit => 1 } )
 | 
						|
#    or die $DBI::errstr;
 | 
						|
 | 
						|
$dbh = DBI->connect( "dbi:mysql:database=$dbname;host=$dbhost;port=$dbport",
 | 
						|
    $dbuser, $dbpwd, { AutoCommit => 1 } )
 | 
						|
    or die $DBI::errstr;
 | 
						|
 | 
						|
#
 | 
						|
# Enable client-side UTF8
 | 
						|
#
 | 
						|
$dbh->{mysql_enable_utf8} = 1;
 | 
						|
 | 
						|
#
 | 
						|
# Prepare to read the database for the selected episode and count the number
 | 
						|
# of comments it has in case we need to know later.
 | 
						|
#
 | 
						|
$sth1 = $dbh->prepare(q{
 | 
						|
    SELECT
 | 
						|
        e.*, count(c.id) as comment_count
 | 
						|
    FROM eps e
 | 
						|
    LEFT JOIN comments c ON e.id = c.eps_id
 | 
						|
    GROUP BY e.id
 | 
						|
    HAVING e.id = ?
 | 
						|
});
 | 
						|
$sth1->execute($show);
 | 
						|
if ( $dbh->err ) {
 | 
						|
    warn $dbh->errstr;
 | 
						|
}
 | 
						|
 | 
						|
#
 | 
						|
# Did we find the episode?
 | 
						|
#
 | 
						|
if ( $h1 = $sth1->fetchrow_hashref ) {
 | 
						|
    #
 | 
						|
    # Found, so do the episode details need changing?
 | 
						|
    #
 | 
						|
    if ( $title || $summary || $tags || $notes ) {
 | 
						|
        change_episode( $dbh, $h1, $show, $updatedb, $title, $summary, $tags,
 | 
						|
            $notes );
 | 
						|
    }
 | 
						|
 | 
						|
    #
 | 
						|
    # Are we to change comment details?
 | 
						|
    #
 | 
						|
    if ( $ctitle || $ctext ) {
 | 
						|
        if ( $h1->{comment_count} > 0 ) {
 | 
						|
            change_comment( $dbh, $h1, $show, $cnumber, $updatedb, $ctitle,
 | 
						|
                $ctext );
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            print "This show has no comments\n";
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
else {
 | 
						|
    print "Unable to find show number $show\n";
 | 
						|
}
 | 
						|
 | 
						|
#$dbh->disconnect;
 | 
						|
 | 
						|
exit;
 | 
						|
 | 
						|
#===  FUNCTION  ================================================================
 | 
						|
#         NAME: change_episode
 | 
						|
#      PURPOSE: Make changes to a row in the 'eps' table for a show
 | 
						|
#   PARAMETERS: $dbh            open handle of the MySQL database
 | 
						|
#               $h              handle of the query that returned the episode
 | 
						|
#                               record and comment count
 | 
						|
#               $show           show number being updated
 | 
						|
#               $updatedb       Boolean; true when changes are to be made
 | 
						|
#               $title          Boolean; true when the episode title is to be
 | 
						|
#                               changed
 | 
						|
#               $summary        Boolean; true when the episode summary is to be
 | 
						|
#                               changed
 | 
						|
#               $tags           Boolean; true when the episode tags are to be
 | 
						|
#                               changed
 | 
						|
#               $notes          Boolean; true when the episode notes are to be
 | 
						|
#                               changed
 | 
						|
#      RETURNS: Nothing
 | 
						|
#  DESCRIPTION: The episode has been found in the database. The requested
 | 
						|
#               changes are applied. If after comparing old with new changes
 | 
						|
#               are found they are applied, otherwise nothing is done.
 | 
						|
#       THROWS: No exceptions
 | 
						|
#     COMMENTS: None
 | 
						|
#     SEE ALSO: N/A
 | 
						|
#===============================================================================
 | 
						|
sub change_episode {
 | 
						|
    my ( $dbh, $h, $show, $updatedb, $title, $summary, $tags, $notes ) = @_;
 | 
						|
 | 
						|
    my (%changes);
 | 
						|
 | 
						|
    #<<< [perltidy messes up the following]
 | 
						|
    if ($title) {
 | 
						|
        $changes{title} = check_field( 'title',
 | 
						|
            scalar( run_editor( $h->{title} ) ), 100, qr{(\n)} );
 | 
						|
    }
 | 
						|
    if ($summary) {
 | 
						|
        $changes{summary} = check_field( 'summary',
 | 
						|
            scalar( run_editor( $h->{summary} ) ), 100, qr{(\n)} );
 | 
						|
    }
 | 
						|
    if ($tags) {
 | 
						|
        $changes{tags} = check_field( 'tags',
 | 
						|
            scalar( run_editor( $h->{tags} ) ), 200, qr{(\n)} );
 | 
						|
    }
 | 
						|
    if ($notes) {
 | 
						|
        $changes{notes} = run_editor( $h->{notes}, ['+set filetype=html'] );
 | 
						|
    }
 | 
						|
    #>>>
 | 
						|
    print Dumper( \%changes ), "\n" if ( $DEBUG > 2 );
 | 
						|
 | 
						|
    #
 | 
						|
    # Are we updating the database then?
 | 
						|
    #
 | 
						|
    if ($updatedb) {
 | 
						|
        #
 | 
						|
        # Was anything edited?
 | 
						|
        #
 | 
						|
        if (%changes) {
 | 
						|
            #
 | 
						|
            # Did the edits do anything? If not delete the element from the
 | 
						|
            # hash since there's no point writing it to the database
 | 
						|
            #
 | 
						|
            for my $key ( keys(%changes) ) {
 | 
						|
                if ( $changes{$key} eq $h->{$key} ) {
 | 
						|
                    print "No change made to $key, ignored\n";
 | 
						|
                    delete( $changes{$key} );
 | 
						|
                }
 | 
						|
            }
 | 
						|
            print Dumper( \%changes ), "\n" if ( $DEBUG > 2 );
 | 
						|
        }
 | 
						|
 | 
						|
        #
 | 
						|
        # If there's anything left apply the changes
 | 
						|
        #
 | 
						|
        if (%changes) {
 | 
						|
            #
 | 
						|
            # Go into transaction mode here so we can fail safely
 | 
						|
            #
 | 
						|
            $rc = $dbh->begin_work or die $dbh->errstr;
 | 
						|
 | 
						|
            my $sql = SQL::Abstract->new;
 | 
						|
            my %where = ( id => $show );
 | 
						|
            my ( $stmt, @bind ) = $sql->update( 'eps', \%changes, \%where );
 | 
						|
 | 
						|
            my $sth = $dbh->prepare($stmt);
 | 
						|
            my $rv  = $sth->execute(@bind);
 | 
						|
 | 
						|
            #
 | 
						|
            # Respond to any error by rolling back
 | 
						|
            #
 | 
						|
            if ( $dbh->err ) {
 | 
						|
                warn $dbh->errstr;
 | 
						|
                eval { $dbh->rollback };
 | 
						|
                $rv = 0;
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                $dbh->commit;
 | 
						|
            }
 | 
						|
            $rv = 0 if ( $rv eq '0E0' );
 | 
						|
 | 
						|
            #
 | 
						|
            # Report the update
 | 
						|
            #
 | 
						|
            if ($rv) {
 | 
						|
                my $ccount = scalar( keys(%changes) );
 | 
						|
                printf "Updated database (%d %s to the eps row)\n",
 | 
						|
                    $ccount, _plural( 'change', $ccount );
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                print "Episode not updated due to error\n";
 | 
						|
            }
 | 
						|
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            #
 | 
						|
            # No changes were found
 | 
						|
            #
 | 
						|
            print "There was nothing to do\n";
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        print "Option -noupdatedb chosen database not updated\n";
 | 
						|
    }
 | 
						|
 | 
						|
    return;
 | 
						|
}
 | 
						|
 | 
						|
#===  FUNCTION  ================================================================
 | 
						|
#         NAME: change_comment
 | 
						|
#      PURPOSE: Make changes to a comment relating to a show
 | 
						|
#   PARAMETERS: $dbh            open handle of the MySQL database
 | 
						|
#               $h              handle of the query that returned the episode
 | 
						|
#                               record and comment count
 | 
						|
#               $show           show number being updated
 | 
						|
#               $cnumber        comment number within show
 | 
						|
#               $updatedb       Boolean; true when changes are to be made
 | 
						|
#               $ctitle         Boolean; true when the comment title is to be
 | 
						|
#                               changed
 | 
						|
#               $ctext          Boolean; true when the comment text is to be
 | 
						|
#                               changed
 | 
						|
#      RETURNS: Nothing
 | 
						|
#  DESCRIPTION: The episode has been found in the database and the number of
 | 
						|
#               comments determined. We know there are more than zero comments
 | 
						|
#               otherwise this routine woulkd not have been called. We check
 | 
						|
#               that the requested comment number is in range here (if could
 | 
						|
#               have been done before invocation). We query the target comment
 | 
						|
#               and modify one or both of the requested fields. If, after
 | 
						|
#               comparing old with new, changes are found, they are applied,
 | 
						|
#               otherwise nothing is done.
 | 
						|
#       THROWS: No exceptions
 | 
						|
#     COMMENTS: None
 | 
						|
#     SEE ALSO: N/A
 | 
						|
#===============================================================================
 | 
						|
sub change_comment {
 | 
						|
    my ( $dbh, $h, $show, $cnumber, $updatedb, $ctitle, $ctext ) = @_;
 | 
						|
 | 
						|
    my ( $sth1, $h1 );
 | 
						|
    my (%changes);
 | 
						|
 | 
						|
    if ( $cnumber <= $h->{comment_count} ) {
 | 
						|
        #
 | 
						|
        # Get the requested comment
 | 
						|
        #
 | 
						|
        $sth1 = $dbh->prepare(
 | 
						|
            q{
 | 
						|
                SELECT *
 | 
						|
                FROM comments
 | 
						|
                WHERE eps_id = ?
 | 
						|
                ORDER BY comment_timestamp
 | 
						|
                LIMIT 1
 | 
						|
                OFFSET ?
 | 
						|
            }
 | 
						|
        );
 | 
						|
        $sth1->execute( $show, $cnumber - 1 );
 | 
						|
        if ( $dbh->err ) {
 | 
						|
            warn $dbh->errstr;
 | 
						|
        }
 | 
						|
 | 
						|
        #
 | 
						|
        # If found perform changes, otherwise it wasn't found (not sure how
 | 
						|
        # that's possible but you never know)
 | 
						|
        #
 | 
						|
        if ( $h1 = $sth1->fetchrow_hashref ) {
 | 
						|
            if ($ctitle) {
 | 
						|
                $changes{comment_title} = run_editor( $h1->{comment_title} );
 | 
						|
            }
 | 
						|
            if ($ctext) {
 | 
						|
                $changes{comment_text} = run_editor( $h1->{comment_text} );
 | 
						|
            }
 | 
						|
            print Dumper( \%changes ), "\n" if ( $DEBUG > 2 );
 | 
						|
 | 
						|
            #
 | 
						|
            # Are we updating the database then?
 | 
						|
            #
 | 
						|
            if ($updatedb) {
 | 
						|
                #
 | 
						|
                # Was anything edited?
 | 
						|
                #
 | 
						|
                if (%changes) {
 | 
						|
                    #
 | 
						|
                    # Did the edits do anything? If not delete the element from the
 | 
						|
                    # hash since there's no point writing it to the database
 | 
						|
                    #
 | 
						|
                    for my $key ( keys(%changes) ) {
 | 
						|
                        if ( $changes{$key} eq $h1->{$key} ) {
 | 
						|
                            print "No change made to $key, ignored\n";
 | 
						|
                            delete( $changes{$key} );
 | 
						|
                        }
 | 
						|
                    }
 | 
						|
                    print Dumper( \%changes ), "\n" if ( $DEBUG > 2 );
 | 
						|
                }
 | 
						|
 | 
						|
                #
 | 
						|
                # If there's anything left apply the changes
 | 
						|
                #
 | 
						|
                if (%changes) {
 | 
						|
                    #
 | 
						|
                    # Go into transaction mode here so we can fail safely
 | 
						|
                    #
 | 
						|
                    $rc = $dbh->begin_work or die $dbh->errstr;
 | 
						|
 | 
						|
                    my $sql = SQL::Abstract->new;
 | 
						|
                    my %where = ( id => $h1->{id} );
 | 
						|
                    my ( $stmt, @bind )
 | 
						|
                        = $sql->update( 'comments', \%changes, \%where );
 | 
						|
 | 
						|
                    my $sth = $dbh->prepare($stmt);
 | 
						|
                    my $rv  = $sth->execute(@bind);
 | 
						|
 | 
						|
                    #
 | 
						|
                    # Respond to any error by rolling back
 | 
						|
                    #
 | 
						|
                    if ( $dbh->err ) {
 | 
						|
                        warn $dbh->errstr;
 | 
						|
                        eval { $dbh->rollback };
 | 
						|
                        $rv = 0;
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        $dbh->commit;
 | 
						|
                    }
 | 
						|
                    $rv = 0 if ( $rv eq '0E0' );
 | 
						|
 | 
						|
                    #
 | 
						|
                    # Report the update
 | 
						|
                    #
 | 
						|
                    if ($rv) {
 | 
						|
                        my $ccount = scalar(keys(%changes));
 | 
						|
                        printf "Updated database (%d %s to the comments row)\n",
 | 
						|
                            $ccount, _plural( 'change', $ccount );
 | 
						|
                    }
 | 
						|
                    else {
 | 
						|
                        print "Comment not updated due to error\n";
 | 
						|
                    }
 | 
						|
 | 
						|
                }
 | 
						|
                else {
 | 
						|
                    print "There was nothing to do\n";
 | 
						|
                }
 | 
						|
            }
 | 
						|
            else {
 | 
						|
                print "Option -noupdatedb chosen database not updated\n";
 | 
						|
            }
 | 
						|
        }
 | 
						|
    }
 | 
						|
    else {
 | 
						|
        print "Requested comment is out of range\n";
 | 
						|
    }
 | 
						|
 | 
						|
    return;
 | 
						|
}
 | 
						|
 | 
						|
#===  FUNCTION  ================================================================
 | 
						|
#         NAME: run_editor
 | 
						|
#      PURPOSE: Run an interactive vim editor on a string
 | 
						|
#   PARAMETERS: $string         String to edit
 | 
						|
#               $options        An arrayref containing options for vim
 | 
						|
#                               (optional) Example '+set paste'. Each option
 | 
						|
#                               (such as '-c startinsert') needs to be
 | 
						|
#                               a separate array element.
 | 
						|
#      RETURNS: Edited string
 | 
						|
#  DESCRIPTION: Makes a temporary file with File::Temp ensuring that the file
 | 
						|
#               is in utf8 mode. Writes the edit string to the file and invokes
 | 
						|
#               the 'vim' editor on it. The resulting file is then read back
 | 
						|
#               into a string and returned to the caller, again taking care to
 | 
						|
#               retain utf8 mode.
 | 
						|
#       THROWS: No exceptions
 | 
						|
#     COMMENTS: File::Slurp and UTF-8 don't go well together. Moved to
 | 
						|
#               File::Slurper instead
 | 
						|
#     SEE ALSO: N/A
 | 
						|
#===============================================================================
 | 
						|
sub run_editor {
 | 
						|
    my ( $string, $options ) = @_;
 | 
						|
 | 
						|
    #
 | 
						|
    # Build an arguments array for 'system'
 | 
						|
    #
 | 
						|
    my @args;
 | 
						|
    push( @args, @$options ) if $options;
 | 
						|
 | 
						|
    #
 | 
						|
    # Make a temporary file
 | 
						|
    #
 | 
						|
    my $tfh = File::Temp->new;
 | 
						|
    binmode $tfh, ":encoding(UTF-8)";
 | 
						|
    my $tfn = $tfh->filename;
 | 
						|
    print $tfh $string if $string;
 | 
						|
    $tfh->close;
 | 
						|
 | 
						|
    #
 | 
						|
    # Add the filename to the arguments
 | 
						|
    #
 | 
						|
    push( @args, $tfn );
 | 
						|
 | 
						|
    die "Edit failed\n"
 | 
						|
        unless ( system( ( 'vim', @args ) ) == 0 );
 | 
						|
 | 
						|
    return read_text($tfn);
 | 
						|
}
 | 
						|
 | 
						|
#===  FUNCTION  ================================================================
 | 
						|
#         NAME: check_field
 | 
						|
#      PURPOSE: Checks the a field is not too long and doesn't contain certain
 | 
						|
#               characters
 | 
						|
#   PARAMETERS: $name           name of field
 | 
						|
#               $field          string to be checked
 | 
						|
#               $maxlen         maximum string length
 | 
						|
#               $regex          regex containing illegal characters to be removed
 | 
						|
#      RETURNS: The input string truncated and with any illegal characters
 | 
						|
#               removed.
 | 
						|
#  DESCRIPTION: Runs a substitution on the string then truncates the result if
 | 
						|
#               it is too long.
 | 
						|
#       THROWS: No exceptions
 | 
						|
#     COMMENTS: None
 | 
						|
#     SEE ALSO: N/A
 | 
						|
#===============================================================================
 | 
						|
sub check_field {
 | 
						|
    my ( $name, $field, $maxlen, $regex ) = @_;
 | 
						|
 | 
						|
    return unless $field;
 | 
						|
 | 
						|
    $field =~ s/$regex//g;
 | 
						|
    if ( length($field) > $maxlen ) {
 | 
						|
        warn "Field '$name' too long ("
 | 
						|
            . length($field)
 | 
						|
            . "); truncated to "
 | 
						|
            . $maxlen . "\n";
 | 
						|
        $field = substr( $field, 0, $maxlen );
 | 
						|
    }
 | 
						|
    return $field;
 | 
						|
}
 | 
						|
 | 
						|
#===  FUNCTION  ================================================================
 | 
						|
#         NAME: _plural
 | 
						|
#      PURPOSE: Add an 's' to a word depending on a number
 | 
						|
#   PARAMETERS: $word           word to pluralise
 | 
						|
#               $count          number being used in message
 | 
						|
#      RETURNS: The word in a plural form or not
 | 
						|
#  DESCRIPTION: Just hides the expression that adds an 's' or not behind
 | 
						|
#               a function call
 | 
						|
#       THROWS: No exceptions
 | 
						|
#     COMMENTS: None
 | 
						|
#     SEE ALSO: N/A
 | 
						|
#===============================================================================
 | 
						|
sub _plural {
 | 
						|
    my ( $word, $count ) = @_;
 | 
						|
 | 
						|
    return $word . ( abs($count) != 1 ? 's' : '' );
 | 
						|
}
 | 
						|
 | 
						|
#===  FUNCTION  ================================================================
 | 
						|
#         NAME: Options
 | 
						|
#      PURPOSE: Processes command-line options
 | 
						|
#   PARAMETERS: $optref     Hash reference to hold the options
 | 
						|
#      RETURNS: Undef
 | 
						|
#  DESCRIPTION:
 | 
						|
#       THROWS: no exceptions
 | 
						|
#     COMMENTS: none
 | 
						|
#     SEE ALSO: n/a
 | 
						|
#===============================================================================
 | 
						|
sub Options {
 | 
						|
    my ($optref) = @_;
 | 
						|
 | 
						|
    my @options = (
 | 
						|
        "help",     "debug=i", "config=s", "updatedb!", "title!",
 | 
						|
        "summary!", "tags!",   "notes!", "ctitle!", "ctext!", "cnumber=i",
 | 
						|
    );
 | 
						|
 | 
						|
    if ( !GetOptions( $optref, @options ) ) {
 | 
						|
        pod2usage( -msg => "Version $VERSION\n", -exitval => 1 );
 | 
						|
    }
 | 
						|
 | 
						|
    return;
 | 
						|
}
 | 
						|
 | 
						|
__END__
 | 
						|
 | 
						|
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 | 
						|
#  Application Documentation
 | 
						|
#%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
 | 
						|
#{{{
 | 
						|
 | 
						|
=head1 NAME
 | 
						|
 | 
						|
edit_episode - edit one or more fields in the database for a given HPR show
 | 
						|
 | 
						|
=head1 VERSION
 | 
						|
 | 
						|
This documentation refers to edit_episode version 0.1.3
 | 
						|
 | 
						|
 | 
						|
=head1 USAGE
 | 
						|
 | 
						|
    edit_episode [-help] [-debug=N] [-config=FILE] [-[no]updatedb] [-[no]title]
 | 
						|
        [-[no]summary] [-[no]tags] [-[no]notes] [-[no]ctitle] [-[no]ctext]
 | 
						|
        [-cnumber=N] shownumber
 | 
						|
 | 
						|
    edit_episode -updatedb -title 1234
 | 
						|
    edit_episode -updatedb -title -summary 2000
 | 
						|
    edit_episode -updatedb -tags 2050
 | 
						|
    edit_episode -updatedb -notes 2045
 | 
						|
    edit_episode -updatedb -ctext -cnumber=1 2813
 | 
						|
 | 
						|
 | 
						|
=head1 REQUIRED ARGUMENTS
 | 
						|
 | 
						|
=over 4
 | 
						|
 | 
						|
=item B<shownumber>
 | 
						|
 | 
						|
The script must be provided with a single show number to operate on.
 | 
						|
 | 
						|
=back
 | 
						|
 | 
						|
=head1 OPTIONS
 | 
						|
 | 
						|
=over 4
 | 
						|
 | 
						|
=item B<-[no]updatedb>
 | 
						|
 | 
						|
This option is required to make the script apply any changes that are made to
 | 
						|
the database. By default no updates are applied (B<-noupdatedb>).
 | 
						|
 | 
						|
=item B<-[no]title>
 | 
						|
 | 
						|
This option, if given (as B<-title>) indicates that the 'title' field of the
 | 
						|
row for the selected episode is to be edited. The Vim editor is invoked to
 | 
						|
make changes. The default is B<-notitle> meaning that this field is not to be
 | 
						|
edited.
 | 
						|
 | 
						|
=item B<-[no]summary>
 | 
						|
 | 
						|
This option, if given (as B<-summary>) indicates that the 'summary' field of the
 | 
						|
row for the selected episode is to be edited. The Vim editor is invoked to
 | 
						|
make changes. The default is B<-nosummary> meaning that this field is not to be
 | 
						|
edited.
 | 
						|
 | 
						|
=item B<-[no]tags>
 | 
						|
 | 
						|
This option, if given (as B<-tags>) indicates that the 'tags' field of the
 | 
						|
row for the selected episode is to be edited. The Vim editor is invoked to
 | 
						|
make changes. The default is B<-notags> meaning that this field is not to be
 | 
						|
edited.
 | 
						|
 | 
						|
=item B<-[no]notes>
 | 
						|
 | 
						|
This option, if given (as B<-notes>) indicates that the 'notes' field of the
 | 
						|
row for the selected episode is to be edited. The Vim editor is invoked to
 | 
						|
make changes. The default is B<-nonotes> meaning that this field is not to be
 | 
						|
edited.
 | 
						|
 | 
						|
=item B<-[no]ctitle>
 | 
						|
 | 
						|
This option, if given (as B<-ctitle>) indicates that the 'title' field of the
 | 
						|
row for the selected comment is to be edited. The Vim editor is invoked to
 | 
						|
make changes. The default is B<-noctitle> meaning that this field is not to be
 | 
						|
edited.
 | 
						|
 | 
						|
=item B<-[no]ctext>
 | 
						|
 | 
						|
This option, if given (as B<-ctext>) indicates that the 'comment_text' field
 | 
						|
of the row for the selected comment is to be edited. The Vim editor is invoked
 | 
						|
to make changes. The default is B<-noctext> meaning that this field is not to
 | 
						|
be edited.
 | 
						|
 | 
						|
=item B<-cnumber=N>
 | 
						|
 | 
						|
If comment fields are being edited then a comment index is required. The
 | 
						|
comments are numbered starting from 1 and are sorted in the submission
 | 
						|
timestamp order. This number must be in the range 1..N where I<N> is the
 | 
						|
number of comments on this particular show.
 | 
						|
 | 
						|
=item B<-config=FILE>
 | 
						|
 | 
						|
This option allows an alternative configuration file to be used. This file
 | 
						|
defines the location of the database, its port, its name and the username and
 | 
						|
password to be used to access it. This feature was added to allow the script
 | 
						|
to access alternative databases or the live database over an SSH tunnel.
 | 
						|
 | 
						|
See the CONFIGURATION AND ENVIRONMENT section below for the file format.
 | 
						|
 | 
						|
If the option is omitted the default file is used: B<.hpr_db.cfg>
 | 
						|
 | 
						|
=back
 | 
						|
 | 
						|
At least one of the options B<-title>, B<-summary>, B<-tags> and B<-notes>
 | 
						|
must be provided otherwise the script will abort with an error.
 | 
						|
 | 
						|
=head1 DESCRIPTION
 | 
						|
 | 
						|
The script B<edit_episode> provides an editor interface to certain fields in
 | 
						|
the HPR database. The fields are:
 | 
						|
 | 
						|
=over 4
 | 
						|
 | 
						|
=item B<title>
 | 
						|
 | 
						|
    A single line of up to 100 characters of text. The line is rendered as an
 | 
						|
    "<h1>" tag on the web page and is incorporated into the RSS feed, so it
 | 
						|
    must only contain characters legal in these contexts.
 | 
						|
 | 
						|
=item B<summary>
 | 
						|
 | 
						|
    A single line of up to 100 characters of text. The line is rendered as an
 | 
						|
    "<h3>" tag on the web page and is incorporated into the RSS feed, so it
 | 
						|
    must only contain characters legal in these contexts.
 | 
						|
 | 
						|
=item B<tags>
 | 
						|
 | 
						|
    A single line of up to 200 characters of text. The field holds tags
 | 
						|
    relevant to the content of the episode in CSV format.
 | 
						|
 | 
						|
=item B<notes>
 | 
						|
 | 
						|
    A block of HTML which is to be included inside "<article>" tags making up
 | 
						|
    the show notes on the web page for the episode.
 | 
						|
 | 
						|
=item B<comment_title>
 | 
						|
 | 
						|
    A single line of text. The title is stored in a 'text' field in the
 | 
						|
    database and could be of any length, but will not be rendered correctly
 | 
						|
    if it exceeds 100 characters.
 | 
						|
 | 
						|
=item B<comment_text>
 | 
						|
 | 
						|
    A block of text (NOT HTML) which is the body of the comment. There are no
 | 
						|
    limit contraints here although the code that initiaslly accepts a comment
 | 
						|
    does impose a limit. Thus it would be unwise to make this field too large.
 | 
						|
 | 
						|
=back
 | 
						|
 | 
						|
=head1 DIAGNOSTICS
 | 
						|
 | 
						|
=over 4
 | 
						|
 | 
						|
=item B<Select one of -title, -summary, -tags, -notes, -ctitle and -ctext>
 | 
						|
 | 
						|
At least one of these options is required. This a fatal error.
 | 
						|
 | 
						|
=item B<Needs a comment number (-cnumber=N)>
 | 
						|
 | 
						|
If one of B<-ctitle> and B<-ctext> is provided then a comment number is needed.
 | 
						|
 | 
						|
=item B<Specify the show number>
 | 
						|
 | 
						|
The show number has been omitted. This a fatal error.
 | 
						|
 | 
						|
=item B<DBI connect ... failed: Access denied for user ... >
 | 
						|
 | 
						|
The database connection has been denied. Check the configuration details (see
 | 
						|
below). This a fatal error.
 | 
						|
 | 
						|
=item B<Edit failed>
 | 
						|
 | 
						|
If the Vim edit session fails in some way the script reports it this way.
 | 
						|
 | 
						|
=back
 | 
						|
 | 
						|
=head1 CONFIGURATION AND ENVIRONMENT
 | 
						|
 | 
						|
The script obtains the credentials it requires to open the HPR database from
 | 
						|
a configuration file. The name of the file it expects is B<.hpr_db.cfg> in the
 | 
						|
directory holding the script. This configuration file can be overridden using
 | 
						|
the B<-config=FILE> option as described above.
 | 
						|
 | 
						|
The configuration file format is as follows:
 | 
						|
 | 
						|
 <database>
 | 
						|
     host = 127.0.0.1
 | 
						|
     port = PORT
 | 
						|
     name = DATABASE
 | 
						|
     user = USERNAME
 | 
						|
     password = PASSWORD
 | 
						|
 </database>
 | 
						|
 | 
						|
=head1 DEPENDENCIES
 | 
						|
 | 
						|
 Config::General
 | 
						|
 DBI
 | 
						|
 Data::Dumper
 | 
						|
 File::Slurp
 | 
						|
 File::Temp
 | 
						|
 Getopt::Long
 | 
						|
 Pod::Usage
 | 
						|
 SQL::Abstract
 | 
						|
 | 
						|
=head1 BUGS AND LIMITATIONS
 | 
						|
 | 
						|
There are no known bugs in this module.
 | 
						|
Please report problems to Dave Morriss  (Dave.Morriss@gmail.com)
 | 
						|
Patches are welcome.
 | 
						|
 | 
						|
=head1 AUTHOR
 | 
						|
 | 
						|
Dave Morriss (Dave.Morriss@gmail.com)
 | 
						|
 | 
						|
 | 
						|
=head1 LICENCE AND COPYRIGHT
 | 
						|
 | 
						|
Copyright (c) 2015-2019 Dave Morriss (Dave.Morriss@gmail.com). All rights reserved.
 | 
						|
 | 
						|
This module is free software; you can redistribute it and/or
 | 
						|
modify it under the same terms as Perl itself. See perldoc perlartistic.
 | 
						|
 | 
						|
This program is distributed in the hope that it will be useful,
 | 
						|
but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
						|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 | 
						|
 | 
						|
=cut
 | 
						|
 | 
						|
#}}}
 | 
						|
 | 
						|
# [zo to open fold, zc to close]
 | 
						|
 | 
						|
# vim: syntax=perl:ts=8:sw=4:et:ai:tw=78:fo=tcrqn21:fdm=marker
 |