File Coverage

blib/lib/XAO/DO/Web/Utility.pm
Criterion Covered Total %
statement 39 196 19.9
branch 17 108 15.7
condition 11 67 16.4
subroutine 9 18 50.0
pod 8 9 88.8
total 84 398 21.1


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