File Coverage

blib/lib/XAO/DO/Web/Utility.pm
Criterion Covered Total %
statement 39 198 19.7
branch 17 110 15.4
condition 11 67 16.4
subroutine 9 18 50.0
pod 8 9 88.8
total 84 402 20.9


line stmt bran cond sub pod time code
1             =head1 NAME
2              
3             XAO::DO::Web::Utility - Miscellaneous utility displayable functions
4              
5             =head1 SYNOPSIS
6              
7             Currently is only useful in XAO::Web site context.
8              
9             =head1 DESCRIPTION
10              
11             This is a collection of various functions that do not fit well into
12             other objects and are not worth creating separate objects for them (at
13             least at present time).
14              
15             =head1 METHODS
16              
17             Utility object is based on Action object (see L)
18             and therefor what it does depends on the "mode" argument.
19              
20             For each mode there is a separate method with usually very similar
21             name. See below for the list of mode names and their method
22             counterparts.
23              
24             =over
25              
26             =cut
27              
28             ###############################################################################
29             package XAO::DO::Web::Utility;
30 1     1   799 use strict;
  1         2  
  1         36  
31 1     1   6 use POSIX qw(mktime);
  1         2  
  1         7  
32 1     1   78 use XAO::Utils qw(:args :debug :html);
  1         2  
  1         167  
33 1     1   7 use XAO::Objects;
  1         2  
  1         40  
34 1     1   6 use base XAO::Objects->load(objname => 'Web::Action');
  1         4  
  1         4  
35              
36             our $VERSION='2.003';
37              
38             sub check_mode ($$) {
39 7     7 0 12 my $self=shift;
40 7         18 my $args=get_args(\@_);
41 7         63 my $mode=$args->{mode};
42 7 50       51 if($mode eq "select-time-range") {
    50          
    50          
    50          
    50          
    50          
    50          
    50          
43 0         0 $self->select_time_range($args);
44             }
45             elsif($mode eq "tracking-url") {
46 0         0 $self->tracking_url($args);
47             }
48             elsif($mode eq "config-param") {
49 0         0 $self->config_param($args);
50             }
51             elsif($mode eq "pass-cgi-params") {
52 0         0 $self->pass_cgi_params($args);
53             }
54             elsif($mode eq "current-url") {
55 0         0 $self->show_current_url($args);
56             }
57             elsif($mode eq "base-url") {
58 0         0 $self->show_base_url($args);
59             }
60             elsif($mode eq "show-pagedesc") {
61 0         0 $self->show_pagedesc($args);
62             }
63             elsif($mode eq "number-ordinal-suffix") {
64 7         23 $self->number_ordinal_suffix($args);
65             }
66             else {
67 0         0 $self->throw("check_mode - Unknown mode '$mode'");
68             }
69             }
70              
71             ###############################################################################
72              
73             =item 'tracking-url' => tracking_url (%)
74              
75             Displays tracking URL for given carrier and tracking number.
76              
77             Arguments are "carrier" and "tracknum". Supported carriers are:
78              
79             =over
80              
81             =item * 'usps'
82              
83             =item * 'ups'
84              
85             =item * 'fedex'
86              
87             =item * 'dhl'
88              
89             =item * 'yellow' (see http://www.yellowcorp.com/)
90              
91             =item * 'sefl' (see https://www.sefl.com/)
92              
93             =back
94              
95             Example:
96              
97             <%Utility mode="tracking-url" carrier="usps" tracknum="VV1234567890"%>
98              
99             Would display:
100              
101             http://www.framed.usps.com/cgi-bin/cttgate/ontrack.cgi?tracknbr=VV1234567890
102              
103             =cut
104              
105             sub tracking_url ($%) {
106 0     0 1 0 my $self=shift;
107 0         0 my $args=get_args(\@_);
108              
109 0   0     0 my $carrier=$args->{'carrier'} || '';
110 0   0     0 my $tracknum=$args->{tracknum} || '';
111              
112 0         0 my $url;
113              
114 0 0       0 if(lc($carrier) eq 'usps') {
    0          
    0          
    0          
    0          
    0          
115 0         0 $url='https://tools.usps.com/go/TrackConfirmAction.action?tRef=fullpage&tLc=1&tLabels=' . t2hq($tracknum);
116             }
117             elsif(lc($carrier) eq 'ups') {
118 0         0 $url='http://wwwapps.ups.com/etracking/tracking.cgi?tracknum=' . t2hq($tracknum);
119             }
120             elsif(lc($carrier) eq 'fedex') {
121 0         0 $url='https://www.fedex.com/apps/fedextrack/?action=track&tracknumbers=' . t2hq($tracknum);
122             }
123             elsif(lc($carrier) eq 'dhl') {
124 0         0 $url='http://www.dhl-usa.com/cgi-bin/tracking.pl' .
125             '?AWB=' . t2hq($tracknum) .
126             'LAN=ENG&TID=US_ENG&FIRST_DB=US';
127             }
128             elsif(lc($carrier) eq 'yellow') {
129 0         0 $tracknum=sprintf('%09u',int($tracknum));
130 0         0 $url='http://www2.yellowcorp.com/cgi-bin/gx.cgi/applogic+yfsgentracing.E000YfsTrace' .
131             '?diff=protrace&PRONumber=' . t2hq($tracknum);
132             }
133             elsif(lc($carrier) eq 'sefl') {
134 0         0 $tracknum =~ s/\D//g;
135 0         0 $url='https://www.sefl.com/webconnect/tracing?Type=PN&RefNum1=' . t2hq($tracknum);
136             }
137             else {
138 0         0 eprint "Unknown carrier '$carrier'";
139 0         0 $url='';
140             }
141              
142 0         0 $self->textout($url);
143             }
144              
145             ###############################################################################
146              
147             =item 'config-param' => config_param (%)
148              
149             Displays site configuration parameter with given "name". Example:
150              
151             <%Utility mode="config-param" name="customer_support" default="aa@bb.com"%>
152              
153             Would display whatever is set in site's Config.pm modules for variable
154             "customer_support" or "aa@bb.com" if it is not set.
155              
156             =cut
157              
158             sub config_param ($%)
159 0     0 1 0 { my $self=shift;
160 0         0 my $args=get_args(\@_);
161 0         0 my $config=$self->siteconfig;
162 0 0       0 $args->{name} || throw XAO::E::DO::Web::Utility
163             "config_param - no 'name' given";
164 0         0 my $value=$config->get($args->{name});
165 0 0 0     0 $value=$args->{default} if !defined($value) && defined($args->{default});
166 0 0       0 $self->textout($value) if defined $value;
167             }
168              
169             ###############################################################################
170              
171             =item 'pass-cgi-params' => pass_cgi_params (%)
172              
173             Builds a piece of HTML code containing current CGI parameters in either
174             form or query formats depending on "result" argument (values are "form"
175             or "query" respectfully).
176              
177             List of parameters to be copied must be in "params" arguments and may
178             end with asterisk (*) to include parameters by template. In addition
179             to that you can exclude some parameters that wer listed in "params" by
180             putting their names (or name templates) into "except" argument.
181              
182             Form example:
183              
184            
185             <%Utility mode="pass-cgi-params" result="form" params="aa,bb,cc"%>
186            
187            
188              
189             Would produce:
190              
191            
192            
193            
194            
195            
196            
197              
198             Actual output would be slightly different because no carriage return
199             symbol would be inserted between hidden tags. This is done for
200             rare situations when your code is space sensitive and you do not want to
201             mess it up.
202              
203             Query example:
204              
205             206             mode="pass-cgi-params"
207             result="query"
208             params="*"
209             except="sortby"
210             %>">Sort by price
211              
212             If current page arguments were "sku=123&category=234&sortby=vendor" then
213             the output would be:
214              
215             Sort by price
216              
217             For 'query' results it is convenient to also provide a 'prefix'
218             parameter that would be included in the output only if there are
219             parameters to copy. This allows to cleanly format URLs without extra '?'
220             or '&' symbols.
221              
222             All special symbols in parameter values would be properly escaped, you
223             do not need to worry about that.
224              
225             =cut
226              
227             sub pass_cgi_params ($%) {
228 0     0 1 0 my $self=shift;
229 0         0 my $args=get_args(\@_);
230              
231             # Creating list of exceptions
232             #
233 0         0 my %except;
234 0   0     0 foreach my $param (split(/[,\s]/,$args->{except} || '')) {
235 0         0 $param=~s/\s//gs;
236 0 0       0 next unless length($param);
237 0 0       0 if(index($param,'*') != -1) {
238 0         0 $param=substr($param,0,index($param,'*'));
239 0         0 foreach my $p ($self->cgi->param) {
240 0 0       0 next unless index($p,$param) == 0;
241 0         0 $except{$p}=1;
242             }
243 0         0 next;
244             }
245 0         0 $except{$param}=1;
246             }
247              
248             # Expanding parameters in list
249             #
250 0         0 my @params;
251 0         0 foreach my $param (split(/[,\s]/,$args->{params})) {
252 0         0 $param=~s/\s//gs;
253 0 0       0 next unless length($param);
254 0 0       0 if(index($param,'*') != -1) {
255 0         0 $param=substr($param,0,index($param,'*'));
256 0         0 foreach my $p ($self->cgi->param) {
257 0 0       0 next unless defined $p;
258 0 0       0 next unless index($p,$param) == 0;
259 0         0 push @params,$p;
260             }
261 0         0 next;
262             }
263 0         0 push @params,$param;
264             }
265              
266             # Creating HTML code that will pass these parameters.
267             #
268 0         0 my $html;
269 0   0     0 my $result=$args->{result} || 'query';
270 0         0 foreach my $param (@params) {
271 0 0       0 next if $except{$param};
272              
273 0         0 my $value=$self->cgi->param($param);
274 0 0       0 next unless defined $value;
275              
276 0 0       0 if($result eq 'form') {
277 0         0 $html.='';
278             }
279             else {
280 0 0       0 $html.='&' if $html;
281 0         0 $html.=t2hq($param) . '=' . t2hq($value);
282             }
283             }
284              
285             # No output if there are no parameters
286             #
287 0 0       0 if(defined $html) {
288 0 0       0 $self->textout($args->{'prefix'}) if $args->{'prefix'};
289 0         0 $self->textout($html);
290             }
291             }
292              
293             ###############################################################################
294              
295             =item 'current-url' => show_current_url ()
296              
297             Prints out current page URL without parameters. Accepts the same
298             arguments as Page's pageurl method and displays the same value.
299              
300             =cut
301              
302             sub show_current_url ($;%) {
303 0     0 1 0 my $self=shift;
304 0         0 $self->textout($self->pageurl(@_));
305             }
306              
307             ###############################################################################
308              
309             =item 'base-url' => show_base_url ()
310              
311             Prints out base site URL without parameters. Accepts the same arguments
312             as Page's base_url() method and displays the same value.
313              
314             =cut
315              
316             sub show_base_url ($;%) {
317 0     0 1 0 my $self=shift;
318 0         0 $self->textout($self->base_url(@_));
319             }
320              
321             ###############################################################################
322              
323             =item 'number-ordinal-suffix' => number_ordinal_suffix (%)
324              
325             Displays a two-letter suffix to make a number into an ordinal, i.e. 2
326             into "2-nd", 43 into "43-rd", 1001 into "1001-st" and so on.
327              
328             Takes one argument -- 'number'.
329              
330             =cut
331              
332             sub number_ordinal_suffix ($%) {
333 7     7 1 12 my $self=shift;
334 7         13 my $args=get_args(\@_);
335              
336 1     1   1323 use integer;
  1         3  
  1         14  
337 7   100     81 my $number=int($args->{number} || 0);
338 7 100       24 $number=-$number if $number<0;
339 7         12 $number=$number % 100;
340 7         9 my $nl=$number%10;
341 7         10 my $suffix;
342 7 100 100     40 if(($number>10 && $number<20) || $nl==0 || $nl>3) {
    100 100        
    100 100        
    50          
343 4         7 $suffix='th';
344             }
345             elsif($nl == 1) {
346 1         2 $suffix='st';
347             }
348             elsif($nl == 2) {
349 1         2 $suffix='nd';
350             }
351             elsif($nl == 2) {
352 0         0 $suffix='nd';
353             }
354             else {
355 1         2 $suffix='rd';
356             }
357              
358 7         21 $self->textout($suffix);
359             }
360              
361             ###############################################################################
362              
363             =item 'show-pagedesc' => show_pagedesc (%)
364              
365             Displays value of pagedesc structure (see L) with the given
366             "name". Default name is "fullpath". Useful for processing tree-to-object
367             mapped documents.
368              
369             =cut
370              
371             sub show_pagedesc ($) {
372 0     0 1   my $self=shift;
373 0           my $args=get_args(\@_);
374 0   0       my $name=$args->{name} || 'fullpath';
375 0   0       $self->textout($self->clipboard->get('pagedesc')->{$name} || '');
376             }
377              
378             ###############################################################################
379              
380             =item 'select-time-range' => select_time_range(%)
381              
382             Displays a list of
383              
384             Exact output depends on "type" argument that can be:
385              
386             =over
387              
388             =item "days"
389              
390             Lists days of month from optional "start" day (default is "1") to
391             optional "end" day (default is the number of days in the current month).
392              
393             =item "quorters"
394              
395             Two optional arguments "start" and "end" set start and end date for the
396             time range. The format is YYYY-Q, where YYYY is a year in four digits
397             format and Q is quarter number from 1 to 4.
398              
399             If "end" is not set the current quorter is assumed.
400              
401             =over
402              
403             Special argument "current" will have select_time_range() add "SELECTED"
404             option to the appropriate entry in the final list. The format is the
405             same as for "start" and "end".
406              
407             Default sorting is from most recent down, but this can be changed with
408             non-zero "ascend" argument.
409              
410             Example:
411              
412            
413             <%Utility mode="select-time-range"
414             type="quarters"
415             start="2000-1"
416             current="2000-3"%>
417            
418              
419             Would produce something like:
420              
421            
422            
423            
424            
425            
426            
427            
428              
429             =cut
430              
431             sub select_time_range ($%) {
432 0     0 1   my $self=shift;
433 0           my $args=get_args(\@_);
434              
435 0           my $type=$args->{type};
436              
437             ##
438             # Quorters
439             #
440 0 0         if($type eq 'quarters') {
    0          
441             ##
442             # Start date
443             #
444 0           my $year;
445             my $quarter;
446 0 0         if($args->{start}) {
447 0           my ($y,$q)=($args->{start} =~ /^(\d+)\D+(\d+)$/);
448 0 0 0       if($y>1000 && $q>0 && $q<5) {
      0        
449 0           $year=$y;
450 0           $quarter=$q;
451             }
452             else {
453 0           eprint "Bad year ($y) or quarter ($q) in '$args->{start}'";
454             }
455             }
456 0 0         if(!$year) {
457 0           $year=2000; # Kind of birthday of XAO::Web :)
458 0           $quarter=1;
459             }
460 0           my $lastyear;
461             my $lastquarter;
462 0 0         if($args->{end}) {
463 0           my ($y,$q)=($args->{end} =~ /^(\d+)\D+(\d+)$/);
464 0 0 0       if($y>1000 && $q>0 && $q<5) {
      0        
465 0           $lastyear=$y;
466 0           $lastquarter=$q;
467             }
468             else {
469 0           eprint "Bad last year ($y) or quarter ($q) in '$args->{end}'";
470             }
471             }
472 0 0         if(!$lastyear) {
473 0           $lastyear=(gmtime)[5]+1900;
474 0           $lastquarter=(gmtime)[4]/3+1;
475             }
476 0 0 0       if($year>$lastyear || ($year == $lastyear && $quarter>$lastquarter)) {
      0        
477 0           eprint "Start date ($year-$quarter) is after end date ($lastyear-$lastquarter)";
478 0           $lastyear=$year;
479 0           $lastquarter=$quarter;
480             }
481 0           my $obj=$self->object;
482 0           my @qq=('1-st Qtr', '2-nd Qtr', '3-rd Qtr', '4-th Qtr');
483 0 0         if($args->{ascend}) {
484 0   0       while($year<$lastyear ||
      0        
485             ($year==$lastyear && $quarter<=$lastquarter)) {
486 0           my $value="$year-$quarter";
487             $obj->display(
488             template => '
489             VALUE => $value,
490 0 0 0       SELECTED => $args->{current} && $args->{current} eq $value ? " SELECTED " : "",
491             TEXT => $year . ', ' . $qq[$quarter-1],
492             YEAR => $year,
493             QUARTER => $quarter
494             );
495 0           $quarter++;
496 0 0         if($quarter>4) {
497 0           $quarter=1;
498 0           $year++;
499             }
500             }
501             }
502             else {
503 0   0       while($lastyear>$year ||
      0        
504             ($year==$lastyear && $lastquarter>=$quarter)) {
505 0           my $value="$lastyear-$lastquarter";
506             $obj->display(
507             template => '
508             VALUE => $value,
509 0 0 0       SELECTED => $args->{current} && $args->{current} eq $value ? " SELECTED " : "",
510             TEXT => $lastyear . ', ' . $qq[$lastquarter-1],
511             YEAR => $lastyear,
512             QUARTER => $lastquarter
513             );
514 0           $lastquarter--;
515 0 0         if($lastquarter<1) {
516 0           $lastquarter=4;
517 0           $lastyear--;
518             }
519             }
520             }
521             }
522              
523             ##
524             # Days of month
525             #
526             elsif($type eq 'days') {
527 1     1   709 use integer;
  1         2  
  1         4  
528              
529 0   0       my $start=int($args->{start} || '1');
530 0           my $end=$args->{end};
531              
532 0 0         if(!$end) {
533 0           my @ct=localtime;
534 0           $ct[0]=30;
535 0           $ct[1]=$ct[2]=0;
536 0           $ct[3]=1;
537 0           $ct[4]+=1;
538 0 0         if($ct[4]>=12) {
539 0           $ct[4]=0;
540 0           $ct[5]++;
541             }
542 0           my $nm=mktime(@ct);
543 0           $end=(localtime($nm-120*60))[3];
544             }
545 0           $end=int($end);
546              
547 0           my $cmp;
548             my $inc;
549 0 0         if($args->{ascend}) {
550 0 0         if($end<$start) {
551 0           my $t=$start;
552 0           $start=$end;
553 0           $end=$t;
554             }
555             $cmp=sub {
556 0     0     return $_[0] <= $end;
557 0           };
558 0           $inc=1;
559             }
560             else {
561 0 0         if($end>$start) {
562 0           my $t=$start;
563 0           $start=$end;
564 0           $end=$t;
565             }
566             $cmp=sub {
567 0     0     return $_[0] >= $end;
568 0           };
569 0           $inc=-1;
570             }
571              
572 0           my $page=$self->object;
573 0           for(my $day=$start; &{$cmp}($day); $day+=$inc) {
  0            
574             $page->display(
575             template => '
576             VALUE => $day,
577 0 0 0       SELECTED => $args->{current} && $args->{current} == $day ? " SELECTED " : "",
578             TEXT => $day,
579             );
580             }
581             }
582              
583             ##
584             # Unknown type
585             #
586             else {
587 0           throw $self "select_time_range - unknown range type ($type)";
588             }
589             }
590              
591             ###############################################################################
592             1;
593             __END__