[freeside-commits] branch master updated. 3185fe4edea62dd3fa9818cf80902e96fe2a2d21

Ivan ivan at 420.am
Thu Sep 27 20:28:07 PDT 2012


The branch, master has been updated
       via  3185fe4edea62dd3fa9818cf80902e96fe2a2d21 (commit)
       via  f50a821d306b561d602edbdac0dac958b862ec0c (commit)
       via  10370b0320f56af88c90e572644e91332815100f (commit)
      from  39533c66139210655fc47404a17fd4e9b9ca8a00 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
commit 3185fe4edea62dd3fa9818cf80902e96fe2a2d21
Merge: f50a821 39533c6
Author: Ivan Kohler <ivan at freeside.biz>
Date:   Thu Sep 27 20:27:43 2012 -0700

    Merge branch 'master' of git.freeside.biz:/home/git/freeside
    
    Conflicts:
    	FS/FS/cust_main/Billing.pm

diff --cc FS/FS/Misc.pm
index 2be9ec2,2be9ec2..297e39f
--- a/FS/FS/Misc.pm
+++ b/FS/FS/Misc.pm
@@@ -913,16 -913,16 +913,6 @@@ sub ocr_image 
    @lines;
  }
  
--=item spool_formats
--  
--Returns a list of the invoice spool formats.
--
--=cut
--
--sub spool_formats {
--  qw(default oneline billco bridgestone)
--}
--
  =back
  
  =head1 BUGS
diff --cc FS/FS/TicketSystem/RT_Internal.pm
index b09647e,b09647e..01e2e29
--- a/FS/FS/TicketSystem/RT_Internal.pm
+++ b/FS/FS/TicketSystem/RT_Internal.pm
@@@ -50,7 -50,7 +50,7 @@@ sub access_right 
  sub session {
    my( $self, $session ) = @_;
  
--  if ( $session && $session->{'Current_User'} ) { # does this even work?
++  if ( $session && $session->{'CurrentUser'} ) { # does this even work?
      warn "$me session: using existing session and CurrentUser: \n".
           Dumper($session->{'CurrentUser'})
        if $DEBUG;
diff --cc FS/FS/cust_pkg_discount.pm
index a207940,a207940..5f4d0dc
--- a/FS/FS/cust_pkg_discount.pm
+++ b/FS/FS/cust_pkg_discount.pm
@@@ -106,7 -106,7 +106,8 @@@ sub insert 
        'amount'   => $self->amount,
        'percent'  => $self->percent,
        'months'   => $self->months,
--      'setup'   => $self->setup,
++      'setup'    => $self->setup,
++      #'linked'   => $self->linked,
        'disabled' => 'Y',
      };
      my $error = $discount->insert;
diff --cc FS/FS/discount.pm
index 88cbdd4,88cbdd4..f6f9945
--- a/FS/FS/discount.pm
+++ b/FS/FS/discount.pm
@@@ -136,6 -136,6 +136,7 @@@ sub check 
      || $self->ut_floatn('months') #actually decimal, but this will do
      || $self->ut_enum('disabled', [ '', 'Y' ])
      || $self->ut_enum('setup', [ '', 'Y' ])
++    #|| $self->ut_enum('linked', [ '', 'Y' ])
    ;
    return $error if $error;
  
diff --cc FS/FS/part_event/Action/pkg_agent_credit.pm
index 4bcee98,4bcee98..e1c77be
--- a/FS/FS/part_event/Action/pkg_agent_credit.pm
+++ b/FS/FS/part_event/Action/pkg_agent_credit.pm
@@@ -18,7 -18,7 +18,7 @@@ sub do_action 
    my $agent_cust_main = $agent->agent_cust_main;
      #? or return "No customer record for agent ". $agent->agent;
  
--  my $amount    = $self->_calc_credit($cust_pkg);
++  my $amount = $self->_calc_credit($cust_pkg);
    return '' unless $amount > 0;
  
    my $reasonnum = $self->option('reasonnum');
@@@ -29,6 -29,6 +29,7 @@@
      'eventnum' => $cust_event->eventnum,
      'addlinfo' => 'for customer #'. $cust_main->display_custnum.
                                 ': '.$cust_main->name,
++    #'commission_agentnum' => $agent->agentnum,
    );
    die "Error crediting customer ". $agent_cust_main->custnum.
        " for agent commission: $error"
diff --cc FS/FS/part_export/shellcommands.pm
index ca4e524,ca4e524..f964af3
--- a/FS/FS/part_export/shellcommands.pm
+++ b/FS/FS/part_export/shellcommands.pm
@@@ -490,7 -490,7 +490,7 @@@ sub ssh_cmd { #subroutine, not metho
    my ($output, $errput) = $ssh->capture2($ssh_opt, $opt->{'command'});
  
    return if $opt->{'ignore_all_errors'};
--  die "Error running SSH command: ". $ssh->error if $ssh->error;
++  #die "Error running SSH command: ". $ssh->error if $ssh->error;
  
    if ( ($output || $errput)
         && $opt->{'ignored_errors'} && length($opt->{'ignored_errors'})
@@@ -504,7 -504,7 +504,9 @@@
      $errput =~ s/[\s\n]//g;
    }
  
--  die "$errput\n" if $errput;
++  die (($errput || $ssh->error). "\n") if $errput || $ssh->error; 
++  #die "$errput\n" if $errput;
++
    die "$output\n" if $output and $opt->{'fail_on_output'};
    '';
  }
diff --cc FS/FS/rate.pm
index 02d8250,02d8250..a2511cf
--- a/FS/FS/rate.pm
+++ b/FS/FS/rate.pm
@@@ -387,7 -387,7 +387,7 @@@ sub rate_detail 
  
  =item process
  
--Experimental job-queue processor for web interface adds/edits
++Job-queue processor for web interface adds/edits
  
  =cut
  
diff --cc FS/FS/svc_Tower_Mixin.pm
index 0b55884,0b55884..6adbc6f
--- a/FS/FS/svc_Tower_Mixin.pm
+++ b/FS/FS/svc_Tower_Mixin.pm
@@@ -52,5 -52,5 +52,4 @@@ sub tower_sector_sql 
    @where;
  }
  
--
  1;
diff --cc bin/231commit
index ca28ede,ca28ede..6d09863
--- a/bin/231commit
+++ b/bin/231commit
@@@ -20,8 -20,8 +20,8 @@@ die "no files!" unless @ARGV
  system join('',
    "( cd /home/$USER/freeside2.3/$prefix; git pull ) && ",
    "( cd /home/$USER/freeside2.1/$prefix; git pull ) && ",
--  "git diff -u @ARGV | ( cd /home/$USER/freeside2.3/$prefix; patch ) ",
--  " && git diff -u @ARGV | ( cd /home/$USER/freeside2.1/$prefix; patch ) ",
++  "git diff -u @ARGV | ( cd /home/$USER/freeside2.3/$prefix; patch -p1 ) ",
++  " && git diff -u @ARGV | ( cd /home/$USER/freeside2.1/$prefix; patch -p1 ) ",
    " && ( ( git commit  -m $desc @ARGV && git push); ",
    "( cd /home/$USER/freeside2.3/$prefix; git commit -m $desc @ARGV && git push); ",
    "( cd /home/$USER/freeside2.1/$prefix; git commit -m $desc @ARGV && git push) )"
diff --cc bin/23diff
index 0c0575a,0c0575a..d38c848
--- a/bin/23diff
+++ b/bin/23diff
@@@ -3,7 -3,7 +3,8 @@@
  my $file = shift;
  
  chomp(my $dir = `pwd`);
--$dir =~ s/freeside\//freeside2.3\//;
++$dir =~ s/freeside(\/?)/freeside2.3$1/;
++warn $dir;
  
  #$cmd = "diff -u $file $dir/$file";
  $cmd = "diff -u $dir/$file $file";
diff --cc bin/cdr.import
index 36266ef,36266ef..36266ef
mode 100644,100644..100755
--- a/bin/cdr.import
+++ b/bin/cdr.import
diff --cc bin/cust_main-bill_now
index 17e48fb,17e48fb..f8a1580
mode 100644,100644..100755
--- a/bin/cust_main-bill_now
+++ b/bin/cust_main-bill_now
@@@ -13,7 -13,7 +13,9 @@@ my $custnum = shift or die &usage
  my $cust_main = qsearchs('cust_main', { 'custnum' => $custnum } )
    or die "unknown custnum $custnum\n";
  
--$cust_main->bill_and_collect( debug=>2, check_freq=>'1d' );
++$FS::cust_main::DEBUG = 3;
++
++$cust_main->bill_and_collect( debug=>3, check_freq=>'1d' );
  
  sub usage {
    die "Usage:\n  cust_main-bill_now user custnum\n";
diff --cc bin/pod2x
index ecb7f91,ecb7f91..1ec998f
--- a/bin/pod2x
+++ b/bin/pod2x
@@@ -7,12 -7,12 +7,15 @@@ chomp( my $mw_password = `cat .mw-passw
  
  my $site_perl = "./FS";
  #my $html = "Freeside:1.7:Documentation:Developer";
--my $html = "Freeside:1.9:Documentation:Developer";
++#my $html = "Freeside:1.9:Documentation:Developer";
++my $html = "Freeside:3:Documentation:Developer";
  
  foreach my $dir (
    $html,
--  map "$html/$_", qw( bin FS FS/UI FS/part_export FS/part_pkg
++  map "$html/$_", qw( bin FS
++                      FS/cdr FS/cust_main FS/cust_pkg FS/detail_format
                        FS/part_event FS/part_event/Condition FS/part_event/Action
++                      FS/part_export FS/part_pkg FS/pay_batch
                        FS/ClientAPI FS/Cron FS/Misc FS/Report FS/Report/Table
                        FS/TicketSystem FS/UI
                        FS/SelfService
@@@ -43,6 -43,6 +46,7 @@@ foreach my $file 
  use WWW::Mediawiki::Client;
  my $mvs = WWW::Mediawiki::Client->new(
              'host'           => 'www.freeside.biz',
++            'protocol'       => 'https',
              'wiki_path'      => 'mediawiki/index.php',
              'username'       => $mw_username,
              'password'       => $mw_password,
diff --cc fs_selfservice/DEPLOY
index e73012f,e73012f..bedb5ec
--- a/fs_selfservice/DEPLOY
+++ b/fs_selfservice/DEPLOY
@@@ -11,7 -11,7 +11,7 @@@ perl Makefile.PL && make && make instal
  cd ..
  
  #( cd ..; make deploy; cd fs_selfservice )
--( cd ..; make clean; make install-perl-modules; /etc/init.d/freeside restart; cd fs_selfservice )
++( cd ..; make clean; make configure-rt; make install-perl-modules; /etc/init.d/freeside restart; cd fs_selfservice )
  
  #cp /home/ivan/freeside/fs_selfservice/FS-SelfService/cgi/* /var/www/MyAccount
  #chown freeside /var/www/MyAccount/*.cgi
diff --cc fs_selfservice/FS-SelfService/cgi/agent.cgi
index 0af94cd,0af94cd..0af94cd
mode 100644,100644..100755
--- a/fs_selfservice/FS-SelfService/cgi/agent.cgi
+++ b/fs_selfservice/FS-SelfService/cgi/agent.cgi
diff --cc fs_selfservice/FS-SelfService/cgi/cust_bill-logo.cgi
index 253f853,253f853..253f853
mode 100644,100644..100755
--- a/fs_selfservice/FS-SelfService/cgi/cust_bill-logo.cgi
+++ b/fs_selfservice/FS-SelfService/cgi/cust_bill-logo.cgi
diff --cc fs_selfservice/FS-SelfService/cgi/xmlrpc.cgi
index d5a8e20,d5a8e20..d5a8e20
mode 100644,100644..100755
--- a/fs_selfservice/FS-SelfService/cgi/xmlrpc.cgi
+++ b/fs_selfservice/FS-SelfService/cgi/xmlrpc.cgi
diff --cc httemplate/browse/cust_note_class.html
index f5d450b,f5d450b..7928199
--- a/httemplate/browse/cust_note_class.html
+++ b/httemplate/browse/cust_note_class.html
@@@ -3,7 -3,7 +3,7 @@@
                   'html_init'   => $html_init,
                   'name'        => 'customer note classes',
                   'disableable' => 1,
--                 'disabled_statuspos' => 2,
++                 'disabled_statuspos' => 1,
                   'query'       => { 'table'     => 'cust_note_class',
                                      'hashref'   => {},
                                      'order_by' => 'ORDER BY classnum',
diff --cc httemplate/docs/license.html
index fab8cd0,fab8cd0..e40b243
--- a/httemplate/docs/license.html
+++ b/httemplate/docs/license.html
@@@ -6,7 -6,7 +6,7 @@@
  
  <P>
  
--Copyright © 2005-2009 Freeside Internet Services, Inc.<BR>
++Copyright © 2005-2012 Freeside Internet Services, Inc.<BR>
  Copyright © 2000-2005 Ivan Kohler<BR>
  Copyright © 1999 Silicon Interactive Software Design<BR>
  All rights reserved<BR>
diff --cc httemplate/edit/discount.html
index b195eb3,b195eb3..9bcd1e7
--- a/httemplate/edit/discount.html
+++ b/httemplate/edit/discount.html
@@@ -22,6 -22,6 +22,7 @@@
                                   postfix => '<BR><FONT SIZE="-1"><I>(blank for non-expiring discount)</I></FONT>',
                                 },
                                 { field => 'setup', type => 'checkbox', value=>'Y', },
++                               #{ field => 'linked', type => 'checkbox', value=>'Y', },
                               ],
                   'labels' => { 
                                 'discountnum' => 'Discount #',
@@@ -32,6 -32,6 +33,7 @@@
                                 'percent'     => 'Percentage ',
                                 'months'      => 'Duration (months)',
                                 'setup'       => 'Apply to setup fees',
++                               #'linked'      => 'Apply to add-on packages',
                               },
                   'viewall_dir' => 'browse',
                   'new_callback' => $new_callback,
@@@ -114,6 -114,6 +116,10 @@@ my $javascript = <<END
          document.getElementById('percent_label').style.visibility = 'hidden';
          document.getElementById('percent_input0').style.display = 'none';
          document.getElementById('percent_input0').style.visibility = 'hidden';
++//        document.getElementById('linked_label').style.display = 'none';
++//        document.getElementById('linked_label').style.visibility = 'hidden';
++//        document.getElementById('linked').style.display = 'none';
++//        document.getElementById('linked').style.visibility = 'hidden';
        } else if ( _type == 'Amount' ) {
          document.getElementById('amount_label').style.display = '';
          document.getElementById('amount_label').style.visibility = '';
@@@ -123,6 -123,6 +129,10 @@@
          document.getElementById('percent_label').style.visibility = 'hidden';
          document.getElementById('percent_input0').style.display = 'none';
          document.getElementById('percent_input0').style.visibility = 'hidden';
++//        document.getElementById('linked_label').style.display = 'none';
++//        document.getElementById('linked_label').style.visibility = 'hidden';
++//        document.getElementById('linked').style.display = 'none';
++//        document.getElementById('linked').style.visibility = 'hidden';
        } else if ( _type == 'Percentage' ) {
          document.getElementById('amount_label').style.display = 'none';
          document.getElementById('amount_label').style.visibility = 'hidden';
@@@ -132,6 -132,6 +142,10 @@@
          document.getElementById('percent_label').style.visibility = '';
          document.getElementById('percent_input0').style.display = '';
          document.getElementById('percent_input0').style.visibility = '';
++//        document.getElementById('linked_label').style.display = '';
++//        document.getElementById('linked_label').style.visibility = '';
++//        document.getElementById('linked').style.display = '';
++//        document.getElementById('linked').style.visibility = '';
       }
  
      }
diff --cc httemplate/edit/process/cust_pkg_discount.html
index 6f97a79,6f97a79..4a71f69
--- a/httemplate/edit/process/cust_pkg_discount.html
+++ b/httemplate/edit/process/cust_pkg_discount.html
@@@ -39,7 -39,7 +39,8 @@@ my $cust_pkg_discount = new FS::cust_pk
    'amount'      => scalar($cgi->param('discountnum_amount')),
    'percent'     => scalar($cgi->param('discountnum_percent')),
    'months'      => scalar($cgi->param('discountnum_months')),
--  'setup'      => scalar($cgi->param('discountnum_setup')),
++  'setup'       => scalar($cgi->param('discountnum_setup')),
++  #'linked'       => scalar($cgi->param('discountnum_linked')),
    #'disabled'    => $self->discountnum_disabled,
  };
  my $error = $cust_pkg_discount->insert;
diff --cc httemplate/edit/process/quick-cust_pkg.cgi
index ba4c5b1,ba4c5b1..c5eee0c
--- a/httemplate/edit/process/quick-cust_pkg.cgi
+++ b/httemplate/edit/process/quick-cust_pkg.cgi
@@@ -2,19 -2,19 +2,24 @@@
  %  $cgi->param('error', $error);
  <% $cgi->redirect(popurl(3). 'misc/order_pkg.html?'. $cgi->query_string ) %>
  %} else {
  %  my $show = $curuser->default_customer_view =~ /^(jumbo|packages)$/
  %               ? ''
  %               : ';show=packages';
--%  my $redir_url = popurl(3)
--%            ."view/cust_main.cgi?custnum=$custnum$show;fragment=$frag#$frag";
++%
++%  my $redir_url = popurl(3);
++%  if ( $svcpart ) { # for going straight to service provisining after ordering
++%    $redir_url .= 'edit/'.$part_svc->svcdb.'.cgi?'.
++%                    'pkgnum='.$cust_pkg->pkgnum. ";svcpart=$svcpart";
++%    $redir_url .= ";qualnum=$qualnum" if $qualnum;
++%  } elsif ( $quotationnum ) {
++%    $redir_url .= "view/quotation.html?quotationnum=$quotationnum";
++%  } else {
++%    my $custnum = $cust_main->custnum;
++%    my $frag = "cust_pkg". $cust_pkg->pkgnum;
++%    $redir_url .=
++%      "view/cust_main.cgi?custnum=$custnum$show;fragment=$frag#$frag";
++%  }
  % 
--% # for going right to a provision service after ordering a package
--% if ( $svcpart ) { 
--%   $redir_url = popurl(3)."edit/".$part_svc->svcdb.".cgi?".
--%                  "pkgnum=".$cust_pkg->pkgnum. ";svcpart=$svcpart";
--%   $redir_url .= ";qualnum=$qualnum" if $qualnum;
--% }
  <% header('Package ordered') %>
    <SCRIPT TYPE="text/javascript">
      // XXX fancy ajax rebuild table at some point, but a page reload will do for now
@@@ -33,16 -33,16 +38,27 @@@ my $curuser = $FS::CurrentUser::Current
  die "access denied"
    unless $curuser->access_right('Order customer package');
  
--#untaint custnum (probably not necessary, searching for it is escape enough)
--$cgi->param('custnum') =~ /^(\d+)$/
--  or die 'illegal custnum '. $cgi->param('custnum');
--my $custnum = $1;
--my $cust_main = qsearchs({
--  'table'     => 'cust_main',
--  'hashref'   => { 'custnum' => $custnum },
--  'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
--});
--die 'unknown custnum' unless $cust_main;
++my $cust_main;
++if ( $cgi->param('custnum') =~ /^(\d+)$/ ) {
++  my $custnum = $1;
++  $cust_main = qsearchs({
++    'table'     => 'cust_main',
++    'hashref'   => { 'custnum' => $custnum },
++    'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
++  });
++}
++
++my $prospect_main;
++if ( $cgi->param('prospectnum') =~ /^(\d+)$/ ) {
++  my $prospectnum = $1;
++  $prospect_main = qsearchs({
++    'table'     => 'prospect_main',
++    'hashref'   => { 'prospectnum' => $prospectnum },
++    'extra_sql' => ' AND '. $FS::CurrentUser::CurrentUser->agentnums_sql,
++  });
++}
++
++die 'no custnum or prospectnum' unless $cust_main || $prospect_main;
  
  #probably not necessary, taken care of by cust_pkg::check
  $cgi->param('pkgpart') =~ /^(\d+)$/
@@@ -72,47 -72,47 +88,70 @@@ if ( $cgi->param('svcpart') ) 
  }
  
  my $qualnum = '';
--if ( $cgi->param('qualnum') ) {
--  $cgi->param('qualnum') =~ /^(\d+)$/ or die 'illegal qualnum';
++if ( $cgi->param('qualnum') =~ /^(\d+)$/ ) {
    $qualnum = $1;
  }
++my $quotationnum = '';
++if ( $cgi->param('quotationnum') =~ /^(\d+)$/ ) {
++  $quotationnum = $1;
++}
++# verify this quotation is visible to this user
  
++my $cust_pkg = '';
++my $quotation_pkg = '';
++my $error = '';
  
--my $cust_pkg = new FS::cust_pkg {
--  'custnum'              => $custnum,
--  'pkgpart'              => $pkgpart,
--  'quantity'             => $quantity,
--  'start_date'           => ( scalar($cgi->param('start_date'))
--                                ? parse_datetime($cgi->param('start_date'))
--                                : ''
--                            ),
--  'no_auto'              => scalar($cgi->param('no_auto')),
--  'refnum'               => $refnum,
--  'locationnum'          => $locationnum,
--  'discountnum'          => $discountnum,
--  #for the create a new discount case
--  'discountnum__type'    => scalar($cgi->param('discountnum__type')),
--  'discountnum_amount'   => scalar($cgi->param('discountnum_amount')),
--  'discountnum_percent'  => scalar($cgi->param('discountnum_percent')),
--  'discountnum_months'   => scalar($cgi->param('discountnum_months')),
--  'discountnum_setup'    => scalar($cgi->param('discountnum_setup')),
--  'contract_end'         => ( scalar($cgi->param('contract_end'))
--                                ? parse_datetime($cgi->param('contract_end'))
--                                : ''
--                            ),
--   'waive_setup'         => ( $cgi->param('waive_setup') eq 'Y' ? 'Y' : '' ),
--};
--
--my %opt = ( 'cust_pkg' => $cust_pkg );
--
--if ( $locationnum == -1 ) {
--  my $cust_location = new FS::cust_location {
--    map { $_ => scalar($cgi->param($_)) }
--        qw( custnum address1 address2 city county state zip country geocode )
--  };
--  $opt{'cust_location'} = $cust_location;
--}
++my %hash = (
++    'pkgpart'              => $pkgpart,
++    'quantity'             => $quantity,
++    'start_date'           => ( scalar($cgi->param('start_date'))
++                                  ? parse_datetime($cgi->param('start_date'))
++                                  : ''
++                              ),
++    'refnum'               => $refnum,
++    'locationnum'          => $locationnum,
++    'discountnum'          => $discountnum,
++    #for the create a new discount case
++    'discountnum__type'    => scalar($cgi->param('discountnum__type')),
++    'discountnum_amount'   => scalar($cgi->param('discountnum_amount')),
++    'discountnum_percent'  => scalar($cgi->param('discountnum_percent')),
++    'discountnum_months'   => scalar($cgi->param('discountnum_months')),
++    'discountnum_setup'    => scalar($cgi->param('discountnum_setup')),
++    'contract_end'         => ( scalar($cgi->param('contract_end'))
++                                  ? parse_datetime($cgi->param('contract_end'))
++                                  : ''
++                              ),
++     'waive_setup'         => ( $cgi->param('waive_setup') eq 'Y' ? 'Y' : '' ),
++);
++$hash{'custnum'} = $cust_main->custnum if $cust_main;
++
++if ( $quotationnum ) {
++
++  $quotation_pkg = new FS::quotation_pkg \%hash;
++  $quotation_pkg->quotationnum($quotationnum);
++  $quotation_pkg->prospectnum($prospect_main->prospectnum) if $prospect_main;
  
--my $error = $cust_main->order_pkg( \%opt );
++  #XXX handle new location
++  $error = $quotation_pkg->insert;
++
++} else {
++
++  $cust_pkg = new FS::cust_pkg \%hash;
++
++  $cust_pkg->no_auto( scalar($cgi->param('no_auto')) );
++
++  my %opt = ( 'cust_pkg' => $cust_pkg );
++
++  if ( $locationnum == -1 ) {
++    my $cust_location = new FS::cust_location {
++      map { $_ => scalar($cgi->param($_)) }
++          qw( custnum address1 address2 city county state zip country geocode )
++    };
++    $opt{'cust_location'} = $cust_location;
++  }
++
++  $error = $cust_main->order_pkg( \%opt );
++
++}
  
  </%init>
diff --cc httemplate/edit/process/svc_broadband.cgi
index 90eab4a,90eab4a..25644e5
--- a/httemplate/edit/process/svc_broadband.cgi
+++ b/httemplate/edit/process/svc_broadband.cgi
@@@ -1,11 -1,11 +1,10 @@@
  <& elements/svc_Common.html,
--  table       => 'svc_broadband',
--  fields      => [ fields('svc_broadband'), fields('nas'), 'usergroup' ],
++  table             => 'svc_broadband',
++  fields            => [ fields('svc_broadband'), fields('nas'), 'usergroup' ],
    precheck_callback => \&precheck,
  &>
  <%init>
--# for historical reasons, process_m2m for usergroup tables is done 
--# in the svc_x::insert/replace/delete methods, not here
++
  my $curuser = $FS::CurrentUser::CurrentUser;
  
  die "access denied"
diff --cc httemplate/elements/tr-select-discount.html
index 30a60ec,30a60ec..ee86251
--- a/httemplate/elements/tr-select-discount.html
+++ b/httemplate/elements/tr-select-discount.html
@@@ -6,7 -6,7 +6,7 @@@
  % } else { 
  
    <TR>
--    <TD ALIGN="right" WIDTH="176"><% $opt{'label'} || '<B>'.emt('Discount').'</B>' %></TD>
++    <TD ALIGN="right" WIDTH="275"><% $opt{'label'} || '<B>'.emt('Discount').'</B>' %></TD>
      <TD <% $colspan %>>
        <% include( '/elements/select-discount.html',
                      'curr_value' => $discountnum,
@@@ -74,6 -74,6 +74,16 @@@
              )
    %>
  
++%#  <% include( '/elements/tr-checkbox.html',
++%#                'label'     => '<B>Apply discount to add-on packages</B>',
++%#                'field'     => $name.'_linked',
++%#                'id'        => $name.'_linked',
++%#                'curr_value' => scalar($cgi->param($name.'_linked')),
++%#                'value'     => 'Y',
++%#                'colspan'    => $opt{'colspan'},
++%#            )
++%#  %>
++
    <SCRIPT TYPE="text/javascript">
  
  %   my $ge = 'document.getElementById';
@@@ -136,6 -136,6 +146,10 @@@
          <% $ge %>('<% $name %>_percent_label0').style.visibility = 'hidden';
          <% $ge %>('<% $name %>_percent_input0').style.display = 'none';
          <% $ge %>('<% $name %>_percent_input0').style.visibility = 'hidden';
++//        <% $ge %>('<% $name %>_linked_label0').style.display = 'none';
++//        <% $ge %>('<% $name %>_linked_label0').style.visibility = 'hidden';
++//        <% $ge %>('<% $name %>_linked').style.display = 'none';
++//        <% $ge %>('<% $name %>_linked').style.visibility = 'hidden';
        } else if ( <% $name %>__type == 'Amount' ) {
          <% $ge %>('<% $name %>_amount_label0').style.display = '';
          <% $ge %>('<% $name %>_amount_label0').style.visibility = '';
@@@ -145,6 -145,6 +159,11 @@@
          <% $ge %>('<% $name %>_percent_label0').style.visibility = 'hidden';
          <% $ge %>('<% $name %>_percent_input0').style.display = 'none';
          <% $ge %>('<% $name %>_percent_input0').style.visibility = 'hidden';
++        <% $ge %>('<% $name %>_percent_input0').style.visibility = 'hidden';
++//        <% $ge %>('<% $name %>_linked_label0').style.display = 'none';
++//        <% $ge %>('<% $name %>_linked_label0').style.visibility = 'hidden';
++//        <% $ge %>('<% $name %>_linked').style.display = 'none';
++//        <% $ge %>('<% $name %>_linked').style.visibility = 'hidden';
        } else if ( <% $name %>__type == 'Percentage' ) {
          <% $ge %>('<% $name %>_amount_label0').style.display = 'none';
          <% $ge %>('<% $name %>_amount_label0').style.visibility = 'hidden';
@@@ -154,6 -154,6 +173,11 @@@
          <% $ge %>('<% $name %>_percent_label0').style.visibility = '';
          <% $ge %>('<% $name %>_percent_input0').style.display = '';
          <% $ge %>('<% $name %>_percent_input0').style.visibility = '';
++        <% $ge %>('<% $name %>_percent_input0').style.visibility = '';
++//        <% $ge %>('<% $name %>_linked_label0').style.display = '';
++//        <% $ge %>('<% $name %>_linked_label0').style.visibility = '';
++//        <% $ge %>('<% $name %>_linked').style.display = '';
++//        <% $ge %>('<% $name %>_linked').style.visibility = '';
       }
  
      }
diff --cc httemplate/view/cust_main/custom_content/.birthdate.html.swp
index 9571d22,9571d22..0000000
deleted file mode 100644,100644
--- a/httemplate/view/cust_main/custom_content/.birthdate.html.swp
+++ /dev/null
diff --cc httemplate/view/cust_main/custom_content/.small_custview.html.swp
index a39f52d,a39f52d..0000000
deleted file mode 100644,100644
--- a/httemplate/view/cust_main/custom_content/.small_custview.html.swp
+++ /dev/null
diff --cc httemplate/view/cust_main/custom_content/.spouse_birthdate.html.swp
index 0042012,0042012..0000000
deleted file mode 100644,100644
--- a/httemplate/view/cust_main/custom_content/.spouse_birthdate.html.swp
+++ /dev/null
diff --cc httemplate/view/cust_main/custom_content/.svc_Common.html.swp
index 15591b9,15591b9..0000000
deleted file mode 100644,100644
--- a/httemplate/view/cust_main/custom_content/.svc_Common.html.swp
+++ /dev/null
diff --cc httemplate/view/cust_main/custom_content/.svc_acct.html.swp
index e2db6d5,e2db6d5..0000000
deleted file mode 100644,100644
--- a/httemplate/view/cust_main/custom_content/.svc_acct.html.swp
+++ /dev/null
diff --cc httemplate/view/cust_main/custom_content/.svc_hardware.html.swp
index 1106f9e,1106f9e..0000000
deleted file mode 100644,100644
--- a/httemplate/view/cust_main/custom_content/.svc_hardware.html.swp
+++ /dev/null
diff --cc httemplate/view/cust_main/custom_content/.svc_phone.html.swp
index 79b8185,79b8185..0000000
deleted file mode 100644,100644
--- a/httemplate/view/cust_main/custom_content/.svc_phone.html.swp
+++ /dev/null
diff --cc rt/sbin/rt-server.fcgi.in
index 45c3770,45c3770..f84f6c1
--- a/rt/sbin/rt-server.fcgi.in
+++ b/rt/sbin/rt-server.fcgi.in
@@@ -172,7 -172,7 +172,7 @@@ if (caller) 
  require Plack::Runner;
  
  my $is_fastcgi = $0 =~ m/fcgi$/;
--my $r = Plack::Runner->new( $0 =~ 'standalone' ? ( server => 'Standalone' ) :
++my $r = Plack::Runner->new( $0 =~ /standalone/ ? ( server => 'Standalone' ) :
                              $is_fastcgi        ? ( server => 'FCGI' )
                                                 : (),
                              env => 'deployment' );
diff --cc rt/sbin/rt-server.in
index 45c3770,45c3770..f84f6c1
--- a/rt/sbin/rt-server.in
+++ b/rt/sbin/rt-server.in
@@@ -172,7 -172,7 +172,7 @@@ if (caller) 
  require Plack::Runner;
  
  my $is_fastcgi = $0 =~ m/fcgi$/;
--my $r = Plack::Runner->new( $0 =~ 'standalone' ? ( server => 'Standalone' ) :
++my $r = Plack::Runner->new( $0 =~ /standalone/ ? ( server => 'Standalone' ) :
                              $is_fastcgi        ? ( server => 'FCGI' )
                                                 : (),
                              env => 'deployment' );
diff --cc rt/sbin/rt-test-dependencies.in
index 37ef32f,37ef32f..960d640
--- a/rt/sbin/rt-test-dependencies.in
+++ b/rt/sbin/rt-test-dependencies.in
@@@ -56,9 -56,9 +56,10 @@@ no warnings qw(numeric redefine)
  use Getopt::Long;
  my %args;
  my %deps;
++my @orig_argv = @ARGV;
  GetOptions(
      \%args,                               'v|verbose',
--    'install',                            'with-MYSQL',
++    'install!',                           'with-MYSQL',
      'with-POSTGRESQL|with-pg|with-pgsql', 'with-SQLITE',
      'with-ORACLE',                        'with-FASTCGI',
      'with-MODPERL1',                      'with-MODPERL2',
@@@ -293,7 -293,7 +294,7 @@@ Test::LongStrin
  .
  
  $deps{'FASTCGI'} = [ text_to_hash( << '.') ];
--FCGI
++FCGI 0.74
  FCGI::ProcManager
  .
  
@@@ -344,7 -344,7 +345,7 @@@ URI 1.5
  
  $deps{'GRAPHVIZ'} = [ text_to_hash( << '.') ];
  GraphViz
--IPC::Run
++IPC::Run 0.90
  .
  
  $deps{'GD'} = [ text_to_hash( << '.') ];
@@@ -359,6 -359,6 +360,7 @@@ Convert::Colo
  
  my %AVOID = (
      'DBD::Oracle' => [qw(1.23)],
++    'Email::Address' => [qw(1.893 1.894)],
  );
  
  if ($args{'download'}) {
@@@ -403,7 -403,7 +405,12 @@@ foreach my $type (sort grep $args{$_}, 
      $Missing_By_Type{$type} = \%missing if keys %missing;
  }
  
--conclude(%Missing_By_Type);
++if ( $args{'install'} && keys %Missing_By_Type ) {
++    exec($0, @orig_argv, '--no-install');
++}
++else {
++    conclude(%Missing_By_Type);
++}
  
  sub test_deps {
      my @deps = @_;
diff --cc rt/share/html/Search/Results.xls
index 52a05da,52a05da..8b94e22
--- a/rt/share/html/Search/Results.xls
+++ b/rt/share/html/Search/Results.xls
@@@ -54,6 -54,6 +54,7 @@@ $Format => unde
  <%INIT>
  
  use Spreadsheet::WriteExcel;
++use OLE::Storage_Lite;
  use List::Util qw( max );
  use Date::Format qw( time2str );
  
diff --cc rt/t/api/config.t
index a986c3c,a986c3c..62b77df
--- a/rt/t/api/config.t
+++ b/rt/t/api/config.t
@@@ -1,7 -1,7 +1,8 @@@
  use strict;
  use warnings;
  use RT;
--use RT::Test nodb => 1, tests => 9;
++use RT::Test nodb => 1, tests => 11;
++use Test::Warn;
  
  ok(
      RT::Config->AddOption(
@@@ -31,3 -31,3 +32,12 @@@ is( $meta->{Widget}, '/Widgets/Form/Boo
  ok( RT::Config->DeleteOption( Name => 'foo' ), 'removed option foo' );
  is( RT::Config->Meta('foo'), undef, 'foo is indeed deleted' );
  
++# Test EmailInputEncodings PostLoadCheck code
++RT::Config->Set('EmailInputEncodings', qw(utf-8 iso-8859-1 us-ascii foo));
++my @encodings = qw(utf-8-strict iso-8859-1 ascii);
++
++warning_is {RT::Config->PostLoadCheck} "Unknown encoding 'foo' in \@EmailInputEncodings option",
++  'Correct warning for encoding foo';
++
++my @canonical_encodings = RT::Config->Get('EmailInputEncodings');
++is_deeply(\@encodings, \@canonical_encodings, 'Got correct encoding list');
diff --cc rt/t/api/template-insert.t
index 1bf5fc3,1bf5fc3..0000000
deleted file mode 100644,100644
--- a/rt/t/api/template-insert.t
+++ /dev/null
diff --cc rt/t/api/template-simple.t
index bbdebb3,bbdebb3..0000000
deleted file mode 100644,100644
--- a/rt/t/api/template-simple.t
+++ /dev/null
diff --cc rt/t/api/template.t
index 2fadede,2fadede..331d9f9
--- a/rt/t/api/template.t
+++ b/rt/t/api/template.t
@@@ -1,25 -1,25 +1,34 @@@
  
--use strict;
  use warnings;
--use RT;
--use RT::Test tests => 2;
--
--
--{
--
--ok(require RT::Template);
++use strict;
  
++use RT;
++use RT::Test tests => 10;
  
--}
++my $queue = RT::Test->load_or_create_queue( Name => 'Templates' );
++ok $queue && $queue->id, "loaded or created a queue";
  
  {
--
--my $t = RT::Template->new(RT->SystemUser);
--$t->Create(Name => "Foo", Queue => 1);
--my $t2 = RT::Template->new(RT->Nobody);
--$t2->Load($t->Id);
--ok($t2->QueueObj->id, "Got the template's queue objet");
--
--
++    my $template = RT::Template->new( RT->SystemUser );
++    isa_ok($template, 'RT::Template');
++    my ($val,$msg) = $template->Create(
++        Queue => $queue->id,
++        Name => 'InsertTest',
++        Content => 'This is template content'
++    );
++    ok $val, "created a template" or diag "error: $msg";
++    ok my $id = $template->id, "id is defined";
++    is $template->Name, 'InsertTest';
++    is $template->Content, 'This is template content', "We created the object right";
++
++    ($val, $msg) = $template->SetContent( 'This is new template content');
++    ok $val, "changed content" or diag "error: $msg";
++
++    is $template->Content, 'This is new template content', "We managed to _Set_ the content";
++
++    ($val, $msg) = $template->Delete;
++    ok $val, "deleted template";
++
++    $template->Load($id);
++    ok !$template->id, "can not load template after deletion";
  }
--
diff --cc rt/t/articles/search-interface.t
index eb3a4f7,eb3a4f7..bcba311
--- a/rt/t/articles/search-interface.t
+++ b/rt/t/articles/search-interface.t
@@@ -3,7 -3,7 +3,7 @@@
  use strict;
  use warnings;
  
--use RT::Test tests => 23;
++use RT::Test tests => 44;
  
  use RT::CustomField;
  use RT::Queue;
@@@ -67,7 -67,7 +67,12 @@@ my %cvals = ('article1q' => 'Some quest
  		'article3q' => 'Why should I eat my supper?',
  		'article3a' => 'There are starving children in Africa',
  		'article4q' => 'What did Brian originally write?',
--		'article4a' => 'Romanes eunt domus');
++		'article4a' => 'This is an answer that is longer than 255 '
++	     . 'characters so these tests will be sure to use the LargeContent '
++	     . 'SQL as well as the normal SQL that would be generated if this '
++	     . 'was an answer that was shorter than 255 characters. This second '
++	     . 'sentence has a few extra characters to get this string to go '
++	     . 'over the 255 character boundary. Lorem ipsum.');
  
  # Create an article or two with our custom field values.
  
@@@ -108,6 -108,6 +113,52 @@@ isa_ok($m, 'Test::WWW::Mechanize')
  ok($m->login, 'logged in');
  $m->follow_link_ok( { text => 'Articles', url_regex => qr!^/Articles/! },
      'UI -> Articles' );
--$m->follow_link_ok( {text => 'Search'}, 'Articles -> Search');
--$m->follow_link_ok( {text => 'in class '.$class->Name}, 'Articles in class '.$class->Name);
--$m->content_contains($article1->Name);
++
++# In all of the search results below, the results page should
++# have the summary text of the article it occurs in.
++
++# Case sensitive search on small field.
++DoArticleSearch($m, $class->Name, 'Africa');
++$m->text_contains('Search results'); # Did we do a search?
++$m->text_contains('blah blah 1');
++
++# Case insensitive search on small field.
++DoArticleSearch($m, $class->Name, 'africa');
++$m->text_contains('Search results'); # Did we do a search?
++$m->text_contains('blah blah 1');
++
++# Case sensitive search on large field.
++DoArticleSearch($m, $class->Name, 'ipsum');
++$m->text_contains('Search results'); # Did we do a search?
++$m->text_contains('hoi polloi 4');
++
++# Case insensitive search on large field.
++DoArticleSearch($m, $class->Name, 'lorem');
++$m->text_contains('Search results'); # Did we do a search?
++TODO:{
++    local $TODO = 'Case insensitive search on LONGBLOB not available in MySQL'
++      if RT->Config->Get('DatabaseType') eq 'mysql';
++    $m->text_contains('hoi polloi 4');
++}
++
++# When you send $m to this sub, it must be on a page with
++# a Search link.
++sub DoArticleSearch{
++  my $m = shift;
++  my $class_name = shift;
++  my $search_text = shift;
++
++  $m->follow_link_ok( {text => 'Search'}, 'Articles -> Search');
++  $m->follow_link_ok( {text => 'in class '. $class_name}, 'Articles in class '. $class_name);
++  $m->text_contains('First article');
++
++  $m->submit_form_ok( {
++            form_number => 3,
++            fields      => {
++                'Article~' => $search_text
++            },
++        }, "Search for $search_text"
++    );
++  return;
++}
++
diff --cc rt/t/articles/uri-a.t
index 82d0f1b,82d0f1b..5c1fdaf
--- a/rt/t/articles/uri-a.t
+++ b/rt/t/articles/uri-a.t
@@@ -3,7 -3,7 +3,7 @@@
  use strict;
  use warnings;
  
--use RT::Test tests => 7;
++use RT::Test tests => 15;
  
  use_ok("RT::URI::a");
  my $uri = RT::URI::a->new($RT::SystemUser);
@@@ -26,3 -26,3 +26,39 @@@ is(ref($uri->Object), "RT::Article", "O
  is($uri->Object->Id, $article->Id, "Object loaded has correct ID");
  is($article->URI, 'fsck.com-article://example.com/article/'.$article->Id, 
     "URI object has correct URI string");
++
++{
++    my $aid = $article->id;
++    my $ticket = RT::Ticket->new( RT->SystemUser );
++    my ($id, $msg) = $ticket->Create(
++        Queue       => 1,
++        Subject     => 'test ticket',
++    );
++    ok $id, "Created a test ticket";
++
++    # Try searching
++    my $tickets = RT::Tickets->new( RT->SystemUser );
++    $tickets->FromSQL(" RefersTo = 'a:$aid' ");
++    is $tickets->Count, 0, "No results yet";
++
++    # try with the full uri
++    $tickets->FromSQL(" RefersTo = '@{[ $article->URI ]}' ");
++    is $tickets->Count, 0, "Still no results";
++
++    # add the link
++    $ticket->AddLink( Type => 'RefersTo', Target => "a:$aid" );
++
++    # verify the ticket has it
++    my @links = @{$ticket->RefersTo->ItemsArrayRef};
++    is scalar @links, 1, "Has one RefersTo link";
++    is ref $links[0]->TargetObj, "RT::Article", "Link points to an article";
++    is $links[0]->TargetObj->id, $aid, "Link points to the article we specified";
++
++    # search again
++    $tickets->FromSQL(" RefersTo = 'a:$aid' ");
++    is $tickets->Count, 1, "Found one ticket with short URI";
++
++    # search with the full uri
++    $tickets->FromSQL(" RefersTo = '@{[ $article->URI ]}' ");
++    is $tickets->Count, 1, "Found one ticket with full URI";
++}
diff --cc rt/t/data/configs/apache2.2+fastcgi.conf.in
index 3ec36dd,3ec36dd..03eaa9a
--- a/rt/t/data/configs/apache2.2+fastcgi.conf.in
+++ b/rt/t/data/configs/apache2.2+fastcgi.conf.in
@@@ -12,6 -12,6 +12,7 @@@ Group @WEB_GROUP
  </IfModule>
  </IfModule>
  
++ServerName localhost
  Listen %%LISTEN%%
  
  ErrorLog "%%LOG_FILE%%"
diff --cc rt/t/data/configs/apache2.2+mod_perl.conf.in
index 3b1f3f6,3b1f3f6..20d2f44
--- a/rt/t/data/configs/apache2.2+mod_perl.conf.in
+++ b/rt/t/data/configs/apache2.2+mod_perl.conf.in
@@@ -30,6 -30,6 +30,7 @@@ Group @WEB_GROUP
  </IfModule>
  </IfModule>
  
++ServerName localhost
  Listen %%LISTEN%%
  
  ErrorLog "%%LOG_FILE%%"
diff --cc rt/t/mail/dashboard-chart-with-utf8.t
index 6d07b96,6d07b96..79f5f0e
--- a/rt/t/mail/dashboard-chart-with-utf8.t
+++ b/rt/t/mail/dashboard-chart-with-utf8.t
@@@ -1,7 -1,7 +1,17 @@@
  use strict;
  use warnings;
  
--use RT::Test tests => 15;
++BEGIN {
++    require RT::Test;
++
++    if (eval { require GD }) {
++        RT::Test->import(tests => 15);
++    }
++    else {
++        RT::Test->import(skip_all => 'GD required.');
++    }
++}
++
  use utf8;
  
  my $root = RT::Test->load_or_create_user( Name => 'root' );
diff --cc rt/t/mail/dashboards.t
index 7a7a54c,7a7a54c..00cfc6a
--- a/rt/t/mail/dashboards.t
+++ b/rt/t/mail/dashboards.t
@@@ -2,7 -2,7 +2,7 @@@
  use strict;
  use warnings;
  
--use RT::Test tests => 187;
++use RT::Test tests => 181;
  use Test::Warn;
  use RT::Dashboard::Mailer;
  
@@@ -138,17 -138,17 +138,6 @@@ sub delete_dashboard { # {{
      ok($ok, $msg);
  } # }}}
  
--sub delete_subscriptions { # {{{
--    my $subscription_id = shift;
--    # delete the dashboard and make sure we get exactly one subscription failure
--    # notice
--    my $user = RT::User->new(RT->SystemUser);
--    $user->Load('root');
--    for my $subscription ($user->Attributes->Named('Subscription')) {
--        $subscription->Delete;
--    }
--} # }}}
--
  my $good_time = 1290423660; # 6:01 EST on a monday
  my $bad_time  = 1290427260; # 7:01 EST on a monday
  
@@@ -223,21 -223,21 +212,9 @@@ SKIP: 
  
  delete_dashboard($dashboard_id);
  
--warning_like {
--    RT::Dashboard::Mailer->MailDashboards(All => 1);
--} qr/Unable to load dashboard $dashboard_id of subscription $subscription_id for user root/;
--
-- at mails = RT::Test->fetch_caught_mails;
--is(@mails, 1, "one mail for subscription failure");
--$mail = parse_mail($mails[0]);
--is($mail->head->get('Subject'), "[example.com] Missing dashboard!\n");
--is($mail->head->get('From'), "dashboard\@example.com\n");
--is($mail->head->get('X-RT-Dashboard-Id'), "$dashboard_id\n");
--is($mail->head->get('X-RT-Dashboard-Subscription-Id'), "$subscription_id\n");
--
  RT::Dashboard::Mailer->MailDashboards(All => 1);
  @mails = RT::Test->fetch_caught_mails;
--is(@mails, 0, "no mail because the subscription notice happens only once");
++is(@mails, 0, "no mail because the subscription is deleted");
  
  RT::Test->stop_server;
  RT::Test->clean_caught_mails;
@@@ -277,7 -277,7 +254,6 @@@ RT->Config->Set('EmailDashboardRemove' 
  ($baseurl, $m) = RT::Test->started_ok;
  
  delete_dashboard($dashboard_id);
--delete_subscriptions();
  
  RT::Test->clean_caught_mails;
  
@@@ -330,7 -330,7 +306,6 @@@ RT->Config->Set('EmailDashboardRemove' 
  ($baseurl, $m) = RT::Test->started_ok;
  
  delete_dashboard($dashboard_id);
--delete_subscriptions();
  
  RT::Test->clean_caught_mails;
  
@@@ -373,7 -373,7 +348,6 @@@ RT->Config->Set('EmailDashboardRemove' 
  ($baseurl, $m) = RT::Test->started_ok;
  
  delete_dashboard($dashboard_id);
--delete_subscriptions();
  
  RT::Test->clean_caught_mails;
  
diff --cc rt/t/mail/gateway.t
index 9f0e669,9f0e669..98eabd5
--- a/rt/t/mail/gateway.t
+++ b/rt/t/mail/gateway.t
@@@ -57,7 -57,7 +57,7 @@@ use strict
  use warnings;
  
  
--use RT::Test config => 'Set( $UnsafeEmailCommands, 1);', tests => 221, actual_server => 1;
++use RT::Test config => 'Set( $UnsafeEmailCommands, 1);', tests => 228, actual_server => 1;
  my ($baseurl, $m) = RT::Test->started_ok;
  
  use RT::Tickets;
@@@ -608,6 -608,6 +608,35 @@@ EO
      $m->no_warnings_ok;
  }
  
++diag "make sure we check that UTF-8 is really UTF-8";
++{
++    my $text = <<EOF;
++From: root\@localhost
++To: rtemail\@@{[RT->Config->Get('rtname')]}
++Subject: This is test wrong utf-8 chars
++Content-Type: text/plain; charset="utf-8"
++
++utf-8: informaci\303\263n confidencial
++latin1: informaci\363n confidencial
++
++bye
++EOF
++    my ($status, $id) = RT::Test->send_via_mailgate_and_http($text);
++    is ($status >> 8, 0, "The mail gateway exited normally");
++    ok ($id, "created ticket");
++
++    my $tick = RT::Test->last_ticket;
++    is ($tick->Id, $id, "correct ticket");
++
++    my $content = $tick->Transactions->First->Content;
++    Encode::_utf8_off($content);
++
++    like $content, qr{informaci\303\263n confidencial};
++    like $content, qr{informaci\357\277\275n confidencial};
++
++    $m->no_warnings_ok;
++}
++
  diag "check that mailgate doesn't suffer from empty Reply-To:";
  {
      my $text = <<EOF;
diff --cc rt/t/shredder/01ticket.t
index 7dff16d,7dff16d..a7abeef
--- a/rt/t/shredder/01ticket.t
+++ b/rt/t/shredder/01ticket.t
@@@ -78,7 -78,7 +78,11 @@@ cmp_deeply( dump_current_and_savepoint(
      my $shredder = shredder_new();
      $shredder->PutObjects( Objects => $child );
      $shredder->WipeoutAll;
--    cmp_deeply( dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint");
++
++  TODO: {
++        local $TODO = "Shredder doesn't delete all links and transactions";
++        cmp_deeply( dump_current_and_savepoint('parent_ticket'), "current DB equal to savepoint");
++    }
  
      $shredder->PutObjects( Objects => $parent );
      $shredder->WipeoutAll;
diff --cc rt/t/shredder/03plugin_tickets.t
index 092b570,092b570..e63eef8
--- a/rt/t/shredder/03plugin_tickets.t
+++ b/rt/t/shredder/03plugin_tickets.t
@@@ -34,6 -34,6 +34,7 @@@ use_ok('RT::Tickets')
      my $child = RT::Ticket->new( RT->SystemUser );
      my ($cid) = $child->Create( Subject => 'child', Queue => 1, MemberOf => $pid );
      ok( $cid, "created new ticket" );
++    $_->ApplyTransactionBatch for $parent, $child;
  
      my $plugin = RT::Shredder::Plugin::Tickets->new;
      isa_ok($plugin, 'RT::Shredder::Plugin::Tickets');
@@@ -77,6 -77,6 +78,8 @@@ cmp_deeply( dump_current_and_savepoint(
      my ($status, $msg) = $child->AddLink( Target => $pid, Type => 'DependsOn' );
      ok($status, "added reqursive link") or diag "error: $msg";
  
++    $_->ApplyTransactionBatch for $parent, $child;
++
      my $plugin = RT::Shredder::Plugin::Tickets->new;
      isa_ok($plugin, 'RT::Shredder::Plugin::Tickets');
  
@@@ -121,6 -121,6 +124,8 @@@ cmp_deeply( dump_current_and_savepoint(
      ok( $cid2, "created new ticket" );
      $child2->SetStatus('resolved');
  
++    $_->ApplyTransactionBatch for $parent, $child1, $child2;
++
      my $plugin = RT::Shredder::Plugin::Tickets->new;
      isa_ok($plugin, 'RT::Shredder::Plugin::Tickets');
  
diff --cc rt/t/shredder/03plugin_users.t
index 4f4ecc8,4f4ecc8..1f4cb49
--- a/rt/t/shredder/03plugin_users.t
+++ b/rt/t/shredder/03plugin_users.t
@@@ -5,8 -5,8 +5,8 @@@ use warnings
  
  use Test::Deep;
  use File::Spec;
--use Test::More tests => 9;
--use RT::Test nodb => 1;
++use Test::More tests => 21;
++use RT::Test ();
  BEGIN {
      my $shredder_utils = RT::Test::get_relocatable_file('utils.pl',
          File::Spec->curdir());
@@@ -38,3 -38,3 +38,61 @@@ use_ok('RT::Shredder::Plugin::Users')
      ok(!$status, "bad 'status' arg value");
  }
  
++init_db();
++
++RT::Test->set_rights(
++    { Principal => 'Everyone', Right => [qw(CreateTicket)] },
++);
++
++create_savepoint('clean');
++
++{ # Create two users and a ticket. Shred second user and replace relations with first user
++    my ($uidA, $uidB, $msg);
++    my $userA = RT::User->new( RT->SystemUser );
++    ($uidA, $msg) = $userA->Create( Name => 'userA', Privileged => 1, Disabled => 0 );
++    ok( $uidA, "created user A" ) or diag "error: $msg";
++
++    my $userB = RT::User->new( RT->SystemUser );
++    ($uidB, $msg) = $userB->Create( Name => 'userB', Privileged => 1, Disabled => 0 );
++    ok( $uidB, "created user B" ) or diag "error: $msg";
++
++    my ($tid, $trid);
++    my $ticket = RT::Ticket->new( RT::CurrentUser->new($userB) );
++    ($tid, $trid, $msg) = $ticket->Create( Subject => 'UserB Ticket', Queue => 1 );
++    ok( $tid, "created new ticket") or diag "error: $msg";
++
++    my $transaction = RT::Transaction->new( RT->SystemUser );
++    $transaction->Load($trid);
++    is ( $transaction->Creator, $uidB, "ticket creator is user B" );
++
++    my $plugin = RT::Shredder::Plugin::Users->new;
++    isa_ok($plugin, 'RT::Shredder::Plugin::Users');
++
++    my $status;
++    ($status, $msg) = $plugin->TestArgs( status => 'any', name => 'userB', replace_relations => $uidA );
++    ok($status, "plugin arguments are ok") or diag "error: $msg";
++
++    my @objs;
++    ($status, @objs) = $plugin->Run;
++    ok($status, "executed plugin successfully") or diag "error: @objs";
++    @objs = RT::Shredder->CastObjectsToRecords( Objects => \@objs );
++    is(scalar @objs, 1, "one object in the result set");
++
++    my $shredder = shredder_new();
++
++    ($status, $msg) = $plugin->SetResolvers( Shredder => $shredder );
++    ok($status, "set conflicts resolver") or diag "error: $msg";
++
++    $shredder->PutObjects( Objects => \@objs );
++    $shredder->WipeoutAll;
++
++    $ticket->Load( $tid );
++    is($ticket->id, $tid, 'loaded ticket');
++
++    $transaction->Load($trid);
++    is ( $transaction->Creator, $uidA, "ticket creator is now user A" );
++
++    $shredder->Wipeout( Object => $ticket );
++    $shredder->Wipeout( Object => $userA );
++}
++cmp_deeply( dump_current_and_savepoint('clean'), "current DB equal to savepoint");
diff --cc rt/t/shredder/utils.pl
index 5f5c182,5f5c182..9b848c6
--- a/rt/t/shredder/utils.pl
+++ b/rt/t/shredder/utils.pl
@@@ -283,7 -283,7 +283,7 @@@ sub dump_sqlit
      my $old_fhkn = $dbh->{'FetchHashKeyName'};
      $dbh->{'FetchHashKeyName'} = 'NAME_lc';
  
--    my $sth = $dbh->table_info( '', '', '%', 'TABLE' ) || die $DBI::err;
++    my $sth = $dbh->table_info( '', '%', '%', 'TABLE' ) || die $DBI::err;
      my @tables = keys %{$sth->fetchall_hashref( 'table_name' )};
  
      my $res = {};
diff --cc rt/t/ticket/search_by_watcher.t
index 809450b,809450b..cfc7b1c
--- a/rt/t/ticket/search_by_watcher.t
+++ b/rt/t/ticket/search_by_watcher.t
@@@ -142,8 -142,8 +142,8 @@@ sub run_auto_tests 
  @conditions = (
      'Cc = "not at exist"'       => sub { 0 },
      'Cc != "not at exist"'      => sub { 1 },
--    'Cc IS NULL'             => sub { $_[0] =~ 'c:-;' },
--    'Cc IS NOT NULL'         => sub { $_[0] !~ 'c:-;' },
++    'Cc IS NULL'             => sub { $_[0] =~ /c:-;/ },
++    'Cc IS NOT NULL'         => sub { $_[0] !~ /c:-;/ },
      'Cc = "x at foo.com"'       => sub { $_[0] =~ /c:[^;]*x/ },
      'Cc != "x at foo.com"'      => sub { $_[0] !~ /c:[^;]*x/ },
      'Cc LIKE "@bar.com"'     => sub { $_[0] =~ /c:[^;]*(?:y|z)/ },
@@@ -152,8 -152,8 +152,8 @@@
  
      'Requestor = "not at exist"'   => sub { 0 },
      'Requestor != "not at exist"'  => sub { 1 },
--    'Requestor IS NULL'         => sub { $_[0] =~ 'r:-;' },
--    'Requestor IS NOT NULL'     => sub { $_[0] !~ 'r:-;' },
++    'Requestor IS NULL'         => sub { $_[0] =~ /r:-;/ },
++    'Requestor IS NOT NULL'     => sub { $_[0] !~ /r:-;/ },
      'Requestor = "x at foo.com"'   => sub { $_[0] =~ /r:[^;]*x/ },
      'Requestor != "x at foo.com"'  => sub { $_[0] !~ /r:[^;]*x/ },
      'Requestor LIKE "@bar.com"' => sub { $_[0] =~ /r:[^;]*(?:y|z)/ },
@@@ -174,7 -174,7 +174,7 @@@
      'Subject LIKE "ne"'      => sub { 0 },
      'Subject NOT LIKE "ne"'  => sub { 1 },
      'Subject = "r:x;c:y;"'   => sub { $_[0] eq 'r:x;c:y;' },
--    'Subject LIKE "x"'       => sub { $_[0] =~ 'x' },
++    'Subject LIKE "x"'       => sub { $_[0] =~ /x/ },
  );
  
  @tickets = generate_tix();
diff --cc rt/t/web/attachments.t
index 8c75f6c,8c75f6c..784cbbe
--- a/rt/t/web/attachments.t
+++ b/rt/t/web/attachments.t
@@@ -1,10 -1,10 +1,11 @@@
  #!/usr/bin/perl -w
  use strict;
  
--use RT::Test tests => 25;
++use RT::Test tests => 33;
  
  use constant LogoFile => $RT::MasonComponentRoot .'/NoAuth/images/bpslogo.png';
  use constant FaviconFile => $RT::MasonComponentRoot .'/NoAuth/images/favicon.png';
++use constant TextFile => $RT::MasonComponentRoot .'/NoAuth/css/print.css';
  
  my ($baseurl, $m) = RT::Test->started_ok;
  ok $m->login, 'logged in';
@@@ -30,9 -30,9 +31,18 @@@ $m->content_contains('Attachments test'
  $m->content_contains('Some content', 'and content');
  $m->content_contains('Download bpslogo.png', 'page has file name');
  
++open LOGO, "<", LogoFile or die "Can't open logo file: $!";
++binmode LOGO;
++my $logo_contents = do {local $/; <LOGO>};
++close LOGO;
++$m->follow_link_ok({text => "Download bpslogo.png"});
++is($m->content_type, "image/png");
++is($m->content, $logo_contents, "Binary content matches");
++
++$m->back;
  $m->follow_link_ok({text => 'Reply'}, "reply to the ticket");
  $m->form_name('TicketUpdate');
--$m->field('Attach',  LogoFile);
++$m->field('Attach',  TextFile);
  $m->click('AddMoreAttach');
  is($m->status, 200, "request successful");
  
@@@ -44,7 -44,7 +54,16 @@@ is($m->status, 200, "request successful
  
  $m->content_contains('Download bpslogo.png', 'page has file name');
  $m->content_contains('Download favicon.png', 'page has file name');
++$m->content_contains('Download print.css', 'page has file name');
++
++$m->follow_link_ok( { text => 'Download bpslogo.png' } );
++is( $m->response->header('Content-Type'), 'image/png', 'Content-Type of png lacks charset' );
++
++$m->back;
  
++$m->follow_link_ok( { text => 'Download print.css' } );
++is( $m->response->header('Content-Type'),
++    'text/css;charset=UTF-8', 'Content-Type of text has charset' );
  
  diag "test mobile ui";
  $m->get_ok( $baseurl . '/m/ticket/create?Queue=' . $qid );
diff --cc rt/t/web/command_line.t
index 1fed8e6,1fed8e6..394daab
--- a/rt/t/web/command_line.t
+++ b/rt/t/web/command_line.t
@@@ -3,7 -3,7 +3,7 @@@
  use strict;
  use File::Spec ();
  use Test::Expect;
--use RT::Test tests => 303, actual_server => 1;
++use RT::Test tests => 315, actual_server => 1;
  my ($baseurl, $m) = RT::Test->started_ok;
  
  use RT::User;
@@@ -480,6 -480,6 +480,8 @@@ expect_like(qr/Merged into ticket #$mer
          expect_like(qr/Created link $link1_id $reln $link2_id/, 'Linked');
          expect_send("show -s ticket/$link1_id/links", "Checking creation of $reln...");
          expect_like(qr/$display_relns{$reln}: [\w\d\.\-]+:\/\/[\w\d\.]+\/ticket\/$link2_id/, "Created link $reln");
++        expect_send("show ticket/$link1_id/links", "Checking show links without format");
++        expect_like(qr/$display_relns{$reln}: [\w\d\.\-]+:\/\/[\w\d\.]+\/ticket\/$link2_id/, "Found link $reln");
  
          # delete link
          expect_send("link -d $link1_id $reln $link2_id", "Delete $reln...");
diff --cc rt/t/web/command_line_with_unknown_field.t
index 736be4d,736be4d..d63956b
--- a/rt/t/web/command_line_with_unknown_field.t
+++ b/rt/t/web/command_line_with_unknown_field.t
@@@ -3,7 -3,7 +3,7 @@@
  use strict;
  use File::Spec ();
  use Test::Expect;
--use RT::Test tests => 14, actual_server => 1;
++use RT::Test tests => 17, actual_server => 1;
  my ($baseurl, $m) = RT::Test->started_ok;
  my $rt_tool_path = "$RT::BinPath/rt";
  
@@@ -19,6 -19,6 +19,11 @@@ expect_run
      prompt => 'rt> ',
      quit => 'quit',
  );
++
++expect_send( q{create -t ticket set foo=bar}, "create ticket with unknown field" );
++expect_like(qr/foo: Unknown field/, 'foo is unknown field');
++expect_like(qr/Could not create ticket/, 'ticket is not created');
++
  expect_send(q{create -t ticket set subject='new ticket' add cc=foo at example.com}, "Creating a ticket...");
  
  expect_like(qr/Ticket \d+ created/, "Created the ticket");
diff --cc rt/t/web/crypt-gnupg.t
index 6bdefda,6bdefda..8c0eb57
--- a/rt/t/web/crypt-gnupg.t
+++ b/rt/t/web/crypt-gnupg.t
@@@ -53,6 -53,6 +53,7 @@@ RT::Test->clean_caught_mails
  
  $m->goto_create_ticket( $queue );
  $m->form_name('TicketCreate');
++$m->field('Requestors', 'recipient at example.com');
  $m->field('Subject', 'Encryption test');
  $m->field('Content', 'Some content');
  ok($m->value('Encrypt', 2), "encrypt tick box is checked");
@@@ -122,6 -122,6 +123,7 @@@ RT::Test->clean_caught_mails
  
  $m->goto_create_ticket( $queue );
  $m->form_name('TicketCreate');
++$m->field('Requestors', 'recipient at example.com');
  $m->field('Subject', 'Signing test');
  $m->field('Content', 'Some other content');
  ok(!$m->value('Encrypt', 2), "encrypt tick box is unchecked");
@@@ -195,6 -195,6 +197,7 @@@ RT::Test->clean_caught_mails
  
  $m->goto_create_ticket( $queue );
  $m->form_name('TicketCreate');
++$m->field('Requestors', 'recipient at example.com');
  $m->field('Subject', 'Crypt+Sign test');
  $m->field('Content', 'Some final? content');
  ok($m->value('Encrypt', 2), "encrypt tick box is checked");
@@@ -260,6 -260,6 +263,7 @@@ RT::Test->clean_caught_mails
  
  $m->goto_create_ticket( $queue );
  $m->form_name('TicketCreate');
++$m->field('Requestors', 'recipient at example.com');
  $m->field('Subject', 'Test crypt-off on encrypted queue');
  $m->field('Content', 'Thought you had me figured out didya');
  $m->field(Encrypt => undef, 2); # turn off encryption
diff --cc rt/t/web/googleish_search.t
index e2a4e91,e2a4e91..f4c8fa4
--- a/rt/t/web/googleish_search.t
+++ b/rt/t/web/googleish_search.t
@@@ -2,7 -2,7 +2,8 @@@
  use strict;
  use warnings;
  
--use RT::Test tests => 96, config => 'Set( %FullTextSearch, Enable => 1, Indexed => 0 );';
++use RT::Test tests => undef,
++    config => 'Set( %FullTextSearch, Enable => 1, Indexed => 0 );';
  my ($baseurl, $m) = RT::Test->started_ok;
  my $url = $m->rt_base_url;
  
@@@ -57,6 -57,6 +58,7 @@@ ok $two_words_queue && $two_words_queue
      is $parser->QueryToSQL("'me'"), "$active AND ( Subject LIKE 'me' )", "correct parsing";
      is $parser->QueryToSQL("owner:me"), "( Owner.id = '__CurrentUser__' ) AND $active", "correct parsing";
      is $parser->QueryToSQL("owner:'me'"), "( Owner = 'me' ) AND $active", "correct parsing";
++    is $parser->QueryToSQL('owner:root at localhost'), "( Owner.EmailAddress = 'root\@localhost' ) AND $active", "Email address as owner";
  
      is $parser->QueryToSQL("resolved me"), "( Owner.id = '__CurrentUser__' ) AND ( Status = 'resolved' )", "correct parsing";
      is $parser->QueryToSQL("resolved active me"), "( Owner.id = '__CurrentUser__' ) AND ( Status = 'resolved' OR Status = 'new' OR Status = 'open' OR Status = 'stalled' )", "correct parsing";
@@@ -217,3 -217,3 +219,5 @@@ for my $quote ( q{'}, q{"} ) 
      }
  }
  
++undef $m;
++done_testing;
diff --cc rt/t/web/query_builder_queue_limits.t
index a3b9765,a3b9765..f583d64
--- a/rt/t/web/query_builder_queue_limits.t
+++ b/rt/t/web/query_builder_queue_limits.t
@@@ -11,6 -11,6 +11,9 @@@ $lifecycles->{foo} = 
  
  };
  
++# explicitly Set so RT::Test can catch our change
++RT->Config->Set( Lifecycles => %$lifecycles );
++
  RT::Lifecycle->FillCache();
  
  my $general = RT::Test->load_or_create_queue( Name => 'General' );
diff --cc rt/t/web/search_simple.t
index 1efc9a5,1efc9a5..a1a3ce8
--- a/rt/t/web/search_simple.t
+++ b/rt/t/web/search_simple.t
@@@ -1,7 -1,7 +1,7 @@@
  use strict;
  use warnings;
  
--use RT::Test tests => 16;
++use RT::Test tests => 30;
  my ( $baseurl, $m ) = RT::Test->started_ok;
  
  RT::Test->create_tickets(
@@@ -19,4 -19,4 +19,58 @@@ $m->content_contains( 'Show Results'
  $m->title_is( 'Found 1 ticket', 'title' );
  $m->content_contains( 'ticket foo', 'has ticket foo' );
  
++# Test searches on custom fields
++my $cf1 = RT::Test->load_or_create_custom_field(
++                      Name  => 'Location',
++                      Queue => 'General',
++                      Type  => 'FreeformSingle', );
++isa_ok( $cf1, 'RT::CustomField' );
++
++my $cf2 = RT::Test->load_or_create_custom_field(
++                      Name  => 'Server-name',
++                      Queue => 'General',
++                      Type  => 'FreeformSingle', );
++isa_ok( $cf2, 'RT::CustomField' );
++
++my $t = RT::Ticket->new(RT->SystemUser);
++
++{
++  my ($id,undef,$msg) = $t->Create(
++            Queue => 'General',
++            Subject => 'Test searching CFs');
++  ok( $id, "Created ticket - $msg" );
++}
++
++{
++  my ($status, $msg) = $t->AddCustomFieldValue(
++                           Field => $cf1->id,
++			   Value => 'Downtown');
++  ok( $status, "Added CF value - $msg" );
++}
++
++{
++  my ($status, $msg) = $t->AddCustomFieldValue(
++                           Field => $cf2->id,
++			   Value => 'Proxy');
++  ok( $status, "Added CF value - $msg" );
++}
++
++# Regular search
++my $search = 'cf.Location:Downtown';
++$m->get_ok("/Search/Simple.html?q=$search");
++$m->title_is( 'Found 1 ticket', 'Found 1 ticket' );
++$m->text_contains( 'Test searching CFs', "Found test CF ticket with $search" );
++
++# Case insensitive
++$search = "cf.Location:downtown";
++$m->get_ok("/Search/Simple.html?q=$search");
++$m->title_is( 'Found 1 ticket', 'Found 1 ticket' );
++$m->text_contains( 'Test searching CFs', "Found test CF ticket with $search" );
++
++# With dash in CF name
++$search = "cf.Server-name:Proxy";
++$m->get_ok("/Search/Simple.html?q=$search");
++$m->title_is( 'Found 1 ticket', 'Found 1 ticket' );
++$m->text_contains( 'Test searching CFs', "Found test CF ticket with $search" );
++
  # TODO more simple search tests
diff --cc rt/t/web/ticket_modify_all.t
index c9dd7e7,c9dd7e7..2f0c4d1
--- a/rt/t/web/ticket_modify_all.t
+++ b/rt/t/web/ticket_modify_all.t
@@@ -1,7 -1,7 +1,7 @@@
  use strict;
  use warnings;
  
--use RT::Test tests => 15;
++use RT::Test tests => 22;
  
  my $ticket = RT::Test->create_ticket(
      Subject => 'test bulk update',
@@@ -40,5 -40,5 +40,44 @@@ $m->click('SubmitTicket')
  $m->form_name('TicketModifyAll');
  is($m->value('Owner'), 'root', 'owner was successfully changed to root');
  
--# XXX TODO test other parts, i.e. basic, dates, people and links
++$m->get_ok($url . "/Ticket/ModifyAll.html?id=" . $ticket->id);
  
++$m->form_name('TicketModifyAll');
++$m->field('Starts_Date' => "2013-01-01 00:00:00");
++$m->click('SubmitTicket');
++$m->text_contains("Starts: (Tue Jan 01 00:00:00 2013)", 'start date successfully updated');
++
++$m->form_name('TicketModifyAll');
++$m->field('Started_Date' => "2014-01-01 00:00:00");
++$m->click('SubmitTicket');
++$m->text_contains("Started: (Wed Jan 01 00:00:00 2014)", 'started date successfully updated');
++
++$m->form_name('TicketModifyAll');
++$m->field('Told_Date' => "2015-01-01 00:00:00");
++$m->click('SubmitTicket');
++$m->text_contains("Last Contact:  (Thu Jan 01 00:00:00 2015)", 'told date successfully updated');
++
++$m->form_name('TicketModifyAll');
++$m->field('Due_Date' => "2016-01-01 00:00:00");
++$m->click('SubmitTicket');
++$m->text_contains("Due: (Fri Jan 01 00:00:00 2016)", 'due date successfully updated');
++
++$m->get( $url . '/Ticket/ModifyAll.html?id=' . $ticket->id );
++$m->form_name('TicketModifyAll');
++$m->field(WatcherTypeEmail => 'Requestor');
++$m->field(WatcherAddressEmail => 'root at localhost');
++$m->click('SubmitTicket');
++$m->text_contains(
++    "Added principal as a Requestor for this ticket",
++    'watcher is added',
++);
++$m->form_name('TicketModifyAll');
++$m->field(WatcherTypeEmail => 'Requestor');
++$m->field(WatcherAddressEmail => 'root at localhost');
++$m->click('SubmitTicket');
++$m->text_contains(
++    "That principal is already a Requestor for this ticket",
++    'no duplicate watchers',
++);
++
++# XXX TODO test other parts, i.e. links
diff --cc rt/t/web/transaction_batch.t
index ae04e1f,ae04e1f..12d01fb
--- a/rt/t/web/transaction_batch.t
+++ b/rt/t/web/transaction_batch.t
@@@ -12,7 -12,7 +12,14 @@@ my ($val, $msg) =$s1->Create( Queue => 
               ScripAction       => 'User Defined',
               CustomIsApplicableCode => 'return ($self->TransactionObj->Field||"") eq "TimeEstimated"',
               CustomPrepareCode => 'return 1',
--             CustomCommitCode  => '$self->TicketObj->SetPriority($self->TicketObj->Priority + 2); return 1;',
++             CustomCommitCode  => '
++if ( $self->TicketObj->CurrentUser->Name ne "RT_System" ) { 
++    warn "Ticket obj has incorrect CurrentUser (should be RT_System) ".$self->TicketObj->CurrentUser->Name
++}
++if ( $self->TicketObj->QueueObj->CurrentUser->Name ne "RT_System" ) { 
++    warn "Queue obj has incorrect CurrentUser (should be RT_System) ".$self->TicketObj->QueueObj->CurrentUser->Name
++}
++$self->TicketObj->SetPriority($self->TicketObj->Priority + 2); return 1;',
               Template          => 'Blank',
               Stage             => 'TransactionBatch',
      );

commit f50a821d306b561d602edbdac0dac958b862ec0c
Author: Ivan Kohler <ivan at freeside.biz>
Date:   Thu Sep 27 20:25:27 2012 -0700

    tax debugging

diff --git a/FS/FS/cust_main/Billing.pm b/FS/FS/cust_main/Billing.pm
index bab94c3..85cafd6 100644
--- a/FS/FS/cust_main/Billing.pm
+++ b/FS/FS/cust_main/Billing.pm
@@ -1227,6 +1227,8 @@ sub _handle_taxes {
 
       $taxhash{'taxclass'} = $part_pkg->taxclass;
 
+      warn "taxhash:\n". Dumper(\%taxhash) if $DEBUG > 2;
+
       my @taxes = (); # entries are cust_main_county objects
       my %taxhash_elim = %taxhash;
       my @elim = qw( district city county state );
@@ -1246,10 +1248,12 @@ sub _handle_taxes {
 
       } while ( !scalar(@taxes) && scalar(@elim) );
 
-      @taxes = grep { ! $_->taxname or ! $self->tax_exemption($_->taxname) }
+      @taxes = grep { ! $_->taxname || ! $self->tax_exemption($_->taxname) }
                     @taxes
         if $self->cust_main_exemption; #just to be safe
 
+      warn "using taxes:\n". Dumper(@taxes) if $DEBUG > 2;
+
       # all packages now have a locationnum and should get a 
       # cust_bill_pkg_tax_location record.  The tax_locationnum
       # may be the package's locationnum, or the customer's bill 

commit 10370b0320f56af88c90e572644e91332815100f
Author: Ivan Kohler <ivan at freeside.biz>
Date:   Thu Sep 27 20:24:42 2012 -0700

    freeswitch provisioning: one file per domain, RT#18087

diff --git a/FS/FS/part_export/freeswitch.pm b/FS/FS/part_export/freeswitch.pm
index 7447849..eb490fd 100644
--- a/FS/FS/part_export/freeswitch.pm
+++ b/FS/FS/part_export/freeswitch.pm
@@ -5,7 +5,8 @@ use vars qw( %info ); # $DEBUG );
 #use Data::Dumper;
 use Tie::IxHash;
 use Text::Template;
-#use FS::Record qw( qsearch qsearchs );
+use FS::Record qw( qsearch ); #qsearchs );
+use FS::svc_phone;
 #use FS::Schema qw( dbdef );
 
 #$DEBUG = 1;
@@ -15,7 +16,7 @@ tie my %options, 'Tie::IxHash',
   'directory' => { label   => 'Directory to store FreeSWITCH account XML files',
                    default => '/usr/local/freeswitch/conf/directory/',
                  },
-  'domain'    => { label => 'Optional fixed SIP domain to use, overrides svc_phone domain', },
+  #'domain'    => { label => 'Optional fixed SIP domain to use, overrides svc_phone domain', },
   'reload'    => { label   => 'Reload command',
                    default => '/usr/local/freeswitch/bin/fs_cli -x reloadxml',
                  },
@@ -38,9 +39,9 @@ END
   'desc'    => 'Provision phone services to FreeSWITCH XML configuration files',
   'options' => \%options,
   'notes'   => <<'END',
-Export XML account configuration files to FreeSWITCH, one per phone services.
+Export XML account configuration files to FreeSWITCH, one per domain.
 <br><br>
-You will need to
+You will need to enable the svc_phone-domain configuration setting and
 <a href="http://www.freeside.biz/mediawiki/index.php/Freeside:1.9:Documentation:Administration:SSH_Keys">setup SSH for unattended operation</a>.
 END
 );
@@ -50,6 +51,33 @@ sub rebless { shift; }
 sub _export_insert {
   my( $self, $svc_phone ) = ( shift, shift );
 
+  $self->_export_rebuild_domain($svc_phone);
+
+}
+
+sub _export_replace {
+  my( $self, $new, $old ) = ( shift, shift, shift );
+
+  my $error = $self->_export_rebuild_domain($new);
+  return $error if $error;
+
+  if ( $new->domsvc ne $old->domsvc && $old->domsvc ) {
+    $error = $self->_export_rebuild_domain($old);
+    return $error if $error;
+  }
+
+  '';
+}
+
+sub _export_delete {
+  my( $self, $svc_phone ) = ( shift, shift );
+
+  $self->_export_rebuild_domain($svc_phone);
+}
+
+sub _export_rebuild_domain {
+  my($self, $svc_phone) = ( shift, shift );
+
   eval "use Net::SCP;";
   die $@ if $@;
 
@@ -57,24 +85,34 @@ sub _export_insert {
 
   my $tempdir = '%%%FREESIDE_CONF%%%/cache.'. $FS::UID::datasrc;
 
-  my $svcnum = $svc_phone->svcnum;
+  my $domain = $svc_phone->domain or return "domain required";
 
   my $fh = new File::Temp(
-    TEMPLATE => "$tempdir/freeswitch.$svcnum.XXXXXXXX",
+    TEMPLATE => "$tempdir/freeswitch.$domain.XXXXXXXX",
     DIR      => $dir,
     #UNLINK   => 0,
   );
 
-  print $fh $self->freeswitch_template_fillin( $svc_phone, 'user' )
-    or die "print to freeswitch template failed: $!";
-  close $fh;
+  print $fh qq(<domain name="$domain">\n);
+
+  my @dom_svc_phone = qsearch( 'svc_phone', { 'domsvc'=>$svc_phone->domsvc } );
+
+  foreach my $dom_svc_phone (@dom_svc_phone) {
+
+    print $fh $self->freeswitch_template_fillin( $dom_svc_phone, 'user' )
+      or die "print to freeswitch template failed: $!";
+
+  }
+
+  print $fh qq(</domain>\n);
+  $fh->flush;
 
   my $scp = new Net::SCP;
   my $user = $self->option('user')||'root';
   my $host = $self->machine;
   my $dir = $self->option('directory');
 
-  $scp->scp( $fh->filename, "$user\@$host:$dir/$svcnum.xml" )
+  $scp->scp( $fh->filename, "$user\@$host:$dir/$domain.xml" )
     or return $scp->{errstr};
 
   #signal freeswitch to reload config
@@ -84,27 +122,6 @@ sub _export_insert {
 
 }
 
-sub _export_replace {
-  my( $self, $new, $old ) = ( shift, shift, shift );
-
-  $self->_export_insert($new, @_);
-}
-
-sub _export_delete {
-  my( $self, $svc_phone ) = ( shift, shift );
-
-  my $dir  = $self->option('directory');
-  my $svcnum = $svc_phone->svcnum;
-
-  #delete file
-  $self->freeswitch_ssh( command => "rm $dir/$svcnum.xml" );
-
-  #signal freeswitch to reload config
-  $self->freeswitch_ssh( command => $self->option('reload') );
-
-  '';
-}
-
 sub freeswitch_template_fillin {
   my( $self, $svc_phone, $template ) = (shift, shift, shift);
 
@@ -117,13 +134,8 @@ sub freeswitch_template_fillin {
     DELIMITERS => [ '<%', '%>' ],
   );
 
-  my $domain =  $self->option('domain')
-             || $svc_phone->domain
-             || '$${sip_profile}';
-
   #false lazinessish w/phone_shellcommands::_export_command
   my %hash = (
-    'domain' => $domain,
     map { $_ => $svc_phone->getfield($_) } $svc_phone->fields
   );
 

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

Summary of changes:
 FS/FS/Misc.pm                                      |   10 -
 FS/FS/TicketSystem/RT_Internal.pm                  |    2 +-
 FS/FS/cust_main/Billing.pm                         |    2 +
 FS/FS/cust_pkg_discount.pm                         |    3 +-
 FS/FS/discount.pm                                  |    1 +
 FS/FS/part_event/Action/pkg_agent_credit.pm        |    3 +-
 FS/FS/part_export/freeswitch.pm                    |   84 ++++---
 FS/FS/part_export/shellcommands.pm                 |    6 +-
 FS/FS/rate.pm                                      |    2 +-
 FS/FS/svc_Tower_Mixin.pm                           |    1 -
 bin/231commit                                      |    4 +-
 bin/23diff                                         |    3 +-
 bin/cust_main-bill_now                             |    4 +-
 bin/pod2x                                          |    8 +-
 fs_selfservice/DEPLOY                              |    2 +-
 httemplate/browse/cust_note_class.html             |    2 +-
 httemplate/docs/license.html                       |    2 +-
 httemplate/edit/discount.html                      |   14 +
 httemplate/edit/process/cust_pkg_discount.html     |    3 +-
 httemplate/edit/process/quick-cust_pkg.cgi         |  151 +++++++----
 httemplate/edit/process/svc_broadband.cgi          |    7 +-
 httemplate/elements/tr-select-discount.html        |   26 ++-
 .../cust_main/custom_content/.birthdate.html.swp   |  Bin 12288 -> 0 bytes
 .../custom_content/.small_custview.html.swp        |  Bin 12288 -> 0 bytes
 .../custom_content/.spouse_birthdate.html.swp      |  Bin 12288 -> 0 bytes
 .../cust_main/custom_content/.svc_Common.html.swp  |  Bin 12288 -> 0 bytes
 .../cust_main/custom_content/.svc_acct.html.swp    |  Bin 12288 -> 0 bytes
 .../custom_content/.svc_hardware.html.swp          |  Bin 12288 -> 0 bytes
 .../cust_main/custom_content/.svc_phone.html.swp   |  Bin 12288 -> 0 bytes
 rt/sbin/rt-server.fcgi.in                          |    2 +-
 rt/sbin/rt-server.in                               |    2 +-
 rt/sbin/rt-test-dependencies.in                    |   15 +-
 rt/share/html/Search/Results.xls                   |    1 +
 rt/t/api/config.t                                  |   12 +-
 rt/t/api/template-insert.t                         |   26 --
 rt/t/api/template-simple.t                         |  275 --------------------
 rt/t/api/template.t                                |   45 ++--
 rt/t/articles/search-interface.t                   |   61 ++++-
 rt/t/articles/uri-a.t                              |   38 +++-
 rt/t/data/configs/apache2.2+fastcgi.conf.in        |    1 +
 rt/t/data/configs/apache2.2+mod_perl.conf.in       |    1 +
 rt/t/mail/dashboard-chart-with-utf8.t              |   12 +-
 rt/t/mail/dashboards.t                             |   30 +--
 rt/t/mail/gateway.t                                |   31 +++-
 rt/t/shredder/01ticket.t                           |    6 +-
 rt/t/shredder/03plugin_tickets.t                   |    5 +
 rt/t/shredder/03plugin_users.t                     |   62 +++++-
 rt/t/shredder/utils.pl                             |    2 +-
 rt/t/ticket/search_by_watcher.t                    |   10 +-
 rt/t/web/attachments.t                             |   23 ++-
 rt/t/web/command_line.t                            |    4 +-
 rt/t/web/command_line_with_unknown_field.t         |    7 +-
 rt/t/web/crypt-gnupg.t                             |    4 +
 rt/t/web/googleish_search.t                        |    6 +-
 rt/t/web/query_builder_queue_limits.t              |    3 +
 rt/t/web/search_simple.t                           |   56 ++++-
 rt/t/web/ticket_modify_all.t                       |   43 +++-
 rt/t/web/transaction_batch.t                       |    9 +-
 58 files changed, 628 insertions(+), 504 deletions(-)
 mode change 100644 => 100755 bin/cdr.import
 mode change 100644 => 100755 bin/cust_main-bill_now
 mode change 100644 => 100755 fs_selfservice/FS-SelfService/cgi/agent.cgi
 mode change 100644 => 100755 fs_selfservice/FS-SelfService/cgi/cust_bill-logo.cgi
 mode change 100644 => 100755 fs_selfservice/FS-SelfService/cgi/xmlrpc.cgi
 delete mode 100644 httemplate/view/cust_main/custom_content/.birthdate.html.swp
 delete mode 100644 httemplate/view/cust_main/custom_content/.small_custview.html.swp
 delete mode 100644 httemplate/view/cust_main/custom_content/.spouse_birthdate.html.swp
 delete mode 100644 httemplate/view/cust_main/custom_content/.svc_Common.html.swp
 delete mode 100644 httemplate/view/cust_main/custom_content/.svc_acct.html.swp
 delete mode 100644 httemplate/view/cust_main/custom_content/.svc_hardware.html.swp
 delete mode 100644 httemplate/view/cust_main/custom_content/.svc_phone.html.swp
 delete mode 100644 rt/t/api/template-insert.t
 delete mode 100644 rt/t/api/template-simple.t




More information about the freeside-commits mailing list