fix ports with dashes (-) not showing their monitoring status, RT#14926
[freeside.git] / torrus / perllib / Torrus / Renderer / HTML.pm
1 #  Copyright (C) 2002  Stanislav Sinyagin
2 #
3 #  This program is free software; you can redistribute it and/or modify
4 #  it under the terms of the GNU General Public License as published by
5 #  the Free Software Foundation; either version 2 of the License, or
6 #  (at your option) any later version.
7 #
8 #  This program is distributed in the hope that it will be useful,
9 #  but WITHOUT ANY WARRANTY; without even the implied warranty of
10 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 #  GNU General Public License for more details.
12 #
13 #  You should have received a copy of the GNU General Public License
14 #  along with this program; if not, write to the Free Software
15 #  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
16
17 # $Id: HTML.pm,v 1.15 2011-10-26 02:44:16 ivan Exp $
18 # Stanislav Sinyagin <ssinyagin@yahoo.com>
19
20 package Torrus::Renderer::HTML;
21
22 use strict;
23
24 use Torrus::ConfigTree;
25 use Torrus::Search;
26 use Torrus::Log;
27
28 use URI::Escape;
29 use Template;
30 use POSIX qw(abs log floor pow);
31 use Date::Parse;
32 use Date::Format;
33
34 Torrus::SiteConfig::loadStyling();
35
36 # All our methods are imported by Torrus::Renderer;
37
38 sub render_html
39 {
40     my $self = shift;
41     my $config_tree = shift;
42     my $token = shift;
43     my $view = shift;
44     my $outfile = shift;
45
46     my $tmplfile = $config_tree->getParam($view, 'html-template');
47
48     my $expires = $config_tree->getParam($view, 'expires');
49     
50     # Create the Template Toolkit processor once, and reuse
51     # it in subsequent render() calls
52
53     if( not defined( $self->{'tt'} ) )
54     {
55         $self->{'tt'} =
56             new Template(INCLUDE_PATH => $Torrus::Global::templateDirs,
57                          TRIM => 1);
58     }
59     my $ttvars =
60     {
61         'treeName'   => $config_tree->treeName(),
62         'token'      => $token,
63         'view'       => $view,
64         'expires'    => $expires,
65         'path'       => sub { return $config_tree->path($_[0]); },
66         'pathToken'  => sub { return $config_tree->token($_[0]); },
67         'nodeExists' => sub { return $config_tree->nodeExists($_[0]); },
68         'children'   => sub { return $config_tree->getChildren($_[0]); },
69         'isLeaf'     => sub { return $config_tree->isLeaf($_[0]); },
70         'isAlias'    => sub { return $config_tree->isAlias($_[0]); },
71         'sortTokens' => sub { return $self->sortTokens($config_tree,
72                                                        $_[0]); },
73         'nodeName'   => sub { return $self->nodeName($config_tree, $_[0]); },
74         'parent'     => sub { return $config_tree->getParent($_[0]); },
75         'nodeParam'  => sub { return $config_tree->getNodeParam(@_); },
76         'param'      => sub { return $config_tree->getParam(@_); },
77         'url'        => sub { return $self->makeURL($config_tree, 0, @_); },
78         'persistentUrl' => sub { return $self->makeURL($config_tree, 1, @_); },
79         'clearVar'   => sub { delete $self->{'options'}{'variables'}{$_[0]};
80                               return undef;},
81         'plainURL'   => $Torrus::Renderer::plainURL,
82         'splitUrls'  => sub { return $self->makeSplitURLs($config_tree,
83                                                           $_[0], $_[1]); },
84         'topURL'     => ($Torrus::Renderer::rendererURL ne '' ?
85                          $Torrus::Renderer::rendererURL : '/'),
86         'rrprint'    => sub { return $self->rrPrint($config_tree,
87                                                     $_[0], $_[1]); },
88         'scale'      => sub { return $self->scale($_[0], $_[1]); },
89         'tsetMembers' => sub { $config_tree->tsetMembers($_[0]); },
90         'tsetList'   => sub { $config_tree->getTsets(); },
91         'style'      => sub { return $self->style($_[0]); },
92         'companyName'=> $Torrus::Renderer::companyName,
93         'companyLogo'=> $Torrus::Renderer::companyLogo,
94         'companyURL' => $Torrus::Renderer::companyURL,
95         'siteInfo'   => $Torrus::Renderer::siteInfo,
96         'treeInfo'   => sub { return $Torrus::Global::treeConfig{
97             $config_tree->treeName()}{'info'}; },
98         'version'    => $Torrus::Global::version,
99         'xmlnorm'    => \&Torrus::Renderer::xmlnormalize,
100         'userAuth'   => $Torrus::CGI::authorizeUsers,
101         'uid'        => $self->{'options'}->{'uid'},
102         'userAttr'   => sub { return $self->userAttribute( $_[0] ) },
103         'mayDisplayAdmInfo' => sub {
104             return $self->may_display_adminfo( $config_tree, $_[0] ) },
105         'adminfo' => $self->{'adminfo'},
106         'mayDisplayReports' => sub {
107             return $self->may_display_reports($config_tree) },
108         'reportsUrl' => sub {
109             return $self->reportsUrl($config_tree); },
110         'timestamp'  => sub { return time2str($Torrus::Renderer::timeFormat,
111                                               time()); },
112         'verifyDate'  => sub { return verifyDate($_[0]); },
113         'markup'     => sub{ return $self->translateMarkup( @_ ); },
114         'searchEnabled' => $Torrus::Renderer::searchEnabled,
115         'searchResults' => sub { return $self->doSearch($config_tree, $_[0]); },
116
117         #Freeside
118         'freesideHeader' => sub { return $self->freesideHeader(@_); },
119         'freesideFooter' => sub { return $self->freesideFooter(); },
120         'freesideComponent' => sub { return $self->freesideComponent(@_); },
121         'uri_escape'        => sub { return uri_escape(@_); },
122         'matches'        => sub { return $_[0] =~ $_[1]; },
123
124         #false laziness w/Torrus_Internal::add_interface, update both
125         'iface_underscore' => sub { $_[0] =~ s/[\/\.\-]/_/g; return $_[0]; },
126
127         'load_nms'       => sub { return $self->load_nms; },
128         'get_serviceids'    => sub { my $nms = shift; 
129                                   my $router = shift;
130                                   return $nms->get_router_serviceids($router);
131                                 },
132         'popup_link'     => sub {  
133           my $type = shift;
134
135           if($type eq 'nms-add_iface.html') {
136               my $host = shift;
137               my $interface = shift;
138               my $nms = shift;
139               my $serviceids = shift;
140
141               if ( $serviceids && $serviceids->{$interface} ) {
142
143                 my $serviceid = $serviceids->{$interface};
144
145                 my $svc_port = $nms->find_svc($serviceid);
146
147                 if ($svc_port) {
148                    my $url = $Torrus::Freeside::FSURL.
149                              "/view/svc_port.cgi?". $svc_port->svcnum;
150                    return "<A HREF='$url'>View Service</A>";
151                 } else {
152                   my $component =
153                    $nms->find_torrus_srvderive_component($serviceid);
154                   
155                   if ($component) {
156                      return "$serviceid combined into ".
157                             $component->torrus_srvderive->serviceid;
158                   } else {
159                      return "Monitored as $serviceid".
160                             '; not yet provisioned or combined';
161                   }
162                 }
163
164               } else {
165
166                return
167                    $self->freesideComponent('/elements/popup_link.html',
168                        'action' => "/freeside/misc/".
169                                $type."?host=$host;iface=$interface",
170                         'label' => 'Monitor for billing',
171                         'actionlabel' => 'Monitor interface',
172                    );
173
174               }
175
176           } elsif ($type eq 'nms-add_router.html') {
177                return
178                    $self->freesideComponent('/elements/popup_link.html',
179                        'action' => "/freeside/misc/$type",
180                         'label' => 'Add Router',
181                         'actionlabel' => 'Add Router',
182                    );
183           }
184
185           '';
186        },
187
188     };
189     
190     
191     # Pass the options from Torrus::Renderer::render() to Template
192     while( my( $opt, $val ) = each( %{$self->{'options'}} ) )
193     {
194         $ttvars->{$opt} = $val;
195     }
196
197     my $result = $self->{'tt'}->process( $tmplfile, $ttvars, $outfile );
198
199     undef $ttvars;
200
201     if( not $result )
202     {
203         if( $config_tree->isTset( $token ) )
204         {
205             Error("Error while rendering tokenset $token: " .
206                   $self->{'tt'}->error());
207         }
208         else
209         {
210             my $path = $config_tree->path($token);
211             Error("Error while rendering $path: " .
212                   $self->{'tt'}->error());
213         }
214         return undef;
215     }
216
217     return ($expires+time(), 'text/html; charset=UTF-8');
218 }
219
220
221 sub nodeName
222 {
223     my $self = shift;
224     my $config_tree = shift;
225     my $token = shift;
226
227     my $n = $config_tree->getNodeParam($token, 'node-display-name', 1);
228     if( defined( $n ) and length( $n ) > 0 )
229     {
230         return $n;
231     }
232     
233     return $config_tree->nodeName($config_tree->path($token));
234 }
235
236
237 sub sortTokens
238 {
239     my $self = shift;
240     my $config_tree = shift;
241     my $tokenlist = shift;
242
243     my @sorted = ();
244     if( ref($tokenlist) and scalar(@{$tokenlist}) > 0 )
245     {
246         @sorted = sort
247         {
248             my $p_a = $config_tree->getNodeParam($a, 'precedence', 1);
249             $p_a = 0 unless defined $p_a;
250             my $p_b = $config_tree->getNodeParam($b, 'precedence', 1);
251             $p_b = 0 unless defined $p_b;
252             if( $p_a == $p_b )
253             {
254                 my $n_a = $config_tree->path($a);
255                 my $n_b = $config_tree->path($b);
256                 return $n_a cmp $n_b;
257             }
258             else
259             {
260                 return $p_b <=> $p_a;
261             }
262         } @{$tokenlist};
263     }
264     else
265     {
266         push(@sorted, $tokenlist);
267     }
268     return @sorted;
269 }
270
271
272 # compose an URL for a node.
273 # $persistent defines if the link should be persistent
274 # Persistent link is done with nodeid if available, or with path
275
276 sub makeURL
277 {
278     my $self = shift;
279     my $config_tree = shift;
280     my $persistent = shift;
281     my $token = shift;
282     my $view = shift;
283     my @add_vars = @_;
284
285     my $ret = $Torrus::Renderer::rendererURL . '/' . $config_tree->treeName();
286     
287     if( $persistent )
288     {
289         my $nodeid = $config_tree->getNodeParam($token, 'nodeid', 1);
290         if( defined( $nodeid ) )
291         {
292             $ret .= '?nodeid=' .
293                 uri_escape($nodeid, $Torrus::Renderer::uriEscapeExceptions);
294         }
295         else
296         {
297             $ret .= '?path=' .
298                 uri_escape($config_tree->path($token),
299                            $Torrus::Renderer::uriEscapeExceptions);
300         }
301     }
302     else
303     {
304         $ret .= '?token=' . uri_escape($token);
305     }
306
307     if( $view )
308     {
309         $ret .= '&amp;view=' . uri_escape($view);
310     }
311
312     my %vars = ();
313     # This could be array or a reference to array
314     my $add_vars_size = scalar( @add_vars );
315     if( $add_vars_size == 1 and ref( $add_vars[0] ) )
316     {
317         %vars = @{$add_vars[0]};
318     }
319     elsif( $add_vars_size > 0 and ($add_vars_size % 2 == 0) )
320     {
321         %vars = @add_vars;
322     }
323
324     if( ref( $self->{'options'}->{'variables'} ) )
325     {
326         foreach my $name ( sort keys %{$self->{'options'}->{'variables'}} )
327         {
328             my $val = $self->{'options'}->{'variables'}->{$name};
329             if( not defined( $vars{$name} ) )
330             {
331                 $vars{$name} = $val;
332             }
333         }
334     }
335
336     foreach my $name ( sort keys %vars )
337     {
338         if( $vars{$name} ne '' )
339         {
340             $ret .= '&amp;' . $name . '=' .
341                 uri_escape( $vars{$name},
342                             $Torrus::Renderer::uriEscapeExceptions );
343         }
344     }
345
346     return $ret;
347 }
348
349 sub makeSplitURLs
350 {
351     my $self = shift;
352     my $config_tree = shift;
353     my $token = shift;
354     my $view = shift;
355
356     my $ret = '';
357     while( defined( $token ) )
358     {
359         my $path = $config_tree->path($token);
360         
361         my $str = '<SPAN CLASS="PathElement">';
362         $str .=
363             sprintf('<A HREF="%s">%s%s</A>',
364                     $self->makeURL($config_tree, 0, $token, $view),
365                     $config_tree->nodeName($path),
366                     ( $config_tree->isSubtree($token) and
367                       $path ne '/') ? '/':'' );
368         $str .= "</SPAN>\n";
369         
370         $ret = $str . $ret;
371                 
372         $token = $config_tree->getParent( $token );
373     }
374     
375     return $ret;
376 }
377
378
379 sub rrPrint
380 {
381     my $self = shift;
382     my $config_tree = shift;
383     my $token = shift;
384     my $view = shift;
385
386     my @ret = ();
387     my($fname, $mimetype) = $self->render( $config_tree, $token, $view );
388
389     if( $mimetype ne 'text/plain' )
390     {
391         Error("View $view does not produce text/plain for token $token");
392     }
393     else
394     {
395         if( not open(IN, $fname) )
396         {
397             Error("Cannot open $fname for reading: $!");
398         }
399         else
400         {
401             chomp(my $values = <IN>);
402             @ret = split(':', $values);
403             close IN;
404         }
405     }
406     return @ret;
407 }
408
409 # This subroutine is taken from Dave Plonka's Flowscan
410
411 sub scale
412 {
413     my $self = shift;
414     # This is based somewhat on Tobi Oetiker's code in rrd_graph.c:
415     my $fmt = shift;
416     my $value = shift;
417     my @symbols = ("a", # 10e-18 Ato
418                    "f", # 10e-15 Femto
419                    "p", # 10e-12 Pico
420                    "n", # 10e-9  Nano
421                    "u", # 10e-6  Micro
422                    "m", # 10e-3  Milli
423                    " ", # Base
424                    "k", # 10e3   Kilo
425                    "M", # 10e6   Mega
426                    "G", # 10e9   Giga
427                    "T", # 10e12  Terra
428                    "P", # 10e15  Peta
429                    "E"); # 10e18  Exa
430
431     my $symbcenter = 6;
432     my $digits = (0 == $value)? 0 : floor(log(abs($value))/log(1000));
433     return sprintf( $fmt . " %s", $value/pow(1000, $digits),
434                     $symbols[ $symbcenter+$digits ] );
435 }
436
437 sub style
438 {
439     my $self = shift;
440     my $object = shift;
441
442     my $media;
443     if( not defined( $media = $self->{'options'}->{'variables'}->{'MEDIA'} ) )
444     {
445         $media = 'default';
446     }
447     return  $Torrus::Renderer::styling{$media}{$object};
448 }
449
450
451
452 sub userAttribute
453 {
454     my $self = shift;
455     my $attr = shift;
456
457     if( $self->{'options'}->{'uid'} and $self->{'options'}->{'acl'} )
458     {
459         $self->{'options'}->{'acl'}->
460             userAttribute( $self->{'options'}->{'uid'}, $attr );
461     }
462     else
463     {
464         return '';
465     }
466 }
467
468 sub hasPrivilege
469 {
470     my $self = shift;
471     my $object = shift;
472     my $privilege = shift;
473
474     if( $self->{'options'}->{'uid'} and $self->{'options'}->{'acl'} )
475     {
476         $self->{'options'}->{'acl'}->
477             hasPrivilege( $self->{'options'}->{'uid'}, $object, $privilege );
478     }
479     else
480     {
481         return undef;
482     }
483 }
484
485
486 sub translateMarkup
487 {
488     my $self = shift;
489     my @strings = @_;
490
491     my $tt = new Template( TRIM => 1 );
492
493     my $ttvars =
494     {
495         'em'      =>  sub { return '<em>' . $_[0] . '</em>'; },
496         'strong'  =>  sub { return '<strong>' . $_[0] . '</strong>'; }
497     };
498     
499     my $ret = '';
500     
501     foreach my $str ( @strings )
502     {
503         my $output = '';
504         my $result = $tt->process( \$str, $ttvars, \$output );
505
506         if( not $result )
507         {
508             Error('Error translating markup: ' . $tt->error());
509         }
510         else
511         {
512             $ret .= $output;
513         }
514     }
515
516     undef $tt;
517     
518     return $ret;
519 }
520
521
522 sub verifyDate
523 {
524     my $input = shift;
525
526     my $time = str2time( $input );
527     # rrdtool does not understand dates prior to 1980 (315529200)
528     if( defined( $time ) and $time > 315529200 )
529     {
530         # Present the time in format understood by rrdtool
531         return time2str('%H:%M %Y%m%d', $time);
532     }
533     else
534     {
535         return '';
536     }
537 }
538
539
540 sub may_display_reports
541 {
542     my $self = shift;
543     my $config_tree = shift;
544
545     if( $Torrus::Renderer::displayReports )
546     {
547         if( not $Torrus::CGI::authorizeUsers )
548         {
549             return 1;
550         }
551         
552         my $tree = $config_tree->treeName();
553         if( $self->hasPrivilege( $tree, 'DisplayReports' ) and
554             -r $Torrus::Global::reportsDir . '/' . $tree .
555             '/html/index.html' )
556         {
557             return 1;
558         }
559     }
560     return 0;
561 }
562
563
564 sub reportsUrl
565 {
566     my $self = shift;
567     my $config_tree = shift;
568
569     return $Torrus::Renderer::rendererURL . '/' .
570         $config_tree->treeName() . '?htmlreport=index.html';
571 }
572
573
574 sub doSearch
575 {
576     my $self = shift;
577     my $config_tree = shift;
578     my $string = shift;
579     
580
581     my $tree = $config_tree->treeName();
582     
583     my $sr = new Torrus::Search;
584     $sr->openTree( $tree );
585     my $result = $sr->searchPrefix( $string, $tree );
586     $sr->closeTree( $tree );
587
588     my $ret = [];
589     push( @{$ret}, sort {$a->[0] cmp $b->[0]} @{$result} );
590     
591     return $ret;
592 }
593
594
595 1;
596
597
598 # Local Variables:
599 # mode: perl
600 # indent-tabs-mode: nil
601 # perl-indent-level: 4
602 # End: