initial svc_pbx implementation, RT#7051
[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   # Sammath Naur
231
232   if ( $FS::Mason::addl_handler_use ) {
233     eval $FS::Mason::addl_handler_use;
234     die $@ if $@;
235   }
236
237   if ( %%%RT_ENABLED%%% ) {
238     eval '
239       use lib ( "/opt/rt3/local/lib", "/opt/rt3/lib" );
240       use vars qw($Nobody $SystemUser);
241       use RT;
242       use RT::Tickets;
243       use RT::Transactions;
244       use RT::Users;
245       use RT::CurrentUser;
246       use RT::Templates;
247       use RT::Queues;
248       use RT::ScripActions;
249       use RT::ScripConditions;
250       use RT::Scrips;
251       use RT::Groups;
252       use RT::GroupMembers;
253       use RT::CustomFields;
254       use RT::CustomFieldValues;
255       use RT::ObjectCustomFieldValues;
256
257       #blah.  manually updated from RT::Interface::Web::Handler
258       use RT::Interface::Web;
259       use MIME::Entity;
260       use Text::Wrapper;
261       use Time::ParseDate;
262       use Time::HiRes;
263       use HTML::Scrubber;
264
265       #blah.  not even in RT::Interface::Web::Handler, just in 
266       #html/NoAuth/css/dhandler and rt-test-dependencies.  ask for it here
267       #to throw a real error instead of just a mysterious unstyled RT
268       use CSS::Squish 0.06;
269
270       use RT::Interface::Web::Request;
271
272       #slow, unreliable, segfaults and is optional
273       #see rt/html/Ticket/Elements/ShowTransactionAttachments
274       #use Text::Quoted;
275
276       #?#use File::Path qw( rmtree );
277       #?#use File::Glob qw( bsd_glob );
278       #?#use File::Spec::Unix;
279
280     ';
281     die $@ if $@;
282   }
283
284   *CGI::redirect = sub {
285     my $self = shift;
286     my $cookie = '';
287     if ( $_[0] eq '-cookie' ) { #this isn't actually used at the moment
288       (my $x, $cookie) = (shift, shift);
289       $HTML::Mason::r->err_headers_out->add( 'Set-cookie' => $cookie );
290     }
291     my $location = shift;
292
293     use vars qw($m);
294
295     # false laziness w/below
296     if ( defined(@DBIx::Profile::ISA) ) {
297
298       if ( $FS::CurrentUser::CurrentUser->option('show_db_profile') ) {
299
300         #profiling redirect
301
302         my $page =
303           qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A>!.
304           '<BR><BR><PRE>'.
305             ( UNIVERSAL::can(dbh, 'sprintProfile')
306                 ? encode_entities(dbh->sprintProfile())
307                 : 'DBIx::Profile missing sprintProfile method;'.
308                   'unpatched or too old?'                        ).
309           #"\n\n". &sprintAutoProfile().  '</PRE>'.
310           "\n\n".                         '</PRE>'.
311           '</BODY></HTML>';
312
313
314         dbh->{'private_profile'} = {};
315         return $page;
316
317       } else {
318
319         #clear db profile, but normal redirect
320         dbh->{'private_profile'} = {};
321         $m->redirect($location);
322         '';
323
324       }
325
326     } else { #normal redirect
327
328       $m->redirect($location);
329       '';
330
331     }
332
333   };
334   
335   sub include {
336     use vars qw($m);
337     $m->scomp(@_);
338   }
339
340   sub errorpage {
341     use vars qw($m);
342     $m->comp('/elements/errorpage.html', @_);
343   }
344
345   sub redirect {
346     my( $location ) = @_;
347     use vars qw($m);
348     $m->clear_buffer;
349     #false laziness w/above
350     if ( defined(@DBIx::Profile::ISA) ) {
351
352       if ( $FS::CurrentUser::CurrentUser->option('show_db_profile') ) {
353
354         #profiling redirect
355
356         $m->print(
357           qq!<HTML><BODY>Redirect to <A HREF="$location">$location</A>!.
358           '<BR><BR><PRE>'.
359             ( UNIVERSAL::can(dbh, 'sprintProfile')
360                 ? encode_entities(dbh->sprintProfile())
361                 : 'DBIx::Profile missing sprintProfile method;'.
362                   'unpatched or too old?'                        ).
363           #"\n\n". &sprintAutoProfile().  '</PRE>'.
364           "\n\n".                         '</PRE>'.
365           '</BODY></HTML>'
366         );
367
368         dbh->{'private_profile'} = {};
369
370       } else {
371
372         #clear db profile, but normal redirect
373         dbh->{'private_profile'} = {};
374         $m->redirect($location);
375
376       }
377
378     } else { #normal redirect
379
380       $m->redirect($location);
381
382     }
383
384   }
385
386 } # end package HTML::Mason::Commands;
387
388 =head1 SUBROUTINE
389
390 =over 4
391
392 =item mason_interps [ MODE [ OPTION => VALUE ... ] ]
393
394 Returns a list consisting of two HTML::Mason::Interp objects, the first for
395 Freeside pages, and the second for RT pages.
396
397 MODE can be 'apache' or 'standalone'.  If not specified, defaults to 'apache'.
398
399 Options and values can be passed following mode.  Currently available options
400 are:
401
402 I<outbuf> should be set to a scalar reference in standalone mode.
403
404 =cut
405
406 sub mason_interps {
407   my $mode = shift || 'apache';
408   my %opt = @_;
409
410   #my $request_class = 'HTML::Mason::Request'.
411                       #( $mode eq 'apache' ? '::ApacheHandler' : '' );
412   my $request_class = 'FS::Mason::Request';
413
414   #not entirely sure it belongs here, but what the hey
415   if ( %%%RT_ENABLED%%% && $mode ne 'standalone' ) {
416     RT::LoadConfig();
417   }
418
419   # A hook supporting strange legacy ways people have added stuff on
420
421   my @addl_comp_root = ();
422   my $addl_comp_root_file = '%%%FREESIDE_CONF%%%/addl_comp_root.pl';
423   if ( -e $addl_comp_root_file ) {
424     warn "reading $addl_comp_root_file\n";
425     my $text = slurp( $addl_comp_root_file );
426     my @addl = eval $text;
427     if ( @addl && ! $@ ) {
428       @addl_comp_root = @addl;
429     } elsif ($@) {
430       warn "error parsing $addl_comp_root_file: $@\n";
431     }
432   }
433
434   my %interp = (
435     request_class        => $request_class,
436     data_dir             => '%%%MASONDATA%%%',
437     error_mode           => 'output',
438     error_format         => 'html',
439     ignore_warnings_expr => '.',
440     comp_root            => [
441                               [ 'freeside'=>'%%%FREESIDE_DOCUMENT_ROOT%%%'    ],
442                               [ 'rt'      =>'%%%FREESIDE_DOCUMENT_ROOT%%%/rt' ],
443                               @addl_comp_root,
444                             ],
445   );
446
447   $interp{out_method} = $opt{outbuf} if $mode eq 'standalone' && $opt{outbuf};
448
449   my $fs_interp = new HTML::Mason::Interp (
450     %interp,
451     escape_flags => { 'js_string' => sub {
452                         #${$_[0]} =~ s/(['\\\n])/'\\'.($1 eq "\n" ? 'n' : $1)/ge;
453                         ${$_[0]} =~ s/(['\\])/\\$1/g;
454                         ${$_[0]} =~ s/\n/\\n/g;
455                         ${$_[0]} = "'". ${$_[0]}. "'";
456                       }
457                     },
458     compiler     => HTML::Mason::Compiler::ToObject->new(
459                       allow_globals        => [qw(%session)],
460                     ),
461   );
462
463   my $rt_interp = new HTML::Mason::Interp (
464     %interp,
465     escape_flags => { 'h' => \&RT::Interface::Web::EscapeUTF8 },
466     compiler     => HTML::Mason::Compiler::ToObject->new(
467                       default_escape_flags => 'h',
468                       allow_globals        => [qw(%session)],
469                     ),
470   );
471
472   ( $fs_interp, $rt_interp );
473
474 }
475
476 =back
477
478 =head1 BUGS
479
480 Lurking in the darkness...
481
482 =head1 SEE ALSO
483
484 L<HTML::Mason>, L<FS>, L<RT>
485
486 =cut
487
488 1;