| 
									
										
										
										
											2024-06-04 16:35:44 +01:00
										 |  |  | #!/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 | 
					
						
							| 
									
										
										
										
											2025-05-06 21:35:23 +01:00
										 |  |  | #      VERSION: 0.1.4 | 
					
						
							| 
									
										
										
										
											2024-06-04 16:35:44 +01:00
										 |  |  | #      CREATED: 2015-06-17 23:17:50 | 
					
						
							| 
									
										
										
										
											2025-05-06 21:35:23 +01:00
										 |  |  | #     REVISION: 2024-07-20 11:21:19 | 
					
						
							| 
									
										
										
										
											2024-06-04 16:35:44 +01:00
										 |  |  | # | 
					
						
							|  |  |  | #=============================================================================== | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 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) | 
					
						
							|  |  |  | # | 
					
						
							| 
									
										
										
										
											2025-05-06 21:35:23 +01:00
										 |  |  | our $VERSION = '0.1.4'; | 
					
						
							| 
									
										
										
										
											2024-06-04 16:35:44 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | # | 
					
						
							|  |  |  | # 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) ); | 
					
						
							| 
									
										
										
										
											2025-05-06 21:35:23 +01:00
										 |  |  |                 printf "Updated database (%d %s to the eps row for show %s)\n", | 
					
						
							|  |  |  |                     $ccount, _plural( 'change', $ccount ), $show; | 
					
						
							| 
									
										
										
										
											2024-06-04 16:35:44 +01:00
										 |  |  |             } | 
					
						
							|  |  |  |             else { | 
					
						
							| 
									
										
										
										
											2025-05-06 21:35:23 +01:00
										 |  |  |                 print "Episode $show not updated due to error\n"; | 
					
						
							| 
									
										
										
										
											2024-06-04 16:35:44 +01:00
										 |  |  |             } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2025-05-06 21:35:23 +01:00
										 |  |  | This documentation refers to edit_episode version 0.1.4 | 
					
						
							| 
									
										
										
										
											2024-06-04 16:35:44 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | =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 |