discounts, RT#6679
[freeside.git] / FS / FS / Mason.pm
1 package FS::Mason;
2
3 use strict;
4 use vars qw( @ISA @EXPORT_OK $addl_handler_use );
5 use Exporter;
6 use File::Slurp qw( slurp );
7 use HTML::Mason 1.27; #http://www.masonhq.com/?ApacheModPerl2Redirect
8 use HTML::Mason::Interp;
9 use HTML::Mason::Compiler::ToObject;
10
11 @ISA = qw( Exporter );
12 @EXPORT_OK = qw( mason_interps );
13
14 =head1 NAME
15
16 FS::Mason - Initialize the Mason environment
17
18 =head1 SYNOPSIS
19
20   use FS::Mason qw( mason_interps );
21
22   my( $fs_interp, $rt_interp ) = mason_interps('apache');
23
24   #OR
25
26   my( $fs_interp, $rt_interp ) = mason_interps('standalone'); #XXX name?
27
28 =head1 DESCRIPTION
29
30 Initializes the Mason environment, loads all Freeside and RT libraries, etc.
31
32 =cut
33
34 $addl_handler_use = '';
35 my $addl_handler_use_file = '%%%FREESIDE_CONF%%%/addl_handler_use.pl';
36 if ( -e $addl_handler_use_file ) {
37   $addl_handler_use = slurp( $addl_handler_use_file );
38 }
39
40 # List of modules that you want to use from components (see Admin
41 # manual for details)
42 {
43   package HTML::Mason::Commands;
44
45   use strict;
46   use vars qw( %session );
47   use CGI 3.29 qw(-private_tempfiles); #3.29 to fix RT attachment problems
48
49   #breaks quick payment entry
50   #http://rt.cpan.org/Public/Bug/Display.html?id=37365
51   die "CGI.pm v3.38 is broken, use any other version >= 3.29".
52       " (Debian 5.0?  aptitude remove libcgi-pm-perl)"
53     if $CGI::VERSION == 3.38;
54
55   #use CGI::Carp qw(fatalsToBrowser);
56   use CGI::Cookie;
57   use List::Util qw( max min );
58   use Data::Dumper;
59   use Date::Format;
60   use Date::Parse;
61   use Time::Local;
62   use Time::HiRes;
63   use Time::Duration;
64   use DateTime;
65   use DateTime::Format::Strptime;
66   use Lingua::EN::Inflect qw(PL);
67   use Tie::IxHash;
68   use URI;
69   use URI::Escape;
70   use HTML::Entities;
71   use HTML::TreeBuilder;
72   use HTML::FormatText;
73   use JSON;
74   use MIME::Base64;
75   use IO::Handle;
76   use IO::File;
77   use IO::Scalar;
78   #not actually using this yet anyway...# use IPC::Run3 0.036;
79   use Net::Whois::Raw qw(whois);
80   if ( $] < 5.006 ) {
81     eval "use Net::Whois::Raw 0.32 qw(whois)";
82     die $@ if $@;
83   }
84   use Text::CSV_XS;
85   use Spreadsheet::WriteExcel;
86   use Business::CreditCard 0.30; #for mask-aware cardtype()
87   use NetAddr::IP;
88   use Net::Ping;
89   use Net::Ping::External;
90   #if CPAN #7815 ever gets fixed# if ( $Net::Ping::External::VERSION <= 0.12 )
91   {
92     no warnings 'redefine';
93     eval 'sub Net::Ping::External::_ping_linux { 
94             my %args = @_;
95             my $command = "ping -s $args{size} -c $args{count} -w $args{timeout} $args{host}";
96             return Net::Ping::External::_ping_system($command, 0);
97           }
98          ';
99     die $@ if $@;
100   }
101   use String::Approx qw(amatch);
102   use Chart::LinesPoints;
103   use Chart::Mountain;
104   use Color::Scheme;
105   use HTML::Widgets::SelectLayers 0.07; #should go away in favor of
106                                         #selectlayers.html
107   use Locale::Country;
108   use Business::US::USPS::WebTools::AddressStandardization;
109   use FS;
110   use FS::UID qw( getotaker dbh datasrc driver_name );
111   use FS::Record qw( qsearch qsearchs fields dbdef
112                     str2time_sql str2time_sql_closing
113                    );
114   use FS::Conf;
115   use FS::CGI qw(header menubar table itable ntable idiot
116                  eidiot myexit http_header);
117   use FS::UI::Web qw(svc_url);
118   use FS::UI::Web::small_custview qw(small_custview);
119   use FS::UI::bytecount;
120   use FS::Msgcat qw(gettext geterror);
121   use FS::Misc qw( send_email send_fax
122                    states_hash counties cities state_label
123                  );
124   use FS::Misc::eps2png qw( eps2png );
125   use FS::Report::Table::Monthly;
126   use FS::TicketSystem;
127   use FS::Tron qw( tron_lint );
128
129   use FS::agent;
130   use FS::agent_type;
131   use FS::domain_record;
132   use FS::cust_bill;
133   use FS::cust_bill_pay;
134   use FS::cust_credit;
135   use FS::cust_credit_bill;
136   use FS::cust_main qw(smart_search);
137   use FS::cust_main::Import;
138   use FS::cust_main_county;
139   use FS::cust_location;
140   use FS::cust_pay;
141   use FS::cust_pkg;
142   use FS::part_pkg_taxclass;
143   use FS::cust_pkg_reason;
144   use FS::cust_refund;
145   use FS::cust_credit_refund;
146   use FS::cust_pay_refund;
147   use FS::cust_svc;
148   use FS::nas;
149   use FS::part_bill_event;
150   use FS::part_event;
151   use FS::part_event_condition;
152   use FS::part_pkg;
153   use FS::part_referral;
154   use FS::part_svc;
155   use FS::part_svc_router;
156   use FS::part_virtual_field;
157   use FS::pay_batch;
158   use FS::pkg_svc;
159   use FS::port;
160   use FS::queue qw(joblisting);
161   use FS::raddb;
162   use FS::session;
163   use FS::svc_acct;
164   use FS::svc_acct_pop qw(popselector);
165   use FS::acct_rt_transaction;
166   use FS::svc_domain;
167   use FS::svc_forward;
168   use FS::svc_www;
169   use FS::router;
170   use FS::addr_block;
171   use FS::svc_broadband;
172   use FS::svc_external;
173   use FS::type_pkgs;
174   use FS::part_export;
175   use FS::part_export_option;
176   use FS::export_svc;
177   use FS::msgcat;
178   use FS::rate;
179   use FS::rate_region;
180   use FS::rate_prefix;
181   use FS::rate_detail;
182   use FS::usage_class;
183   use FS::payment_gateway;
184   use FS::agent_payment_gateway;
185   use FS::XMLRPC;
186   use FS::payby;
187   use FS::cdr;
188   use FS::cdr_batch;
189   use FS::inventory_class;
190   use FS::inventory_item;
191   use FS::pkg_category;
192   use FS::pkg_class;
193   use FS::access_user;
194   use FS::access_user_pref;
195   use FS::access_group;
196   use FS::access_usergroup;
197   use FS::access_groupagent;
198   use FS::access_right;
199   use FS::AccessRight;
200   use FS::svc_phone;
201   use FS::phone_device;
202   use FS::part_device;
203   use FS::reason_type;
204   use FS::reason;
205   use FS::cust_main_note;
206   use FS::tax_class;
207   use FS::cust_tax_location;
208   use FS::part_pkg_taxproduct;
209   use FS::part_pkg_taxoverride;
210   use FS::part_pkg_taxrate;
211   use FS::tax_rate;
212   use FS::part_pkg_report_option;
213   use FS::cust_attachment;
214   use FS::h_cust_pkg;
215   use FS::h_svc_acct;
216   use FS::h_svc_broadband;
217   use FS::h_svc_domain;
218   #use FS::h_domain_record;
219   use FS::h_svc_external;
220   use FS::h_svc_forward;
221   use FS::h_svc_phone;
222   #use FS::h_phone_device;
223   use FS::h_svc_www;
224   use FS::cust_statement;
225   use FS::cust_class;
226   use FS::cust_category;
227   use FS::prospect_main;
228   use FS::contact;
229   use FS::svc_pbx;
230   use FS::discount;
231   # Sammath Naur
232
233   if ( $FS::Mason::addl_handler_use ) {
234     eval $FS::Mason::addl_handler_use;
235     die $@ if $@;
236   }
237
238   if ( %%%RT_ENABLED%%% ) {
239     eval '
240       use lib ( "/opt/rt3/local/lib", "/opt/rt3/lib" );
241       use vars qw($Nobody $SystemUser);
242       use RT;
243       use RT::Tickets;
244       use RT::Transactions;
245       use RT::Users;
246       use RT::CurrentUser;
247       use RT::Templates;
248       use RT::Queues;
249       use RT::ScripActions;
250       use RT::ScripConditions;
251       use RT::Scrips;
252       use RT::Groups;
253       use RT::GroupMembers;
254       use RT::CustomFields;
255       use RT::CustomFieldValues;
256       use RT::ObjectCustomFieldValues;
257
258       #blah.  manually updated from RT::Interface::Web::Handler
259       use RT::Interface::Web;
260       use MIME::Entity;
261       use Text::Wrapper;
262       use Time::ParseDate;
263       use Time::HiRes;
264       use HTML::Scrubber;
265
266       #blah.  not even in RT::Interface::Web::Handler, just in 
267       #html/NoAuth/css/dhandler and rt-test-dependencies.  ask for it here
268       #to throw a real error instead of just a mysterious unstyled RT
269       use CSS::Squish 0.06;
270
271       use RT::Interface::Web::Request;
272
273       #slow, unreliable, segfaults and is optional
274       #see rt/html/Ticket/Elements/ShowTransactionAttachments
275       #use Text::Quoted;
276
277       #?#use File::Path qw( rmtree );
278       #?#use File::Glob qw( bsd_glob );
279       #?#use File::Spec::Unix;
280
281     ';
282     die $@ if $@;
283   }
284
285   *CGI::redirect = sub {
286     my $self = shift;
287     my $cookie = '';
288     if ( $_[0] eq '-cookie' ) { #this isn't actually used at the moment
289       (my $x, $cookie) = (shift, shift);
290       $HTML::Mason::r->err_headers_out->add( 'Set-cookie' => $cookie );
291     }
292     my $location = shift;
293
294     use vars qw($m);
295
296     # false laziness w/below
297     if ( defined(@DBIx::Profile::ISA) ) {
298
299       if ( $FS::CurrentUser::CurrentUser->option('show_db_profile') ) {
300
301         #profiling redirect
302
303         my $page =
304           qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A>!.
305           '<BR><BR><PRE>'.
306             ( UNIVERSAL::can(dbh, 'sprintProfile')
307                 ? encode_entities(dbh->sprintProfile())
308                 : 'DBIx::Profile missing sprintProfile method;'.
309                   'unpatched or too old?'                        ).
310           #"\n\n". &sprintAutoProfile().  '</PRE>'.
311           "\n\n".                         '</PRE>'.
312           '</BODY></HTML>';
313
314
315         dbh->{'private_profile'} = {};
316         return $page;
317
318       } else {
319
320         #clear db profile, but normal redirect
321         dbh->{'private_profile'} = {};
322         $m->redirect($location);
323         '';
324
325       }
326
327     } else { #normal redirect
328
329       $m->redirect($location);
330       '';
331
332     }
333
334   };
335   
336   sub include {
337     use vars qw($m);
338     $m->scomp(@_);
339   }
340
341   sub errorpage {
342     use vars qw($m);
343     $m->comp('/elements/errorpage.html', @_);
344   }
345
346   sub redirect {
347     my( $location ) = @_;
348     use vars qw($m);
349     $m->clear_buffer;
350     #false laziness w/above
351     if ( defined(@DBIx::Profile::ISA) ) {
352
353       if ( $FS::CurrentUser::CurrentUser->option('show_db_profile') ) {
354
355         #profiling redirect
356
357         $m->print(
358           qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A>!.
359           '<BR><BR><PRE>'.
360             ( UNIVERSAL::can(dbh, 'sprintProfile')
361                 ? encode_entities(dbh->sprintProfile())
362                 : 'DBIx::Profile missing sprintProfile method;'.
363                   'unpatched or too old?'                        ).
364           #"\n\n". &sprintAutoProfile().  '</PRE>'.
365           "\n\n".                         '</PRE>'.
366           '</BODY></HTML>'
367         );
368
369         dbh->{'private_profile'} = {};
370
371       } else {
372
373         #clear db profile, but normal redirect
374         dbh->{'private_profile'} = {};
375         $m->redirect($location);
376
377       }
378
379     } else { #normal redirect
380
381       $m->redirect($location);
382
383     }
384
385   }
386
387 } # end package HTML::Mason::Commands;
388
389 =head1 SUBROUTINE
390
391 =over 4
392
393 =item mason_interps [ MODE [ OPTION => VALUE ... ] ]
394
395 Returns a list consisting of two HTML::Mason::Interp objects, the first for
396 Freeside pages, and the second for RT pages.
397
398 MODE can be 'apache' or 'standalone'.  If not specified, defaults to 'apache'.
399
400 Options and values can be passed following mode.  Currently available options
401 are:
402
403 I<outbuf> should be set to a scalar reference in standalone mode.
404
405 =cut
406
407 sub mason_interps {
408   my $mode = shift || 'apache';
409   my %opt = @_;
410
411   #my $request_class = 'HTML::Mason::Request'.
412                       #( $mode eq 'apache' ? '::ApacheHandler' : '' );
413   my $request_class = 'FS::Mason::Request';
414
415   #not entirely sure it belongs here, but what the hey
416   if ( %%%RT_ENABLED%%% && $mode ne 'standalone' ) {
417     RT::LoadConfig();
418   }
419
420   # A hook supporting strange legacy ways people have added stuff on
421
422   my @addl_comp_root = ();
423   my $addl_comp_root_file = '%%%FREESIDE_CONF%%%/addl_comp_root.pl';
424   if ( -e $addl_comp_root_file ) {
425     warn "reading $addl_comp_root_file\n";
426     my $text = slurp( $addl_comp_root_file );
427     my @addl = eval $text;
428     if ( @addl && ! $@ ) {
429       @addl_comp_root = @addl;
430     } elsif ($@) {
431       warn "error parsing $addl_comp_root_file: $@\n";
432     }
433   }
434
435   my %interp = (
436     request_class        => $request_class,
437     data_dir             => '%%%MASONDATA%%%',
438     error_mode           => 'output',
439     error_format         => 'html',
440     ignore_warnings_expr => '.',
441     comp_root            => [
442                               [ 'freeside'=>'%%%FREESIDE_DOCUMENT_ROOT%%%'    ],
443                               [ 'rt'      =>'%%%FREESIDE_DOCUMENT_ROOT%%%/rt' ],
444                               @addl_comp_root,
445                             ],
446   );
447
448   $interp{out_method} = $opt{outbuf} if $mode eq 'standalone' && $opt{outbuf};
449
450   my $fs_interp = new HTML::Mason::Interp (
451     %interp,
452     escape_flags => { 'js_string' => sub {
453                         #${$_[0]} =~ s/(['\\\n])/'\\'.($1 eq "\n" ? 'n' : $1)/ge;
454                         ${$_[0]} =~ s/(['\\])/\\$1/g;
455                         ${$_[0]} =~ s/\n/\\n/g;
456                         ${$_[0]} = "'". ${$_[0]}. "'";
457                       }
458                     },
459     compiler     => HTML::Mason::Compiler::ToObject->new(
460                       allow_globals        => [qw(%session)],
461                     ),
462   );
463
464   my $rt_interp = new HTML::Mason::Interp (
465     %interp,
466     escape_flags => { 'h' => \&RT::Interface::Web::EscapeUTF8 },
467     compiler     => HTML::Mason::Compiler::ToObject->new(
468                       default_escape_flags => 'h',
469                       allow_globals        => [qw(%session)],
470                     ),
471   );
472
473   ( $fs_interp, $rt_interp );
474
475 }
476
477 =back
478
479 =head1 BUGS
480
481 Lurking in the darkness...
482
483 =head1 SEE ALSO
484
485 L<HTML::Mason>, L<FS>, L<RT>
486
487 =cut
488
489 1;