X-Git-Url: http://git.freeside.biz/gitweb/?p=freeside.git;a=blobdiff_plain;f=FS%2FFS%2FUI%2FWeb.pm;h=8a1d50236b24b53f2c0607e183921b9255fd5d66;hp=7926808762df4649a76a4b7e0da853d9df6316dc;hb=1fe87434632f2627de487ca2aed6cfadea2c6061;hpb=c917013f3e2232be3c3303e8cabef365096da979 diff --git a/FS/FS/UI/Web.pm b/FS/FS/UI/Web.pm index 792680876..8a1d50236 100644 --- a/FS/FS/UI/Web.pm +++ b/FS/FS/UI/Web.pm @@ -15,15 +15,87 @@ use FS::cust_main; # are sql_balance and sql_date_balance in the right module? #@ISA = qw( FS::UI ); @ISA = qw( Exporter ); -@EXPORT_OK = qw( svc_url ); +@EXPORT_OK = qw( get_page_pref set_page_pref svc_url random_id ); $DEBUG = 0; $me = '[FS::UID::Web]'; +our $NO_RANDOM_IDS; + +### +# user prefs +### + +=item get_page_pref NAME, TABLENUM + +Returns the user's page preference named NAME for the current page. If the +page is a view or edit page or otherwise shows a single record at a time, +it should use TABLENUM to link the preference to that record. + +=cut + +sub get_page_pref { + my ($prefname, $tablenum) = @_; + + my $m = $HTML::Mason::Commands::m + or die "can't get page pref when running outside the UI"; + # what's more useful: to tie prefs to the base_comp (usually where + # code is executing right now), or to the request_comp (approximately the + # one in the URL)? not sure. + $FS::CurrentUser::CurrentUser->get_page_pref( $m->request_comp->path, + $prefname, + $tablenum + ); +} + +=item set_page_pref NAME, TABLENUM, VALUE + +Sets the user's page preference named NAME for the current page. Use TABLENUM +as for get_page_pref. + +If VALUE is an empty string, the preference will be deleted (and +C will return an empty string). + + my $mypref = set_page_pref('mypref', '', 100); + +=cut + +sub set_page_pref { + my ($prefname, $tablenum, $prefvalue) = @_; + + my $m = $HTML::Mason::Commands::m + or die "can't set page pref when running outside the UI"; + $FS::CurrentUser::CurrentUser->set_page_pref( $m->request_comp->path, + $prefname, + $tablenum, + $prefvalue ); +} + ### # date parsing ### +=item parse_beginning_ending CGI [, PREFIX ] + +Parses a beginning/ending date range, as used on many reports. This function +recognizes two sets of CGI params: "begin" and "end", the integer timestamp +values, and "beginning" and "ending", the user-readable date fields. + +If "begin" contains an integer, that's passed through as the beginning date. +Otherwise, "beginning" is passed to L and turned +into an integer. If this fails or it doesn't have a value, zero is used as the +beginning date. + +The same happens for "end" and "ending", except that if "ending" contains a +date without a time, it gets moved to the end of that day, and if there's no +value, the value returned is the highest unsigned 32-bit time value (some time +in 2037). + +PREFIX is optionally a string to prepend (with '_' as a delimiter) to the form +field names. + +=cut + use Date::Parse; sub parse_beginning_ending { my($cgi, $prefix) = @_; @@ -113,16 +185,16 @@ sub svc_url { if $DEBUG; if ( $opt{m}->interp->comp_exists("/$opt{action}/$svcdb.cgi") ) { $url = "$svcdb.cgi?"; + } elsif ( $opt{m}->interp->comp_exists("/$opt{action}/$svcdb.html") ) { + $url = "$svcdb.html?"; } else { - my $generic = $opt{action} eq 'search' ? 'cust_svc' : 'svc_Common'; $url = "$generic.html?svcdb=$svcdb;"; $url .= 'svcnum=' if $query =~ /^\d+(;|$)/ or $query eq ''; } - import FS::CGI 'rooturl'; #WTF! why is this necessary - my $return = rooturl(). "$opt{action}/$url$query"; + my $return = FS::CGI::rooturl(). "$opt{action}/$url$query"; $return = qq!! if $opt{ahref}; @@ -170,7 +242,8 @@ sub svc_export_links { } sub parse_lt_gt { - my($cgi, $field) = @_; + my($cgi, $field) = (shift, shift); + my $table = ( @_ && length($_[0]) ) ? shift.'.' : ''; my @search = (); @@ -188,7 +261,7 @@ sub parse_lt_gt { my $num = $1; $num =~ s/[\,\s]+//g; - my $search = "$field $op{$op} $num"; + my $search = "$table$field $op{$op} $num"; push @search, $search; warn "found ${field}_$op field; adding search element $search\n" @@ -205,6 +278,7 @@ sub parse_lt_gt { # cust_main report subroutines ### +=over 4 =item cust_header [ CUST_FIELDS_VALUE ] @@ -225,46 +299,60 @@ sub cust_header { my %header2method = ( 'Customer' => 'name', - 'Cust. Status' => 'ucfirst_cust_status', - 'Cust#' => 'custnum', + 'Cust. Status' => 'cust_status_label', + 'Cust#' => 'display_custnum', 'Name' => 'contact', 'Company' => 'company', + + # obsolete but might still be referenced in configuration '(bill) Customer' => 'name', '(service) Customer' => 'ship_name', '(bill) Name' => 'contact', '(service) Name' => 'ship_contact', '(bill) Company' => 'company', '(service) Company' => 'ship_company', + '(bill) Day phone' => 'daytime', + '(bill) Night phone' => 'night', + '(bill) Fax number' => 'fax', + + 'Customer' => 'name', 'Address 1' => 'bill_address1', 'Address 2' => 'bill_address2', 'City' => 'bill_city', 'State' => 'bill_state', 'Zip' => 'bill_zip', - 'Country' => 'country_full', + 'Country' => 'bill_country_full', 'Day phone' => 'daytime', # XXX should use msgcat, but how? 'Night phone' => 'night', # XXX should use msgcat, but how? + 'Mobile phone' => 'mobile', # XXX should use msgcat, but how? 'Fax number' => 'fax', '(bill) Address 1' => 'bill_address1', '(bill) Address 2' => 'bill_address2', '(bill) City' => 'bill_city', '(bill) State' => 'bill_state', '(bill) Zip' => 'bill_zip', - '(bill) Country' => 'country_full', - '(bill) Day phone' => 'daytime', # XXX should use msgcat, but how? - '(bill) Night phone' => 'night', # XXX should use msgcat, but how? - '(bill) Fax number' => 'fax', + '(bill) Country' => 'bill_country_full', + '(bill) Latitude' => 'bill_latitude', + '(bill) Longitude' => 'bill_longitude', '(service) Address 1' => 'ship_address1', '(service) Address 2' => 'ship_address2', '(service) City' => 'ship_city', '(service) State' => 'ship_state', '(service) Zip' => 'ship_zip', '(service) Country' => 'ship_country_full', - '(service) Day phone' => 'ship_daytime', # XXX should use msgcat, how? - '(service) Night phone' => 'ship_night', # XXX should use msgcat, how? - '(service) Fax number' => 'ship_fax', + '(service) Latitude' => 'ship_latitude', + '(service) Longitude' => 'ship_longitude', 'Invoicing email(s)' => 'invoicing_list_emailonly_scalar', - 'Payment Type' => 'payby', + 'Contact email(s)' => 'contact_list_emailonly', + 'Invoices' => 'contact_list_cust_invoice_only', + 'Messages' => 'contact_list_cust_message_only', +# FS::Upgrade::upgrade_config removes this from existing cust-fields settings +# 'Payment Type' => 'cust_payby', 'Current Balance' => 'current_balance', + 'Agent Cust#' => 'agent_custid', + 'Agent' => 'agent_name', + 'Agent Cust# or Cust#' => 'display_custnum', + 'Advertising Source' => 'referral', ); $header2method{'Cust#'} = 'display_custnum' if $conf->exists('cust_main-default_agent_custid'); @@ -322,6 +410,14 @@ sub cust_header { @cust_header; } +sub cust_sort_fields { + cust_header(@_) if( @_ or !@cust_fields ); + #inefficientish, but tiny lists and only run once per page + + map { $_ eq 'custnum' ? 'custnum' : '' } @cust_fields; + +} + =item cust_sql_fields [ CUST_FIELDS_VALUE ] Returns a list of fields for the SELECT portion of an SQL query. @@ -337,26 +433,38 @@ sub cust_sql_fields { my @fields = qw( last first company ); # push @fields, map "ship_$_", @fields; - cust_header(@_); + cust_header(@_) if( @_ or !@cust_fields ); #inefficientish, but tiny lists and only run once per page my @location_fields; - foreach my $field (qw( address1 address2 city state zip )) { + foreach my $field (qw( address1 address2 city state zip latitude longitude )) { foreach my $pre ('bill_','ship_') { if ( grep { $_ eq $pre.$field } @cust_fields ) { push @location_fields, $pre.'location.'.$field.' AS '.$pre.$field; } } } - - push @fields, 'payby' if grep { $_ eq 'payby'} @cust_fields; + foreach my $pre ('bill_','ship_') { + if ( grep { $_ eq $pre.'country_full' } @cust_fields ) { + push @location_fields, $pre.'locationnum'; + } + } + + foreach my $field (qw(daytime night mobile fax )) { + push @fields, $field if (grep { $_ eq $field } @cust_fields); + } push @fields, 'agent_custid'; + push @fields, 'agentnum' if grep { $_ eq 'agent_name' } @cust_fields; + my @extra_fields = (); if (grep { $_ eq 'current_balance' } @cust_fields) { push @extra_fields, FS::cust_main->balance_sql . " AS current_balance"; } + push @extra_fields, 'part_referral_x.referral AS referral' + if grep { $_ eq 'referral' } @cust_fields; + map("cust_main.$_", @fields), @location_fields, @extra_fields; } @@ -421,6 +529,10 @@ sub join_cust_main { " ON (ship_location.locationnum = $location_table.$locationnum) "; } + if ( !@cust_fields or grep { $_ eq 'referral' } @cust_fields ) { + $sql .= ' LEFT JOIN (select refnum, referral from part_referral) AS part_referral_x ON (cust_main.refnum = part_referral_x.refnum) '; + } + $sql; } @@ -470,25 +582,29 @@ element. sub cust_fields_subs { my $unlinked_warn = 0; + return map { my $f = $_; - if( $unlinked_warn++ ) { + if ( $unlinked_warn++ ) { + sub { my $record = shift; - if( $record->custnum ) { - $record->$f(@_); - } - else { + if ( $record->custnum ) { + encode_entities( $record->$f(@_) ); + } else { '(unlinked)' }; - } - } - else { + }; + + } else { + sub { my $record = shift; - $record->$f(@_) if $record->custnum; - } + $record->custnum ? encode_entities( $record->$f(@_) ) : ''; + }; + } + } @cust_fields; } @@ -553,6 +669,19 @@ sub cust_aligns { } } +=item cust_links + +Returns an array of links to view/cust_main.cgi, for use with cust_fields. + +=cut + +sub cust_links { + my $link = [ FS::CGI::rooturl().'view/cust_main.cgi?', 'custnum' ]; + + return map { $_ eq 'cust_status_label' ? '' : $link } + @cust_fields; +} + =item is_mobile Utility function to determine if the client is a mobile browser. @@ -566,7 +695,41 @@ sub is_mobile { } return 0; } - + +=item random_id [ DIGITS ] + +Returns a random number of length DIGITS, or if unspecified, a long random +identifier consisting of the timestamp, process ID, and a random number. +Anything in the UI that needs a random identifier should use this. + +=cut + +sub random_id { + my $digits = shift; + if (!defined $NO_RANDOM_IDS) { + my $conf = FS::Conf->new; + $NO_RANDOM_IDS = $conf->exists('no_random_ids') ? 1 : 0; + warn "TEST MODE--RANDOM ID NUMBERS DISABLED\n" if $NO_RANDOM_IDS; + } + if ( $NO_RANDOM_IDS ) { + if ( $digits > 0 ) { + return 0; + } else { + return '0000000000-0000-000000000.000000'; + } + } else { + if ($digits > 0) { + return int(rand(10 ** $digits)); + } else { + return time . "-$$-" . rand() * 2**32; + } + } +} + +=back + +=cut + ### # begin JSRPC code... ### @@ -578,11 +741,12 @@ use vars qw($DEBUG); use Carp; use Storable qw(nfreeze); use MIME::Base64; -use JSON; -use FS::UID qw(getotaker); +use Cpanel::JSON::XS; +use FS::CurrentUser; use FS::Record qw(qsearchs); use FS::queue; use FS::CGI qw(rooturl); +use FS::Report::Queued::FutureAutobill; $DEBUG = 0; @@ -653,7 +817,7 @@ sub start_job { push @{$param{$field}}, $value; } } - $param{CurrentUser} = getotaker(); + $param{CurrentUser} = $FS::CurrentUser::CurrentUser->username; $param{RootURL} = rooturl($self->{cgi}->self_url); warn "FS::UI::Web::start_job\n". join('', map { @@ -673,10 +837,9 @@ sub start_job { #too slow to insert all the cgi params as individual args..,? #my $error = $queue->insert('_JOB', $cgi->Vars); - #warn 'froze string of size '. length(nfreeze(\%param)). " for job args\n" - # if $DEBUG; + #rely on FS::queue smartness to freeze/encode the param hash - my $error = $job->insert( '_JOB', encode_base64(nfreeze(\%param)) ); + my $error = $job->insert( '_JOB', \%param ); if ( $error ) { @@ -723,10 +886,7 @@ sub job_status { @return = ( 'error', $job ? $job->statustext : $jobnum ); } - #to_json(\@return); #waiting on deb 5.0 for new JSON.pm? - #silence the warning though - my $to_json = JSON->can('to_json') || JSON->can('objToJson'); - &$to_json(\@return); + encode_json \@return; }