#!/usr/bin/perl
use strict; 

#---------------------------------------------------------------------#
# scheduled_mailings.pl (Beatitude)
# For instructions, see the pod of this file. try:
#  pod2text ./scheduled_mailings.pl | less
#
# Or try online: 
#  http://mojo.skazat.com/support/documentation/scheduled_mailings.pl.html
#
#---------------------------------------------------------------------#
# Required:

use CGI::Carp qw(fatalsToBrowser); 




$|++; 

#Change! the lib paths

use lib qw(

	../ 
	../DADA/perllib 
	../../../../perl 
	../../../../perllib

 /home/account/www/cgi-bin/dada
 /home/account/www/cgi-bin/dada/DADA
 /home/account/www/cgi-bin/dada/DADA/perllib

/usr/local/lib/perl5/site_perl/5.8.0/mach
/usr/local/lib/perl5/site_perl/5.8.0
/usr/local/lib/perl5/site_perl
/usr/local/lib/perl5/5.8.0/BSDPAN
/usr/local/lib/perl5/5.8.0/mach
/usr/local/lib/perl5/5.8.0

);

use DADA::Config 3.0.0 qw(!:DEFAULT);
use CGI; my $q = new CGI;$q->charset($DADA::Config::HTML_CHARSET);
my $Plugin_Config = {}; 


#---------------------------------------------------------------------#
# Optional:

$Plugin_Config->{Log} = undef, # Example: $LOGS . '/schedules.txt';


# Usually, this doesn't need to be changed. 
# But, if you are having trouble saving settings 
# and are redirected to an 
# outside page, you may need to set this manually. 

$Plugin_Config->{Plugin_URL} = $q->url; 

$Plugin_Config->{Allow_Manual_Run}    = 1; 

$Plugin_Config->{Manual_Run_Passcode} = undef;

#---------------------------------------------------------------------#
# Nothing else to be configured.                                      #



$Plugin_Config->{Program_Name} = 'Beatitude'; 


my $App_Version          = '.6';

use DADA::Template::HTML; 
use DADA::App::Guts;
use DADA::MailingList::Schedules; 


my $mss; 
my $list; 
my $li; 
my $Have_Log = 0; 
my $yeah_root_login = 0; 


#---------------------------------------------------------------------#
# a "few" defaults
my $f; 

my %mail_month_values = (
0  => 'January', 
1  => 'February', 
2  => 'March', 
3  => 'April', 
4  => 'May', 
5  => 'June',
6  => 'July', 
7  => 'August', 
8  => 'September', 
9  => 'October', 
10 => 'November', 
11 => 'December', 
); 



my %mail_day_values = (

1  => '1st', 
2  => '2nd', 
3  => '3rd', 
4  => '4th', 
5  => '5th',
6  => '6th', 
7  => '7th', 
8  => '8th', 
9  => '9th', 
10 => '10th', 
11 => '11th',
12 => '12th', 
13 => '13th', 
14 => '14th', 
15 => '15th', 
16 => '16th', 
17 => '17th', 
18 => '18th', 
19 => '19th', 
20 => '20th', 
21 => '21st', 
22 => '22nd', 
23 => '23rd', 
24 => '24th', 
25 => '25th', 
26 => '26th', 
27 => '27th', 
28 => '28th', 
29 => '29th', 
30 => '30th', 
31 => '31st',
); 

my %mail_minute_values = (
0 => '00',
1 => '01', 
2 => '02',
3 => '03',
4 => '04',
5 => '05',
6 => '06',
7 => '07',
8 => '08',
9 => '09',
); 

my %mail_year_values = ( 
103  => 2003,
104  => 2004, 
105  => 2005, 
106  => 2006,
107  => 2007,
108  => 2008, 
109  => 2009, 
110  => 2010,
111  => 2011,
112  => 2012, 
113  => 2013, 
114  => 2014,
115  => 2015,
116  => 2016, 
117  => 2017, 
118  => 2018,
); 

my @hours   = (0..12); 

my %hours_labels = (
0  => 12, 
1  => 1, 
2  => 2, 
3  => 3, 
4  => 4, 
5  => 5, 
6  => 6, 
7  => 7, 
8  => 8, 
9  => 9, 
10 => 10, 
11 => 11, 
12 => 12
); 



my @minutes = (0..59); 

my %repeat_label_values = (
	minutes => 'Minute(s)',  
	hours   => 'Hour(s)', 
	days    => 'Day(s)', 
	years   => 'Year(s)'
);


my %d_form_vals = (
		message_name           => 'scheduled mailing', 
		active                  => 0, 
		mailing_date            => time, 
		repeat_times            => 1, 
		repeat_label            => 'days',
		repeat_mailing          => 0,
		only_send_to_list_owner => 0,
		archive_mailings        => 0,
		only_send_if_diff       => 0,
		self_destruct           => 0, 
		
		headers       => 
		
			{ 

				'Reply-To'        => undef, 
				'Return-Path'     => undef,
				Precedence      => undef, 
				'X-Priority'    => undef, 
				Subject         => undef,
				 
			},
				
		PlainText_ver => {
						   source                    => 'from_text',
						   use_email_template        => 1,
						   only_send_if_defined      => 0,
						   grab_headers_from_message => 0, 
						   
						 }, 
		HTML_ver      => { 				 
						   source                    => 'from_text',
						   use_email_template        => 1,
						   only_send_if_defined      => 0,
						   grab_headers_from_message => 0, 
						 	
						 },
		attachments            => [],
		partial_sending_params => [ 
										# { field_name => '', field_comparison_type => '', field_value => '' }, 
							  	  ], 
							
	); 


&init_vars(); 
&main(); 


sub init_vars { 

     while ( my $key = each %$Plugin_Config ) {
        
        if(exists($DADA::Config::PLUGIN_CONFIGS->{Beatitude}->{$key})){ 
        
            if(defined($DADA::Config::PLUGIN_CONFIGS->{Beatitude}->{$key})){ 
                    
                $Plugin_Config->{$key} = $DADA::Config::PLUGIN_CONFIGS->{Beatitude}->{$key};
        
            }
        }
     }
}



sub main { 
	if(!$ENV{GATEWAY_INTERFACE}){ 
		&cl_main(); 
	}else{ 
		&cgi_main(); 
	}
}

#---------------------------------------------------------------------#









	my $help     = 0;
	my $test; 
	my $verbose  = 0; 
	my $log; 
	my $version; 
	my $run_list; 
	
sub cl_main { 
	require Getopt::Long; 

	
	
	
	Getopt::Long::GetOptions(
		   
		   "help"       => \$help, 
		   "test"       => \$test, 
		   "log=s"      => \$log,
		   "version"    => \$version,
		   "verbose"    => \$verbose,
		   "list=s"     => \$run_list,  
		   
		   );

	$Plugin_Config->{Log}            = $log      if $log; 
	$verbose        = 1         if $test; 

	if($version){ 
		&version;
	}
	elsif($help){
		&show_help; 	
	}
	else{ 
	
        cl_run_schedules(
                      -run_list => $run_list,
                      -test     => $test, 
                      -verbose  => $verbose
                     );
	}	    
	
}




sub cl_run_schedules { 

	my %args = (-run_list => undef, 
				-test     => undef, 
				-verbose  => undef,
				@_); 
	
	
	my @lists_to_run; 
	 
	$args{-run_list} ? ($lists_to_run[0] = $args{-run_list}) : (@lists_to_run = DADA::App::Guts::available_lists()); 
	
	foreach(@lists_to_run){ 
		my $mss = DADA::MailingList::Schedules->new({-list => $_}); 
		   my $report = $mss->run_schedules(-test    => $args{-test});
		   					  				
	
	print $report if $args{-verbose};
	
	logit($report); 

    } 
}




sub cgi_main { 

    if(keys %{$q->Vars}                        && 
       $q->param('run')                        && 
       xss_filter($q->param('run'))       == 1 &&
       $Plugin_Config->{Allow_Manual_Run} == 1
      ) { 
        cgi_manual_start();
    }
    else { 
        
        
        my ($admin_list, $root_login) = check_list_security(-cgi_obj   => $q, 
                                                            -Function  => 'scheduled_mailings');
                                                                                                                    
    
          $list = $admin_list; 
          $yeah_root_login = $root_login; 
    
          require  DADA::MailingList::Settings; 
          my $ls = DADA::MailingList::Settings->new({-list => $list}); 
             $li = $ls->get; 
          
                                      
        
        
        #---------------------------------------------------------------------#
        
        $mss = DADA::MailingList::Schedules->new({-list => $list}); 
        my $flavor = $q->param('flavor') || 'default';
        my %Mode = ( 
        'default' => \&default, 
        'edit'    => \&edit, 
        'remove'  => \&remove, 
        'run_all' => \&run_all_handler, 
        ); 
        
        if(exists($Mode{$flavor})) { 
            $Mode{$flavor}->();  #call the correct subroutine 
        }else{
            &default;
        }
    }

}




sub cgi_manual_start { 
        
        if(
            (xss_filter($q->param('passcode')) eq $Plugin_Config->{Manual_Run_Passcode}) ||             
            ($Plugin_Config->{Manual_Run_Passcode}              eq ''                  )
            
          ) {
            
            if(defined(xss_filter($q->param('verbose')))){
                $verbose = xss_filter($q->param('verbose'));
            }
            else { 
                $verbose = 1;
            }
            
            
            if(defined(xss_filter($q->param('test')))){
                $test = $q->param('test');
            }
            
            # DEV: Why is this, "$run_list" and simply not, "$list?!"
            if(defined(xss_filter($q->param('list')))){
                $run_list = $q->param('list');
            }
            
            
            
            #if(defined(xss_filter($q->param('messages')))){ 
            #    $Plugin_Config->{MessagesAtOnce} = xss_filter($q->param('messages')); 
            #}
            
            
            print $q->header();
			
        	print '<pre>'
        	    if $verbose; 
            cl_main();
            print '</pre>'
                if $verbose; 
            

        } else { 
            print $q->header(); 
            print "$DADA::Config::PROGRAM_NAME $DADA::Config::VER Authorization Denied.";
        }
}





sub default { 
	
	
	print(admin_template_header(
							-Title      => "Scheduled Mailings",
		                    -List       => $list,
		                    -Form       => 0,
		                    -Root_Login => $yeah_root_login)
		                    );
		                
		                
	print schedule_index();  
	
	print admin_template_footer(-Form    => 0, 
							-List    => $li->{list},
						    ); 
	
	
}




sub edit  { 
	
	
	
	if($q->param('role') =~ m/remove|delete/i){ 
		remove();
		exit; # DEV: Exit... wah?
	}
	
	
	my $key; 	
	my $message; 
	my $schedule_form; 
	
	
	if($q->param('process') eq 'true'){ 
		if($q->param('role') =~ m/test/i){ 
			$key = save();
			my $status = test_handler(-key => $key );
			if(! keys %$status){ 
				$message = '<p class="positive">Your test message has been sent.</p>';
			}else{ 
				$message  = '<p class="error">!!! Message not sent, details:</p>';
				$message .= '<p class="error">!!! '.  DADA::App::Guts::pretty($_)  .'</p>' foreach keys %$status;
			}
		}else{ 
			$message = $DADA::Config::GOOD_JOB_MESSAGE;
			$key = save();
			
		}
		$schedule_form = schedule_form($key, $message);
	} elsif($q->param('key')){ 
		$schedule_form = schedule_form($q->param('key'), $message);
	}else{ 
		$schedule_form =  schedule_form(undef,$message);
	}
	
	require CGI::Ajax;
	my $pjx  = new CGI::Ajax('external' => $DADA::Config::S_PROGRAM_URL);
	
	my $scrn = ''; 
	
	$scrn .= (admin_template_header(
  					    -HTML_Header => 0,
						-Title      => "Scheduled Mailings - Edit",
						-List       => $li->{list},
						-Form       => 0,
						-Root_Login => $yeah_root_login)
						);

	$scrn .= '<p><a href="' . $Plugin_Config->{Plugin_URL} . '">' . $Plugin_Config->{Program_Name} . '</a> &#187 Add/Edit</p>';


	$scrn .=  $schedule_form; 
	$scrn .=  admin_template_footer(-Form    => 0, 
						    -List    => $li->{list},
						); 
						
	print $pjx->build_html( $q, $scrn, {admin_header_params()});
						    
}




sub test_handler { 
	my %args = (-key => '', @_); 
	die "no key! $!" if ! $args{-key}; 
	my ($status, $cs) = $mss->send_scheduled_mailing(-key => $args{-key}, -test => 1);
	return $status; 
}




sub run_all_handler { 

	
	print(admin_template_header(
							-Title      => "Manually Running Schedules",
		                    -List       => $li->{list},
		                    -Form       => 0,
		                    -Root_Login => $yeah_root_login)
		                    );
	
	print '<p><a href="' . $Plugin_Config->{Plugin_URL} . '">' . $Plugin_Config->{Program_Name} . '</a> &#187 Manually Running Schedules</p>';
			                    
	print '<pre>';
	$verbose = 1;
	$run_list = $list;
	
	cl_main(); 
	print '</pre>';
	print '<p><a href="javascript:history.back()">Back...</a></p>';

	print admin_template_footer(-Form    => 0, 
							-List    => $li->{list},
						    ); 
}




sub save { 


	my %form_vals; 

	my $action = $q->param('action'); 
	
	 $form_vals{message_name}                 = $q->param('message_name'); 
	 $form_vals{active}         	          = $q->param('active')                  || 0; 
	 $form_vals{mailing_date}       		  = mailing_date(); 
	 $form_vals{repeat_mailing} 			  = $q->param('repeat_mailing')          || 0; 
	 $form_vals{repeat_times} 			      = $q->param('repeat_times')            || 0; 
	 $form_vals{repeat_number}        		  = $q->param('repeat_number'); 
	 $form_vals{repeat_label}        		  = $q->param('repeat_label'); 
	 $form_vals{only_send_to_list_owner}      = $q->param('only_send_to_list_owner') || 0; 
	 $form_vals{archive_mailings}        	  = $q->param('archive_mailings')        || 0; 

	my $tmp_record = {};
	if($q->param('key')){
		$tmp_record = $mss->get_record($q->param('key'));
	}
		 
	$form_vals{headers} = {};
	
	if(defined($q->param('Reply-To'))){
		$form_vals{headers}->{'Reply-To'}     = $q->param('Reply-To');
	}
	if(defined($q->param('Errors-To'))){
		$form_vals{headers}->{'Errors-To'}    = $q->param('Errors-To');
	}
	if(defined($q->param('Return-Path'))){
		$form_vals{headers}->{'Return-Path'}  = $q->param('Return-Path');
	}
	if(defined($q->param('X-Priority'))){
		$form_vals{headers}->{'X-Priority'}   = $q->param('X-Priority');	
	}
	if(defined($q->param('Precedence'))){
		$form_vals{headers}->{Precedence}     = $q->param('Precedence');
	}
	if(defined($q->param('Subject'))){	
		$form_vals{headers}->{Subject}        = $q->param('Subject');
	}
	
	if(keys %{$tmp_record->{headers}}){ 
		# That sure was ugly... 
		%{$form_vals{headers}} = (%{$tmp_record->{headers}}, %{$form_vals{headers}}); 
	}
	
	
	foreach my $t('PlainText', 'HTML'){ 
		$form_vals{$t.'_ver'}->{source}                    = $q->param($t.'_source'); 
		$form_vals{$t.'_ver'}->{text}                      = $q->param($t.'_text'); 
		$form_vals{$t.'_ver'}->{url}                       = $q->param($t.'_url');
		$form_vals{$t.'_ver'}->{file}                      = $q->param($t.'_file'); 
	    $form_vals{$t.'_ver'}->{use_email_template}        = $q->param($t.'_use_email_template')        || 0; 
	    $form_vals{$t.'_ver'}->{only_send_if_defined}      = $q->param($t.'_only_send_if_defined')      || 0; 
		$form_vals{$t.'_ver'}->{only_send_if_diff}         = $q->param($t.'_only_send_if_diff')         || 0;    
	    $form_vals{$t.'_ver'}->{grab_headers_from_message} = $q->param($t.'_grab_headers_from_message') || 0; 
		$form_vals{$t.'_ver'}->{text} =~ s/\r\n/\n/g;	   # I hate browsers.
		
		if($q->param('key')){
			%{$form_vals{$t.'_ver'}} = (%{$tmp_record->{$t.'_ver'}}, %{$form_vals{$t.'_ver'}}); # so as not to whipe out things like the checksums...	
		}else{ 
			$form_vals{last_schedule_run} = time;
		}
	}
	
	
	
	
	$form_vals{attachments} = []; 
	
	
	my $att_i = $q->param('num_attachments') || 0; 
	my $i = 0; 
	my $ditch_num; 
	
	
	if($action =~ m/Remove Attachment /){
		$ditch_num = $action; 
		$ditch_num =~ s/Remove Attachment //;
	}
	for($i = 0; $i <= $att_i; $i++){ 
		next if(defined($ditch_num) && ($ditch_num eq $i)); 
		
		my $attachment = {}; 
		$attachment->{attachment_filename}     = $q->param('attachment_filename_'.$i); 
	    $attachment->{attachment_disposition}  = $q->param('attachment_disposition_'.$i); 
	    $attachment->{attachment_mimetype}     = $q->param('attachment_mimetype_'.$i); 	
		
		push(@{$form_vals{attachments}}, $attachment) if $q->param('attachment_filename_'.$i); 

	} 
	
	# Subscriber Fields
	# First, let's figure out what they may be...
	
	require DADA::MailingList::Subscribers; 
	my $lh = DADA::MailingList::Subscribers->new({-list => $list}); 
	
	my $fields = [];  
	my $saved_pso = []; 
	
	
	$fields = $lh->subscriber_fields; 
	push(@$fields, 'email'); 
	foreach my $field(@$fields){ 
		if(defined($q->param('field_comparison_type_subscriber.' . $field))){ 
			
		
			push(@$saved_pso, {
				
								field_name 			  => $field,
								field_comparison_type => $q->param('field_comparison_type_subscriber.' . $field), 
								field_value           => $q->param('field_value_subscriber.' . $field),  
							}
			); 
			
		}
	}
		
#	require Data::Dumper; 
#	die Data::Dumper::Dumper(%form_vals); 
	
	$form_vals{partial_sending_params} = $saved_pso; 
	my $s_key = $q->param('key'); 			
	my $key = $mss->save_record(-key   => $s_key, 
								-mode  => 'append', 
					            -data  => \%form_vals); 
	return $key;  
}




sub remove { 


	my $key = $q->param('key'); 
	die "no key!" if ! $key; 
	$mss->remove_record($key);

	print $q->redirect(-uri => $Plugin_Config->{Plugin_URL}.'?message=r'); 	
}




sub mailing_date { 
	my $min = $q->param('mail_minute'); 
	my $hour = $q->param('mail_hour'); 
	my $mday = $q->param('mail_day'); 
	my $mon  = $q->param('mail_month'); 
	my $year = $q->param('mail_year'); 
	
	$hour += 12 if $q->param('mail_am_pm') eq 'pm'; 
	
	$min = int($min) ;
	require Time::Local; 
	my $time = Time::Local::timelocal(0,$min,$hour,$mday,$mon,$year);
			
	return $time; 
}




sub schedule_index { 
	
	
	my $r;
	   
    	$r .= '<p><a href="' . $Plugin_Config->{Plugin_URL} . '">' . $Plugin_Config->{Program_Name} . '</a></p>';
	
	
	   $r .= "<p class=error>Scheduled Mailing Removed.</p>" if $q->param('message') eq 'r'; 
	   $r .= q{ 
		<fieldset> 
		<legend>Your Schedules</legend>
		
		};
	   
	   
	   $r .= "<table style=\"border: 1px solid black;background:#fff\">\n <tr>"; 
	   $r .= qq{ 
	   	<tr  style="border-bottom:1px solid black"> 
	   		<td><p><strong>Name</strong></p></td>
	   		<td><p><b>Next Mailing</b></p></td>
	   		<td><p>&nbsp;</p></td>
	   	</tr>
	   
	   }; 
	   
	   
	foreach($mss->record_keys){ 
		$r .= schedule_row($_); 
	}
	   $r .= '</table>';
	   
	$r .= ($q->p({-align => 'right'}, '<div class="buttonfloat">', 
	
		  $q->startform(-action => $Plugin_Config->{Plugin_URL}, -method => 'GET'), 
		  $q->hidden('flavor', 'edit'), 
		  $q->submit(-class => 'processing', -value => "Add Scheduled Mailing... "), 
		  $q->endform() , '</div>' 
		  )); 
		  
$r .= '</fieldset>'; 


my $manually_run_tmpl = q{ 
	
	<fieldset> 
	 <legend>Manually Run <!-- tmpl_var Program_Name --></legend> 
			
	<form action="<!-- tmpl_var Plugin_URL -->">
	<input type="hidden" name="flavor" value="run_all" /> 	
	<div class="buttonfloat"> 
	<input type="submit" class="cautionary" value="Manually Run All Schedules" />
	</div> 
	<div class="floatclear"></div> 
	</form>

	<p>
	 <label for="cronjob_url">Manual Run URL:</label><br /> 
	<input type="text" class="full" id="cronjob_url" value="<!-- tmpl_var Plugin_URL -->?run=1&passcode=<!-- tmpl_var Manual_Run_Passcode -->" />
	</p>

	<!-- tmpl_if curl_location --> 
	<p> <label for="cronjob_command">curl command example (for a cronjob):</label><br /> 
	<input type="text" class="full" id="cronjob_command" value="<!-- tmpl_var curl_location -->  -s --get --data run=1\;passcode=<!-- tmpl_var Manual_Run_Passcode -->\;verbose=0  --url <!-- tmpl_var Plugin_URL -->" />
	<!-- /tmpl_if --> 

	<!-- tmpl_unless Allow_Manual_Run --> 
	    <span class="error">(Currently disabled)</a>
	<!-- /tmpl_unless --> 

	</p>
	</li>
	</ul> 
	</fieldset>
};

my $curl_location = `which curl`; 
   $curl_location = make_safer($curl_location);

$r .= DADA::Template::Widgets::screen(
		{
			-data => \$manually_run_tmpl, 
			-vars => { 
				Program_Name        => $Plugin_Config->{Program_Name},  
				Plugin_URL          => $Plugin_Config->{Plugin_URL}, 
				Manual_Run_Passcode => $Plugin_Config->{Manual_Run_Passcode}, 
				Allow_Manual_Run    => $Plugin_Config->{Allow_Manual_Run}, 
				curl_location       => $curl_location, 
				
			},
			
			
		}
	); 

	return $r; 
} 
	


sub schedule_row { 
	my $key = shift; 
	my $r; 
		
	my $record = $mss->get_record($key);	
	my $status = ""; 
	my $mailing_schedule = $mss->mailing_schedule($key);
		
	my $row_style = 'background-color:#CCFFCC';
	   $row_style = 'background-color:#FFFFFF' if (($record->{active} == 0) || (($mailing_schedule->[-1] < int(time))) && ($record->{repeat_number} ne 'indefinite') );	
	   
		 if($mailing_schedule->[-1] > int(time)){ 
		 	if($record->{active} == 0){ 
		 		$status =  $q->p({-class => 'smallred'},$q->i("This schedule is inactive.")); 
		 	}else{
		  		$status =  $q->p({-class => 'smallblack'},$q->i($mss->printable_date($mailing_schedule->[0]))); 
		 	}
		 }elsif($record->{repeat_number} eq 'indefinite'){ 
		  	$status =  $q->p({-class => 'smallblack'},$q->i($mss->printable_date($mailing_schedule->[0]))); 
		 }else{
			$status =  $q->p({-class => 'smallred'},$q->i('This scheduled mailing has ended.')); 
		 }  
		
	$r = "<tr style=\"$row_style\"><td><p><strong>" . edit_schedule_href($key, $record) . "</strong></p></td><td>$status</td><td>" . remove_schedule_form(-key   => $key, -label => '[x]') . "</td></tr>"; 
	return $r; 
}




sub edit_schedule_href { 

	my ($key, $record) = @_; 
	$record->{message_name} = 'unnamed scheduled mailing' if ! $record->{message_name};
	return $q->a({-href => $Plugin_Config->{Plugin_URL} . '?flavor=edit&key='.$key}, $record->{message_name}); 

} 




sub schedule_form { 

	my $key     = shift; 
	my $message = shift;; 
	my %form_vals; 



	
	
	if(!$key){ 
		%form_vals = %d_form_vals; 	
	}else{ 
		my $form_vals  = $mss->get_record($key); 
		%form_vals = %$form_vals; 
	}

	my $f; 
	$f .= $message; 
	
	
	$f .= q{

	<fieldset> 
 	<legend>Scheduling Options</legend>

	};
	
	
	$f .= $q->p({-class => "positive"}, 'Server time is: ' . $mss->printable_date(time));
	
		
	$f .= $q->start_form(); 
	$f .= (
		   $q->p($q->b('Scheduled Message Name:'), 
		   $q->textfield(
		   			     -name => 'message_name', 
		                 -value => $form_vals{message_name}, 
		                 -style => 'width: 300px',
		                ))
		   ); 	
	
	$f .= (
			$q->p(
			$q->checkbox(
						 -name    => 'active', 
						 -id      => 'active', 
						 -value   => 1, 
							  (($form_vals{active} == 1) ? 
							  (-checked => 'checked',) :
							  (-checked => '',)),
						 -label   => '',
						), 
					 $q->label({'for' => 'active'}, 'Active'))
		  );
		  
	
	
	$f .= date_widget(\%form_vals); 
	$f .= repeat_widget(\%form_vals, $key); 
	
$f .= q{

</fieldset> 

	
};


# Message Headers

require DADA::Template::Widgets; 
$f .= DADA::Template::Widgets::screen(
	{
		-screen                        => 'message_headers_fieldset_widget.tmpl', 
		-vars                          => 
			{ 
				priority_popup_menu    => DADA::Template::Widgets::priority_popup_menu($li,    $form_vals{headers}->{'X-Priority'}),
				precendence_popup_menu => DADA::Template::Widgets::precendence_popup_menu($li, $form_vals{headers}->{Precedence}),
				'Reply-To'             => $form_vals{headers}->{'Reply-To'}, 
				'Errors-To'			   => $form_vals{headers}->{'Errors-To'},
				'Return-Path'          => $form_vals{headers}->{'Return-Path'},
				Subject                => $form_vals{headers}->{Subject}, 
			},
		-list_settings_vars       => $li, # Uh, ok - $li is global. That's stupid. 
		-list_settings_vars_param => 
			{
					-dot_it => 1, 
			},
	}
); 

	   
$f .= q{	
	<fieldset> 
	<legend>Message Body</legend>
};

	
	$f .= message_widget(-type => 'PlainText', -form_vals => \%form_vals); 
	$f .= message_widget(-type => 'HTML', -form_vals => \%form_vals); 

$f.= q{ 

</fieldset> 
	
};

	$f .= attachment_widget(\%form_vals);

	
	
	$f .= qq{ 
		<fieldset> 
		<legend>
		       <a href="#" onclick="toggleDisplay('advanced_options');return false;">
		      +/- 
		     </a>Advanced Options
		</legend>
		
		<div id="advanced_options" style="display:none">
		
		
	};
	
	if($mss->can_archive){ 
	
	
	
	$f .= (
		$q->p(
		$q->checkbox(
					 -name    => 'archive_mailings', 
					 -value   => 1, 
						  (($form_vals{archive_mailings} == 1) ? 
						  (-checked => 'checked',) :

						  (-checked => '',)),
					 -label   => '',
					), 
				 'Archive Mailings')	 
		);    
	}
		
	$f .= (
		$q->p(
		$q->checkbox(
					 -name    => 'only_send_to_list_owner', 
					 -value   => 1, 
						  (($form_vals{only_send_to_list_owner} == 1) ? 
						  (-checked => 'checked',) :
						  (-checked => '',)),
					 -label   => '',
					), 
				 'Only send this schedule to the List Owner')	 
		);  

$f .= '			</div> </fieldset> '; 

require DADA::MailingList::Subscribers; 
my $lh = DADA::MailingList::Subscribers->new({-list => $list }); 


my $fields = [];  
# Extra, special one... 
push(@$fields, {name => 'subscriber.email'}); 
foreach my $field(@{$lh->subscriber_fields({-dotted => 1})}){ 
     push(@$fields, {name => $field});
 }

my $partial_saved = $form_vals{partial_sending_params};
my $edited_fields = []; 

foreach my $p_field(@$fields){ 
	foreach my $partial_saved_entry(@$partial_saved){ 
		# Did you catch that?
		if('subscriber.' . $partial_saved_entry->{field_name} eq $p_field->{name}){ 
			$p_field->{field_comparison_type} = $partial_saved_entry->{field_comparison_type};
			$p_field->{field_value}           = $partial_saved_entry->{field_value};
			 
		}
	}
	
	push(@$edited_fields, $p_field); 

}



require DADA::Template::Widgets; 
$f .= DADA::Template::Widgets::screen({
	-screen => 'partial_sending_options_widget.tmpl',
	
	
-vars => { 	                                                                  
    fields                           => $edited_fields,
    can_have_subscriber_fields       => $lh->can_have_subscriber_fields, 
    
}, 

}); 






$f .= $q->hidden('key', $key);	
$f .= $q->hidden('process', 'true');
$f .= $q->hidden('flavor', 'edit');	 

$f .= submit_widget($key); 

$f .= $q->end_form(); 


$f .= $q->p('&nbsp;') . $q->p($q->a({-href => $Plugin_Config->{Plugin_URL}}, '<- Schedule Index...')); 	

if($q->param('debug')){ 
	require Data::Dumper; 
	$f .= $q->hr . '<pre>' . Data::Dumper::Dumper(%form_vals) . '</pre>'; 
}





	return $f; 
    	
}





sub remove_schedule_form { 
	my %args = (-key => undef, 
				-label => 'remove', 
				@_); 
	die "no key!" if ! $args{-key}; 

	my $r = ( $q->start_form(-action => $Plugin_Config->{Plugin_URL}) .
			  $q->hidden('key', $args{-key}) .
			  $q->hidden('flavor', 'remove') .
			  $q->submit(-value => $args{-label}, -class => 'alertive') . 
			  $q->end_form()
			); 
			
	return $r; 

}




sub date_widget { 

	#my %args = (-date => time, @_); 
	
	my $form_vals = shift; 
	
	my $date = $form_vals->{mailing_date};		

	#  0    1    2     3     4    5     6     7     8
	my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($date);

	
	my $default_mail_day = $mday; #20; 
	my $default_mail_month = $mon; #3; 
	my $default_mail_year = $year; #103; 
	my $default_mail_hour = $hour; #6; 
	my $default_mail_minute = $min; #31; 
	my $default_mail_am_pm = 'am'; 
	
	if($default_mail_hour > 12){ 
		$default_mail_hour -= 12 ;
		$default_mail_am_pm = 'pm'; 
	}

	my $r = '<div style="width:550px">';
	
	$r .= ( 
		
		
		$q->p('Schedule mailing for: ', 
		$q->popup_menu(
					-name     => 'mail_month', 
				   -labels  => {%mail_month_values}, 
				    '-values'   => [sort keys  %mail_month_values],
				    -default  => $default_mail_month,
				   ), ' ', 
        $q->popup_menu(
					-name     => 'mail_day', 
				   '-values'  => [1..31], 
				    -default  => $default_mail_day,
				    -labels   => {%mail_day_values}
				   ), ' ', 
        $q->popup_menu(
					-name     => 'mail_year', 
				   '-values'  => [sort keys %mail_year_values], 
				    -default  => $default_mail_year, 
				    -labels  => {%mail_year_values}, 
				   ), ' - ', 
       $q->popup_menu(
					-name     => 'mail_hour', 
				   '-values'  => [@hours], 
				    -labels   => {%hours_labels},
				    -default  => $default_mail_hour,
				   ), ':', 				   						   
       $q->popup_menu(
					-name     => 'mail_minute', 
				   '-values'  => [@minutes], 
				   -labels    => {%mail_minute_values}, 
				    -default  => $default_mail_minute,
				   ),
       $q->popup_menu(
					-name     => 'mail_am_pm', 
				   '-values'  => ['am', 'pm'], 
				    -default  => $default_mail_am_pm,
				   ),				   				   
				   ), 
		
  	  ); 
		$r .= '</div>'; 

	return $r; 
}




sub repeat_widget { 

	my $form_vals = shift;
	my $key        = shift; 
	
	my $r = ($q->p(
			$q->checkbox(
						-name    => 'repeat_mailing', 
						-id      => 'repeat_mailing',

						(($form_vals->{repeat_mailing} == 1) ? 
							  (-checked => 'checked',) :
							  (-checked => '',)),
							  
						-value => 1, 	  
							  
						-label => ''
					 ), 
			$q->label({'for' => 'repeat_mailing'},' Repeat') , ' schedule every: ', 
			
			$q->popup_menu(
							-name     => 'repeat_times', 
						   '-values'  => [1..100], 
							-default  => $form_vals->{repeat_times},
							
							
			   ),	
			
			$q->popup_menu(
							-name     => 'repeat_label', 
						   '-values'  => ['minutes', 'hours', 'days', 'years'], 
						   -labels    => {%repeat_label_values},
							-default  => $form_vals->{repeat_label},
			   			  ), ' ',			 	  		
			
			$q->popup_menu(
							-name     => 'repeat_number', 
						   '-values'  => ['indefinite', 1..100,], 
						  
							-default  => $form_vals->{repeat_number},
			   ), 'time(s)',
			   
	));
	
	
	
	
	if($form_vals->{last_mailing}){ 
		$r .= $q->p({-class => 'positive'},$q->i('Last mailing was on: ' . $mss->printable_date($form_vals->{last_mailing}))); 
	}
	
	if($key){ 
		my $mailing_schedule = $mss->mailing_schedule($key);
		if(
			(($mailing_schedule->[-1] > int(time)) || ($form_vals->{repeat_number} eq 'indefinite')) &&
			($form_vals->{repeat_mailing} == 1)   &&
			($form_vals->{active}         == 1)  
		   ){ 
			 
			$r .= $q->p({-class => 'positive'},
				   $q->i("Next mailing will be on: " . $mss->printable_date($mailing_schedule->[0]))); 
		}
		
		
		if(($mailing_schedule->[-1] < int(time)) && (($form_vals->{repeat_number} ne 'indefinite')) ){ 
			$r .= $q->p({-class => 'smallred'},$q->i('This scheduled mailing has ended.')); 
		}
	
	}	
	
	return $r;

} 




sub message_widget { 

	my %args = (-type      => 'PlainText', 
				-form_vals => {}, 
				@_); 
	
	my $type = $args{-type}; 
	my %form_vals;
	my $form_vals = $args{-form_vals}; 
	%form_vals = %$form_vals; 
	
	
	my $r; 
	my $display = ""; 
	if($type !~ m/plain/i){ 
		$display = 'none;';  
	}
	
	
	$r .= '<fieldset style="background:#e6e6e6">';
$r .= qq { 	
	 <legend>
      <a href="#" onclick="toggleDisplay('$type\_message');return false;">+   /-</a> $type Version
     </legend>
 <div id="$type\_message" style="display:$display">
};





	
$r .= '<table width="90%">'; 
		
	$r .=  	from_text_widget($type, $form_vals);
		 
	$r .=  from_url_widget($type, $form_vals);
	
	$r .=  from_file_widget($type, $form_vals);

$r .= qq{		 
	
	</table> 
	
	<p>
	  <strong>
	   <a href="#" onclick="toggleDisplay('$type\_advanced_options');return false;">
		+/- Advanced Options...
	   </a>
	  </strong>
	
	
	<div id="$type\_advanced_options" style="display:none">
	
	<table width="90%">
	
};
	
	

	
	$r .=  	use_email_template_widget($type, $form_vals);
		
	$r .=  grab_headers_from_message_widget($type, $form_vals);
	
	$r .=  only_send_if_defined_widget($type, $form_vals);
	
	$r .=  only_send_if_different_widget($type, $form_vals);			

	$r .= qq{ 
	
	</table> 
	</div> 
		
	};

		$r .= "</div></fieldset>";
				
	return $r; 
			
}

sub from_text_widget { 

	my $type      = shift; 
	my $form_vals = shift; 
	my %form_vals = %$form_vals; 	
	my $r; 
	my $Default_Text = DADA::App::Guts::js_enc($form_vals{$type.'_ver'}->{text}); 
	
	$r = (

		  $q->Tr(
		  $q->td([
		 	(
		  
		  $q->p(
		  $q->radio_group(-name => "$type\_source", 
						  -value => 'from_text', 
						  							
							   (($form_vals{$type.'_ver'}->{source} eq 'from_text') ? 
							   (-default => 'from_text',) :
							   (-default => '-',)),
							   
						  -labels => {'from_text' => ''},
									
					   ))
		  ), 
		  ($q->p('Use the below message:')
		  )
		  ]) #td
		  ).  #tr
		  $q->Tr(
		  $q->td([
		  (
		  $q->p('&nbsp;'), 
		  ), 
		  (
		  	
		  	$q->p(
		  	
		  	(
			$type eq 'HTML'              && 
			$DADA::Config::FCKEDITOR_URL && 
			$li->{enable_fckeditor} == 1
			) ? (
		
		
		  		qq{ 
		  		
		  		<!-- DEV: Hack! --> 
		
		  		<script type="text/javascript">
					<!-- 
                 	var oFCKeditor = new FCKeditor( '$type\_text' ) ;
	                oFCKeditor.BasePath = "$DADA::Config::FCKEDITOR_URL/"
	                oFCKeditor.Config['FullPage'] = true;
	                oFCKeditor.Value = '$Default_Text';
	                oFCKeditor.Height = 400;
					oFCKeditor.Create() ;
	                //-->
                </script> 
                
                  }

		  	
		  	
		  	)
		  	
		  	: (
		  	
		  	
			$q->textarea(-name  => "$type\_text",
						 -cols  => 70, 
						 -rows  => 15, 
						 -wrap  => 'NONE',
						 -value => $form_vals{$type.'_ver'}->{text})
			
			
			)
			
			
			    )#p
			)
			]) #td
			)  #tr
		); 
		
	return $r; 
	
}




sub from_url_widget {
 
	my $type      = shift; 
	my $form_vals = shift; 
	my %form_vals = %$form_vals; 	
	my $r; 
	
	
	eval { 
		require LWP::Simple; 
	};
	
	return '' if @$; 
	
	$r = (

		   $q->Tr(
		   $q->td([ 
		   
		   (
		   $q->p(
		   $q->radio_group(
						  -name  => "$type\_source", 
						  -value => 'from_url',
						  
							   
							   (($form_vals{$type.'_ver'}->{source} eq 'from_url') ? 
							   (-default => 'from_url',) :
							   (-default => '-',)),
						  
						  -labels => {'from_url' => ''}),
			) # p
			), 
			(
			$q->p('Fetch message from this URL:'), 
			), 
			]), #td
			) .  #tr
			$q->Tr(
			$q->td([
			(
			$q->p('&nbsp;')
			), 
			(
			
			$q->p(
			$q->textfield(
					-name => "$type\_url", 
					-value => $form_vals{$type.'_ver'}->{url},
					-class => 'full', 
				   )  
			   . ' ' . 
				
				(
				 ($form_vals{$type.'_ver'}->{url}) ? 
				 ($q->a({-href => $form_vals{$type.'_ver'}->{url}, -target =>'new'}, ' View....')) : 
				 ()
				 ) . 
				  
				
				url_test($form_vals{$type.'_ver'}->{url}))
				
				
				
			 ), 
			 ]) #td
			 ) #tr
			 
			 ); 
	return $r; 

} 



sub from_file_widget { 

	my $type      = shift; 
	my $form_vals = shift; 
	my %form_vals = %$form_vals; 	
	my $r; 
	
	$r = (
		$q->Tr(
		$q->td([
		(
			$q->p(
		    $q->radio_group(-name  => "$type\_source", 
					        -value => 'from_file', 		   
						   (($form_vals{$type.'_ver'}->{source} eq 'from_file') ? 
						   (-default => 'from_file',) :
						   (-default => '-',)),			   
						   -labels => {'from_file' => ''}), 
			)
		), 
		(	           
	 		$q->p('Fetch message from this file:')
		), 
		]), #td
		 ) #tr
		 
		 
		 . 
		 
		$q->Tr(
		$q->td([
		(
			$q->p('&nbsp;')
		), 
		(
			$q->p($q->textfield(-name  => "$type\_file", 
							    -value => $form_vals{$type.'_ver'}->{file},
							    -class => 'full',
							   ), 
			file_test($form_vals{$type.'_ver'}->{file}),)
		) 
		]) #td
		)); #tr

	return $r; 
	
} 


sub use_email_template_widget { 

	my $type      = shift; 
	my $form_vals = shift; 
	my %form_vals = %$form_vals; 	
	my $r; 
	
	$r = ($q->Tr(
		  $q->td([
		  (
		  	$q->p(
		  	$q->checkbox(-name => $type .'_use_email_template', -value => 1, -id => $type .'_use_email_template',
		  	
		  	(
		  	 ($form_vals{$type.'_ver'}->{use_email_template} ==1) ? 
		  	 (-checked => 'checked',) :
		  	 (-checked => '')
		  	), 
		  	
		  	-label => '')
		  	)
		  ), 
		  (
		  $q->label({for => $type .'_use_email_template'}, 'Apply the ' . $type . ' Email template to this message')
		  )
		  ])
		  )
		  );
	return $r; 
		
} 


sub grab_headers_from_message_widget { 
	my $type      = shift; 
	my $form_vals = shift; 
	my %form_vals = %$form_vals; 
	
	
	my $r; 



	$r = (
  		  $q->Tr(
		  $q->td([
		  (
		  	$q->p(
		  	$q->checkbox(-name => $type .'_grab_headers_from_message', -value => 1, id => $type .'_grab_headers_from_message', 
		  	
		  	(
		  	 ($form_vals{$type.'_ver'}->{grab_headers_from_message} ==1) ? 
		  	 (-checked => 'checked',) :
		  	 (-checked => '')
		  	), 
		  	
		  	-label => '')
		  	)
		  ), 
		  (
		  $q->label({for => $type .'_grab_headers_from_message'}, 'Grab headers from  ' . $type . ' message')
		  )
		  ])
		  )
	);
	
	return $r; 
	
		  


} 





sub only_send_if_defined_widget { 

	my $type      = shift; 
	my $form_vals = shift; 
	my %form_vals = %$form_vals; 
	
	
	my $r; 
	

	$r = (
		$q->Tr(
		$q->td([
		
		(
			$q->p(
		  	$q->checkbox(
		  				-name => $type .'_only_send_if_defined', -value => 1, -id => $type .'_only_send_if_defined', 
		  	
						(
						 ($form_vals{$type.'_ver'}->{only_send_if_defined} ==1) ? 
						 (-checked => 'checked',) :
						 (-checked => '')
						), 
						
						-label => '')
						)
		  ), 
		  (
		  	$q->label({for => $type .'_only_send_if_defined'}, 'Only send schedule if ' . $type . ' message has data')
		  )
		  
		  ])	#td
		  )		#Tr
		  ); 
		  
	return $r; 
}




sub only_send_if_different_widget { 
	my $type      = shift; 
	my $form_vals = shift; 
	
	eval { 
		require Digest::MD5; 
	};
	
	return '' if @$; 
	 
	my %form_vals = %$form_vals; 
	my $r; 
	
	$r = ($q->Tr(
		  $q->td([
				  (
					$q->p(
					$q->checkbox(
								 -name  => $type .'_only_send_if_diff', 
								 -value => 1, 
								 -id    => $type .'_only_send_if_diff', 
					
								(
								 ($form_vals{$type.'_ver'}->{only_send_if_diff} == 1) ? 
								 (-checked => 'checked',) :
								 (-checked => '')
								), 
								
								-label => '')
								)
				  ), 
				  (
				  $q->label({for => $type .'_only_send_if_diff'}, 'Only send schedule if ' . $type . ' message is different from last mailing')
				  )
				  ]) # td
				  )  # Tr
				  );
		return $r; 
		

} 


sub attachment_widget { 
	
	my $form_vals = shift; 
	
	my $r; 
	my $attachments = $form_vals->{attachments}; 	
	my $num = 0; 
	
	
$r .= q{ 
	<fieldset> 

	<legend>
	       <a href="#" onclick="toggleDisplay('file_attachments');return false;">
	      +/- 
	     </a>File Attachments
	</legend>
	
	<div id="file_attachments" style="display:none">
	

};

		foreach my $att(@$attachments){
			$r .= single_attachment_widget($att, $num);
			$num++; 
		}
	$r .= $q->hr;
	$r .= $q->p($q->b('Add an attachment:')); 
	$r .= single_attachment_widget({}, $num); 
	$r .= $q->hidden(-name => 'num_attachments', -value => $num, -force => 1); 

	$r .= q{ </div></fieldset> };
	
}




sub single_attachment_widget { 

	my $att = shift || {}; 
	my $num = shift; 
	my $r; 
	
	$r .= $q->table(
	      $q->Tr(
	      $q->td([
	      ( 
	        $q->p('Filename:')
	      ),
	      (
	      
	      $q->p($q->textfield(
	                           -name  => 'attachment_filename_'.$num, 
				               -value => $att->{attachment_filename}, 
				               -force => 1, ), 
				               
				               file_test($att->{attachment_filename})
				 )
		   ),
		   ]) # td
		   ), #tr
		   $q->Tr(
		   $q->td([
		    
		   (
		   $q->p('Disposition:'),
		   ), 
		   (
		   $q->p(
		   $q->popup_menu(
							-name => 'attachment_disposition_'.$num, 
						   '-values' => ['attachment', 'inline'], 
							-default => $att->{attachment_disposition},
							-force => 1, )
			)
			), 
			]) #td
			), #Tr
			$q->Tr(
			$q->td([
			(
	         $q->p('MIME type'),
	         ), 
	         (
	         $q->p(
	               $q->popup_menu(-name    => 'attachment_mimetype_'.$num, 
	                             '-values' => ['find automatically', sort values %DADA::Config::MIME_TYPES], 
	                              -default => $att->{attachment_mimetype}, 
	                              -force   => 1, )
	              )
	          )
	          ]) #td
	          ), #tr
	          $q->Tr(
	          $q->td([
	          		(
	          		$q->p('&nbsp;') 
	          		), 
	                 (
	           
	           
	           (
	           (keys %$att) ? 
	       	   ($q->p($q->submit(-name => 'action', -value => 'Remove Attachment ' .$num, -class => 'alertive'))) : 
	       	   ($q->p($q->submit(-name => 'action', -value => 'Add Attachment',           -class => 'processing')))
	       	   )
	           
	           ) 
	           ]) 
	           )
	           );
	       				 
            
	return $r; 

}




sub submit_widget { 
	my $key = shift || undef; 
	
	my $r; 
	   $r .= $q->table({-align => 'right'}, 
			 $q->Tr(
			 $q->td([
			 (
			 $q->submit(-name  => 'role', 
						-value => 'Send Test Mailing to List Owner', 
						-class => 'cautionary', 
					   ), 
			 ), 
			 (
			 $q->submit(-name  => 'role', 
						-value => 'Save Schedule', 
						-class => 'processing'
					   ),
			 ), 
			  (
			 
			 (($key) ? 
			 (
			 $q->submit(-name  => 'role', 
						-value => 'Remove Schedule', 
						-class => 'alertive'
					   ),
			 
			 ) : ())
			 
			 
			 ),
			 ])
			 )
			 );  	   		  
	return $r; 
} 





sub file_test { 
	my $fn = shift || undef; 
	my $r; 

	return '' if ! $fn; 
	
	$r .= $q->p({-class => 'error'}, 'Can\'t read file!') unless -r $fn; 
	$r .= $q->p({-class => 'error'}, 'File doesn\'t exist!') unless -e $fn; 	
	
	return $r; 
}




sub url_test { 
	my $url = shift; 
	my $r = ''; 
	require LWP::Simple; 
	return $r if ! $url; 
	if(!LWP::Simple::get($url)){ 
		$r .= $q->p({-class => 'smallred'}, $q->i('Error fetching URL!'));
	} 
	return $r; 
	
}




sub show_help { 
	print q{ 

arguments: 
-----------------------------------------------------------
--run			

--list			   a list's shortname
--help                 		
--verbose
--test 
--log              filename
--version
-----------------------------------------------------------
Example: To run the schedules for "mylistshortname": 

	./schedule_mailings.pl --run --list mylistshortname


-----------------------------------------------------------
for instructions, try:

pod2text ./schedule_mailings.pl | less

-----------------------------------------------------------

};
	exit; 
}




sub version { 

	#heh, subversion, wild. 
	print $Plugin_Config->{Program_Name} . " Version: $App_Version\n"; 
	print "$DADA::Config::PROGRAM_NAME Version: $DADA::Config::VER\n\n"; 
	exit; 
	
} 




sub logit { 
	my $action = shift; 
	open_log($Plugin_Config->{Log});
	log_action($action); 
	&close_log;
}




sub open_log { 
	my $log = shift; 	
	if($log){ 
		open(LOG, ">>$log") or warn "Can't open scheduler log! $!"; 
		chmod($DADA::Config::FILE_CHMOD, 	$log) 
			if $log; 
		$Have_Log = 1; 
		return 1; 
	}
}




sub log_action { 

	my ($action) = @_; 

	if($Have_Log){ 
		print LOG $action;
	} 
	
}




sub close_log{ 
	if($Have_Log){ 
		close(LOG); 
	}
}

=pod

=head1 NAME

Beatitude - A Scheduled Mailer for Dada Mail 

=for html <p align="center"><img src="images/beatitude-overview.jpg"  style="border: 1px solid black" /></p>


=head1 Description

Beatitude is a plugin for Dada Mail that allows you to compose 
email messages to be scheduled for sending in the future. 

Highly configurable, messages themselves can be in PlainText, HTML
or multipart/alternative and have an unlimited number of attachments.
The message itself can be composed in the plugin itself, fetched from a 
file or from a webpage. 

The schedules themselves can be anywhere from one minute to years 
into the future and can be repeated infinitely. Schedules also have many
safegaurds to help send only new content; for example: if a scheduled mailing 
is created to send the contents of a URL once a day, it will only send that
URL if the contents are different from the previous day.

=head1 REQUIREMENTS

It's best to get all these requirements in order before you begin. 

=over

=item * Dada Mail 2.8.10

Beatitude will not work in any version previous.

=item * Ability to set Cron Jobs. 

You need to be able to set scheduled tasks, or cron jobs to use Beatitude. If you don't know if you do have this feature available
you, ask your sys admin. 

=item * The Storable Module

If you have perl 5.8, this should already be installed. If it's not, you can grab it here: 

	http://search.cpan.org/~ams/Storable-2.08/

and install it. 

=item * A Command Line

It really helps if you have ssh or telnet access to your hosting
account. Testing would be much easier. 

=back

=head2 Installation Difficulty: moderate

=head1 Configuration

There's a few things you need to configure in this script, they're all
at the top. 

=over

=item * Change the lib path

You'll need to explicitly state where both the:


=over

=item * Absolute Path to the site-wide Perl libraries

=item * Absolute Path of the local Dada Mail libraries

=back

This is because this script will be running in a different environment and from a different location than what you'd run it as, when you visit it in a web-browser. It's annoying, but one of the things you have to do when running a command line script via a cronjob. 

As an example: C<use lib qw()> lines probably look like: 


 use lib qw(
 /home/myaccount/www/cgi-bin/dada
 /home/myaccount/www/cgi-bin/dada/DADA/perllib
  
 /usr/local/lib/perl5/site_perl/5.8.0/mach
 /usr/local/lib/perl5/site_perl/5.8.0
 /usr/local/lib/perl5/site_perl
 /usr/local/lib/perl5/5.8.0/BSDPAN
 /usr/local/lib/perl5/5.8.0/mach
 /usr/local/lib/perl5/5.8.0
 );

The first few lines: 

 /home/myaccount/www/cgi-bin/dada
 /home/myaccount/www/cgi-bin/dada/DADA/perllib
 
Are the B<Absolute Path of the local Dada Mail libraries>.

B</home/myaccount/www/cgi-bin/dada> is the absolute path to where the, I<dada> directory in your cgi-bin (as it may be obvious). 

B</home/myaccount/www/cgi-bin/dada/DADA/perllib> is a directory B<inside> your dada directory. Most like if you can find that first line (), just tack on, "/DADA/perllib" and you've got the second line. 


If you don't know where your site-wide Perl libraries are, try running this via the command line:

 perl -e 'print $_ ."\n" foreach @INC'; 

If you do not know how to run the above command, visit your Dada Mail in a web browser, log into your list and on the left hand menu and: click, B<About Dada Mail> 

Under B<Script Information>, click the, B<More...> link and under the, B<Perl Library Locations>, select each point that begins with a, "/" and use those as your site-wide path to your perl libraries. 


=back

As far as required changes, that's it. We'll get to interesting
optional things further down the line. 

=head1 Installation

=head2 Setting up the script.

Beatitude is B<both> a command line script and a CGI script. 

More specifically, as a CGI script, it's a Dada Mail plugin. To
install the script, it's best to put the scheduled_mailings.pl file into 
the B<plugins> directory inside the B<dada> directory, which is itself
in your B<cgi-bin>. If that directory doesn't exist, it's safe to make it. 

Once the scheduled_mailings.pl script is inside that directory, 
change it's permissions to, B<755> and view it in your web browser.

If the installation was done correctly, You'll most likely see a screen 
that asks you to log into your list. 

Add this entry to the $ADMIN_MENU Config.pm array ref:

 {
 -Title          => 'Scheduled Mailings', 
 -Title_URL      => $PLUGIN_URL."/scheduled_mailings.pl",
 -Function       => 'scheduled_mailings',
 -Activated      => 1, 
 },

It's possible that this has already been added to $ADMIN_MENU and all
you would need to do is uncomment this entry. This adds a menu item for Beatitude
in Dada Mail's List Control Panel. 

=head2 Setting up Beatitude to check schedules to run. 

Since this program is also command line tool, you can execute it via a
command line. Running Beatitude with the B<--run> argument will 
have it check if any schedules should be run, and mail messages that need 
to be mailed. 

 prompt>./scheduled_mailings.pl

I suggest before you do that, you test the scheduled_mailings.pl script.

=head2 Testing

You can pass the B<--test> argument to scheduled_mailings.pl to make
sure everything is workings as it should. The B<--test> argument does not
take any arguments. If everything is set up correctly, you'll get back a verbose
message of the going's on of the script: 

 prompt>./scheduled_mailings.pl --test
 
 ------------------------------------------------------------------------
 Running Schedule For: mytestlist
 Current time is: June 26th 2003 - 5:25 pm
     No schedules to run.
 ------------------------------------------------------------------------

In this example, Beatitude checked schedules to be run for the 'mytestlist' list, 
found none, and exited. If there is a schedule to run, the output my look like this: 

 ------------------------------------------------------------------------
 Running Schedule For: mytestlist
 Current time is: June 26th 2003 - 5:33 pm
 
     Examining Schedule: 'Justin's Test Schedule'
     'Justin's Test Schedule' is active -  
         Schedule last checked:     June 26th 2003 - 5:31 pm
         Next mailing should be on: June 26th 2003 - 5:33 pm
             'Justin's Test Schedule' scheduled to run now! 
 ------------------------------------------------------------------------

=head1 Setting up the Cron/Scheduled Job

You want to set this script to execute via a con
or scheduled, job. Here's what a theoretical cron tab for this script
may look like: 

 0,30 * * * * /usr/bin/perl /home/myaccount/www/cgi-bin/dada/plugins/scheduled_mailings.pl >/dev/null 2>&1

This will run the script every half hour. You can run this script
as often as you want, just be logical. I wouldn't run this script every five
minutes, that's a bit overkill.

Different hosts may have a control panel to set up crontabs, my host
gives me the pleasure of the B<contrab> command. I type in: 

 prompt> crontab -e

and am launched into my favorite text editor to type in the crontab. 

=head1 Optional Fun Things

There's a slew of optional arguments you can give to this script: 

=over

=item * --verbose
  
  prompt>./scheduled_mailings.pl --verbose

passing the --verbose parameter is like giving this script some 
coffee.  Similar to what you'd see if you ran the script using: 

 prompt>./scheduled_mailings.pl --test
 
But the scheduled mailings will actually get sent. 


=item * --log

If you pass a filename to the script it'll write a log entry that 
will look the same as what's outputted when you run with the 
B<--verbose> flag. 

If you don't want to pass the log each time, you can set a log in the
B<$Plugin_Config->{Log}> variable.

=item * --version

 prompt>./scheduled_mailings.pl --version

WIll print out both the version of Beatitude and also of Dada Mail. 
Good for debugging. Looks like this: 

 Beatitude version: .1
 Dada Mail version: 2.8.8

=item * --list
 
 ./scheduled_mailings.pl --list myslistshortname

If you want to run schedules for only one list, you can pass the B<--list> 
argument to scheduled_mailings.pl with a listshortname as its value. 

=back

=head1 Debugging

Beatitude can be a bit difficult to set up, if you've never set up a similar script before. Here's a few things I do, to make sure a Beatitude install is working correctly: 

First off, I install Beatitude, using the direction above. 

Then, I'll make a test list, so if something goes wrong, no one will be affected. I won't add any subscribers, since it won't be necessary. Any test messages I send out will go to the list owner (me). 

I'll then create a new schedule to send a message, every minute and repeat that schedule, indefinitely.

Then, I'll connect to the server via ssh, and run the command to run Beatitude, exactly as I would type the command in the crontab - except perhaps I'll put that --verbose flag on there, so I can see what's happening.

It takes a minute for the first message to be sent, and after that, every minute, if I run the command, I'll get a mailing. This will let me know that the schedules are firing correctly, and that I have the correct command to run Beatitude. 

If that's working, I'll set the cronjob - and have it run every five minutes or so. I'll get some coffee.

I'll come back and if I have a few messages that I didn't send, I'll know the cronjob did its job.

=head1 FAQs

=over

=item * I keep getting, 'permission denied' errors, what's wrong?

It's very possible that Beatitude can't read your subscription database or the list settings database. This is because Dada Mail may be running under the webserver's username, usually, B<nobody>, and not what Mystery Girl is running under, usually your account username. 

You'll need to do a few things: 

=over

=item * Change the permissions of the list subscription and settings databases

You'll most likely need to change the permissions of these files to, '777'. PlainText subscription databases have the format of B<listshortname.list> and are usually located where you set the B<$FILES> Config file variable. .List settings Databases have the format of B<mj-listshortname> and are usually located in the same location.

=item * Change the $FILE_CHMOD variable

So you don't need to change the permissions of the list files for every new list you create, set the $FILE_CMOD Config variable to 0777:
	
	$FILE_CHMOD = 0777; 
	
Notice there are no quotes around 0777. 

=back


=item * I found a bug in this program, what do I do? 

Report it to the bug tracker: 

http://sourceforge.net/tracker/?group_id=13002&atid=113002

=item * What's up with the name, Beatitude?

B<Beatitude>, in a historical context, refers to one of the eight sayings the Christian prophet, Jesus is believed to have said on the Sermon on the Mount. Each saying starts with, "Blessed are the..." - a similar saying over and over again, much like sending a similar message again and again

It also means, "a state of supreme happiness". 

But to me, I think of B<Beatitude> as almost a shorthand for, B<Beat Attitude>; the Beat Generation being a group of writers whose inner circle included Jack 
Kerouac, Allen Ginsberg, William Bourroughs, Gregory Corso and many more. 

I<To Kerouac, "Beat" -- a shorthand term for "beatitude" and the idea that the downtrodden are saintly -- was not about politics but about spirituality and art.>
-B<Douglas Brinkley>

A modern Beatitude would be Ginsberg's I<Please Master>, found in the book, B<Fall of America>.

The Beat Generation attempted to I<communicate> the thoughts, ideas and
adventures of their normal lives. I can only hope that communication with my B<Beatitude> will be a tenth that incredible. 

My personal bookshelf is overflowing with books from the Beat Generation authors
and one reason I moved to Boulder, CO, and then Denver, CO was because of the  Denver adventures of Sal Paradise and Dean Moriarty in I<On The Road> and to be close to the B<Jack Kerouac School of Disembodied Poets> in Boulder, CO. 

=back

=head1 History

=over

=item * .6 8/25/04

Very very small tweak to the log file routine - log file is chmod'd to the $FILE_CHMOD

=item * .5 12/27/03 

Archiving has been put in place, but only works if you have MIME-Tools installed. Given the nature of how messages are made, it's far easier to just rip apart a message already made, than try to remake it. 

Now that archiving works, Beatitude should play well with the Clickthru tracker. Grr!

minor bugs fixed in regards of erroneous messages saying that the schedule was ended if the mailing schedule was indefinite. 

=item * .4 - 12/07/03

There was a slight issue with repeating schedules: 

Repeated schedules would not get sent if the schedule had never been checked before. Although a minor bug, it is rather annoying. 

This issue should be fixed. 




=item * .3 - 11/23/03

Minor tweaks.

=item * .2 - 7/2/03

I noticed that the HTML admin template was printed twice when you send a 
test mailing in some instances. This has been fixed. 

There was a bug in the supporting libraries that affects repeating schedules. It's advised that if you run repeating schedules, that you upgrade to vesion 2.8.9 of Dada Mail. 


=item * .1 - 6/26/03

Initial Release for Mojo Mail, 2.8.8

=back

=head1 To Do

Look into ways to couple Beatitude with such apps as Blogger and Movable Type. (and if anyone who uses that sounds interested, let me know...)

=head1 COPYRIGHT

Copyright (c) 1999 - 2008 Justin Simoni 
http://justinsimoni.com 
All rights reserved. 

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.

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.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

=cut
