1use vars qw($theme_no_table $ui_radio_selector_donejs $module_name 2 $ui_multi_select_donejs, $ui_formcount); 3 4=head1 ui-lib.pl 5 6Common functions for generating HTML for Webmin user interface elements. 7Some example code : 8 9 use WebminCore; 10 init_config(); 11 ui_print_header(undef, 'My Module', ''); 12 13 print ui_form_start('save.cgi'); 14 print ui_table_start('My form', undef, 2); 15 16 print ui_table_row('Enter your name', 17 ui_textbox('name', undef, 40)); 18 19 print ui_table_end(); 20 print ui_form_end([ [ undef, 'Save' ] ]); 21 22 ui_print_footer('/', 'Webmin index'); 23 24=cut 25 26####################### utility functions 27 28=head2 ui_link(href, text, [class], [tags]) 29 30Returns HTML for an <a href>. 31 32=item href - Link 33 34=item text - Text to display for link 35 36=item class - Optional additional CSS classes to include 37 38=item tags - Additional HTML attributes for the <a> tag. 39 40=cut 41 42sub ui_link 43{ 44return &theme_ui_link(@_) if (defined(&theme_ui_link)); 45my ($href, $text, $class, $tags) = @_; 46return ("<a class='ui_link".($class ? " ".$class : "")."' href='$href'".($tags ? " ".$tags : "").">$text</a>"); 47} 48 49=head2 ui_help(title) 50 51Returns HTML for help tooltip bubble 52 53=item title - help tooltip title 54 55=cut 56 57sub ui_help 58{ 59return &theme_ui_help(@_) if (defined(&theme_ui_help)); 60my ($title) = @_; 61return ("<sup class=\"ui_help\" aria-label=\"$title\" data-tooltip><samp>?</samp></sup>"); 62} 63 64=head2 ui_img(src, alt, title, [class], [tags]) 65 66Returns HTML for an <img src>. 67 68=item src - Image path and filename 69 70=item alt - Alt text for screen readers, etc. 71 72=item title - Element title, and tooltip when user hovers over image 73 74=item class - Optional additional CSS classes to include 75 76=item tags - Additional HTML attributes for the <img> tag 77 78=cut 79 80sub ui_img 81{ 82return &theme_ui_img(@_) if (defined(&theme_ui_img)); 83my ($src, $alt, $title, $class, $tags) = @_; 84return ("<img src='".$src."' class='ui_img".($class ? " ".$class : "")."' alt='$alt' ".($title ? "title='$title'" : "").($tags ? " ".$tags : "").">"); 85} 86 87=head2 ui_link_button(href, text, [target], [tags]) 88 89Returns HTML for a button, which opens a URL when clicked. The parameters are : 90 91=item href - Link URL 92 93=item text - Text to display on the button 94 95=item target - Window name to open the link in 96 97=item tags - Additional HTML attributes for the <input> tag. 98 99=cut 100 101sub ui_link_button 102{ 103return &theme_ui_link_button(@_) if (defined(&theme_ui_link_button)); 104my ($href, $label, $target, $tags) = @_; 105$target ||= "_self"; 106return &ui_button($label, undef, 0, 107 "onClick='window.open(\""."e_javascript($href)."\", \"$target\")' ". 108 $tags); 109} 110 111####################### table generation functions 112 113=head2 ui_table_start(heading, [tabletags], [cols], [&default-tds], [right-heading]) 114 115Returns HTML for the start of a form block into which labelled inputs can 116be placed. By default this is implemented as a table with another table inside 117it, but themes may override this with their own layout. 118 119The parameters are : 120 121=item heading - Text to show at the top of the form. 122 123=item tabletags - HTML attributes to put in the outer <table>, typically something like width=100%. 124 125=item cols - Desired number of columns for labels and fields. Defaults to 4, but can be 2 for forms with lots of wide inputs. 126 127=item default-tds - An optional array reference of HTML attributes for the <td> tags in each row of the table. 128 129=item right-heading - HTML to appear in the heading, aligned to the right. 130 131=cut 132sub ui_table_start 133{ 134return &theme_ui_table_start(@_) if (defined(&theme_ui_table_start)); 135my ($heading, $tabletags, $cols, $tds, $rightheading) = @_; 136if (defined($main::ui_table_cols)) { 137 # Push on stack, for nested call 138 push(@main::ui_table_cols_stack, $main::ui_table_cols); 139 push(@main::ui_table_pos_stack, $main::ui_table_pos); 140 push(@main::ui_table_default_tds_stack, $main::ui_table_default_tds); 141 } 142my $colspan = 1; 143my $rv; 144$rv .= "<table class='ui_table' border $tabletags>\n"; 145if (defined($heading) || defined($rightheading)) { 146 $rv .= "<tr".($tb ? " ".$tb : "")." class='ui_table_head'>"; 147 if (defined($heading)) { 148 $rv .= "<td><b>$heading</b></td>" 149 } 150 if (defined($rightheading)) { 151 $rv .= "<td align='right'>$rightheading</td>"; 152 $colspan++; 153 } 154 $rv .= "</tr>\n"; 155 } 156$rv .= "<tr".($cb ? " ".$cb : "")." class='ui_table_body'> <td colspan='$colspan'>". 157 "<table width='100%'>\n"; 158$main::ui_table_cols = $cols || 4; 159$main::ui_table_pos = 0; 160$main::ui_table_default_tds = $tds; 161return $rv; 162} 163 164=head2 ui_table_end 165 166Returns HTML for the end of a block started by ui_table_start. 167 168=cut 169sub ui_table_end 170{ 171return &theme_ui_table_end(@_) if (defined(&theme_ui_table_end)); 172my $rv; 173if ($main::ui_table_cols == 4 && $main::ui_table_pos) { 174 # Add an empty block to balance the table 175 $rv .= &ui_table_row(" ", " "); 176 } 177if (@main::ui_table_cols_stack) { 178 $main::ui_table_cols = pop(@main::ui_table_cols_stack); 179 $main::ui_table_pos = pop(@main::ui_table_pos_stack); 180 $main::ui_table_default_tds = pop(@main::ui_table_default_tds_stack); 181 } 182else { 183 $main::ui_table_cols = undef; 184 $main::ui_table_pos = undef; 185 $main::ui_table_default_tds = undef; 186 } 187$rv .= "</table></td></tr></table>\n"; 188return $rv; 189} 190 191=head2 ui_table_row(label, value, [cols], [&td-tags]) 192 193Returns HTML for a row in a table started by ui_table_start, with a 1-column 194label and 1+ column value. The parameters are : 195 196=item label - Label for the input field. If this is undef, no label is displayed. 197 198=item value - HTML for the input part of the row. 199 200=item cols - Number of columns the value should take up, defaulting to 1. 201 202=item td-tags - Array reference of HTML attributes for the <td> tags in this row. 203 204=cut 205sub ui_table_row 206{ 207return &theme_ui_table_row(@_) if (defined(&theme_ui_table_row)); 208my ($label, $value, $cols, $tds) = @_; 209$cols ||= 1; 210$tds ||= $main::ui_table_default_tds; 211my $rv; 212if ($main::ui_table_pos+$cols+1 > $main::ui_table_cols && 213 $main::ui_table_pos != 0) { 214 # If the requested number of cols won't fit in the number 215 # remaining, start a new row 216 my $leftover = $main::ui_table_cols - $main::ui_table_pos; 217 $rv .= "<td colspan='$leftover'></td>\n"; 218 $rv .= "</tr>\n"; 219 $main::ui_table_pos = 0; 220 } 221$rv .= "<tr class='ui_table_row'>\n" 222 if ($main::ui_table_pos%$main::ui_table_cols == 0); 223if (defined($label) && 224 ($value =~ /id="([^"]+)"/ || $value =~ /id='([^']+)'/ || 225 $value =~ /id=([^>\s]+)/)) { 226 # Value contains an input with an ID 227 my $id = $1; 228 $label = "<label for=\""."e_escape($id)."\">$label</label>"; 229 } 230$rv .= "<td valign='top' $tds->[0] class='ui_label'><b>$label</b></td>\n" 231 if (defined($label)); 232$rv .= "<td valign='top' colspan='$cols' $tds->[1] class='ui_value'>$value</td>\n"; 233$main::ui_table_pos += $cols+(defined($label) ? 1 : 0); 234if ($main::ui_table_pos%$main::ui_table_cols == 0) { 235 $rv .= "</tr>\n"; 236 $main::ui_table_pos = 0; 237 } 238return $rv; 239} 240 241=head2 ui_table_hr 242 243Returns HTML for a row in a block started by ui_table_row, with a horizontal 244line inside it to separate sections. 245 246=cut 247sub ui_table_hr 248{ 249return &theme_ui_table_hr(@_) if (defined(&theme_ui_table_hr)); 250my $rv; 251if ($ui_table_pos) { 252 $rv .= "</tr>\n"; 253 $ui_table_pos = 0; 254 } 255$rv .= "<tr class='ui_table_hr'> ". 256 "<td colspan='$main::ui_table_cols'><hr></td> </tr>\n"; 257return $rv; 258} 259 260=head2 ui_table_span(text) 261 262Outputs a table row that spans the whole table, and contains the given text. 263 264=cut 265sub ui_table_span 266{ 267my ($text) = @_; 268return &theme_ui_table_span(@_) if (defined(&theme_ui_table_span)); 269my $rv; 270if ($ui_table_pos) { 271 $rv .= "</tr>\n"; 272 $ui_table_pos = 0; 273 } 274$rv .= "<tr class='ui_table_span'> ". 275 "<td colspan='$main::ui_table_cols'>$text</td> </tr>\n"; 276return $rv; 277} 278 279=head2 ui_columns_start(&headings, [width-percent], [noborder], [&tdtags], [heading]) 280 281Returns HTML for the start of a multi-column table, with the given headings. 282The parameters are : 283 284=item headings - An array reference of headers for the table's columns. 285 286=item width-percent - Desired width as a percentage, or undef to let the browser decide. 287 288=item noborder - Set to 1 if the table should not have a border. 289 290=item tdtags - An optional reference to an array of HTML attributes for the table's <td> tags. 291 292=item heading - An optional heading to put above the table. 293 294=cut 295sub ui_columns_start 296{ 297return &theme_ui_columns_start(@_) if (defined(&theme_ui_columns_start)); 298my ($heads, $width, $noborder, $tdtags, $title) = @_; 299my $rv; 300$rv .= "<table".($noborder ? "" : " border"). 301 (defined($width) ? " width='$width%'" : "")." class='ui_columns'>\n"; 302if ($title) { 303 $rv .= "<tr".($tb ? " ".$tb : "")." class='ui_columns_heading'>". 304 "<td colspan='".scalar(@$heads)."'><b>$title</b></td></tr>\n"; 305 } 306$rv .= "<tr".($tb ? " ".$tb : "")." class='ui_columns_heads'>\n"; 307my $i; 308for($i=0; $i<@$heads; $i++) { 309 $rv .= "<td ".$tdtags->[$i]."><b>". 310 ($heads->[$i] eq "" ? "<br>" : $heads->[$i])."</b></td>\n"; 311 } 312$rv .= "</tr>\n"; 313return $rv; 314} 315 316=head2 ui_columns_row(&columns, &tdtags) 317 318Returns HTML for a row in a multi-column table. The parameters are : 319 320=item columns - Reference to an array containing the HTML to show in the columns for this row. 321 322=item tdtags - An optional array reference containing HTML attributes for the row's <td> tags. 323 324=cut 325sub ui_columns_row 326{ 327return &theme_ui_columns_row(@_) if (defined(&theme_ui_columns_row)); 328my ($cols, $tdtags) = @_; 329my $rv; 330$rv .= "<tr".($cb ? " ".$cb : "")." class='ui_columns_row'>\n"; 331my $i; 332for($i=0; $i<@$cols; $i++) { 333 $rv .= "<td ".$tdtags->[$i].">". 334 ($cols->[$i] !~ /\S/ ? "<br>" : $cols->[$i])."</td>\n"; 335 } 336$rv .= "</tr>\n"; 337return $rv; 338} 339 340=head2 ui_columns_header(&columns, &tdtags) 341 342Returns HTML for a row in a multi-column table, styled as a header. Parameters 343are the same as ui_columns_row. 344 345=cut 346sub ui_columns_header 347{ 348return &theme_ui_columns_header(@_) if (defined(&theme_ui_columns_header)); 349my ($cols, $tdtags) = @_; 350my $rv; 351$rv .= "<tr".($tb ? " ".$tb : "")." class='ui_columns_header'>\n"; 352my $i; 353for($i=0; $i<@$cols; $i++) { 354 $rv .= "<td ".$tdtags->[$i]."><b>". 355 ($cols->[$i] eq "" ? "<br>" : $cols->[$i])."</b></td>\n"; 356 } 357$rv .= "</tr>\n"; 358return $rv; 359} 360 361=head2 ui_checked_columns_row(&columns, &tdtags, checkname, checkvalue, [checked?], [disabled], [tags]) 362 363Returns HTML for a row in a multi-column table, in which the first column 364contains a checkbox. The parameters are : 365 366=item columns - Reference to an array containing the HTML to show in the columns for this row. 367 368=item tdtags - An optional array reference containing HTML attributes for the row's <td> tags. 369 370=item checkname - Name for the checkbox input. Should be the same for all rows. 371 372=item checkvalue - Value for this checkbox input. 373 374=item checked - Set to 1 if it should be checked by default. 375 376=item disabled - Set to 1 if the checkbox should be disabled and thus un-clickable. 377 378=item tags - Extra HTML tags to include in the radio button. 379 380=cut 381sub ui_checked_columns_row 382{ 383return &theme_ui_checked_columns_row(@_) if (defined(&theme_ui_checked_columns_row)); 384my ($cols, $tdtags, $checkname, $checkvalue, $checked, $disabled, $tags) = @_; 385my $rv; 386$rv .= "<tr".($cb ? " ".$cb : "")." class='ui_checked_columns'>\n"; 387$rv .= "<td class='ui_checked_checkbox' ".$tdtags->[0].">". 388 &ui_checkbox($checkname, $checkvalue, undef, $checked, $tags, $disabled). 389 "</td>\n"; 390my $i; 391for($i=0; $i<@$cols; $i++) { 392 $rv .= "<td ".$tdtags->[$i+1].">"; 393 if ($cols->[$i] !~ /<a\s+href|<input|<select|<textarea/) { 394 $rv .= "<label for=\"". 395 "e_escape("${checkname}_${checkvalue}")."\">"; 396 } 397 $rv .= ($cols->[$i] !~ /\S/ ? "<br>" : $cols->[$i]); 398 if ($cols->[$i] !~ /<a\s+href|<input|<select|<textarea/) { 399 $rv .= "</label>"; 400 } 401 $rv .= "</td>\n"; 402 } 403$rv .= "</tr>\n"; 404return $rv; 405} 406 407=head2 ui_radio_columns_row(&columns, &tdtags, checkname, checkvalue, [checked], [disabled], [tags]) 408 409Returns HTML for a row in a multi-column table, in which the first 410column is a radio button. The parameters are : 411 412=item columns - Reference to an array containing the HTML to show in the columns for this row. 413 414=item tdtags - An optional array reference containing HTML attributes for the row's <td> tags. 415 416=item checkname - Name for the radio button input. Should be the same for all rows. 417 418=item checkvalue - Value for this radio button option. 419 420=item checked - Set to 1 if it should be checked by default. 421 422=item disabled - Set to 1 if the radio button should be disabled and thus un-clickable. 423 424=item tags - Extra HTML tags to include in the radio button. 425 426=cut 427sub ui_radio_columns_row 428{ 429return &theme_ui_radio_columns_row(@_) if (defined(&theme_ui_radio_columns_row)); 430my ($cols, $tdtags, $checkname, $checkvalue, $checked, $dis, $tags) = @_; 431my $rv; 432$rv .= "<tr".($cb ? " ".$cb : "")." class='ui_radio_columns'>\n"; 433$rv .= "<td class='ui_radio_radio' ".$tdtags->[0].">". 434 &ui_oneradio($checkname, $checkvalue, "", $checked, undef, $dis)."</td>\n"; 435my $i; 436for($i=0; $i<@$cols; $i++) { 437 $rv .= "<td ".$tdtags->[$i+1].">"; 438 if ($cols->[$i] !~ /<a\s+href|<input|<select|<textarea/) { 439 $rv .= "<label for=\"". 440 "e_escape("${checkname}_${checkvalue}")."\">"; 441 } 442 $rv .= ($cols->[$i] !~ /\S/ ? "<br>" : $cols->[$i]); 443 if ($cols->[$i] !~ /<a\s+href|<input|<select|<textarea/) { 444 $rv .= "</label>"; 445 } 446 $rv .= "</td>\n"; 447 } 448$rv .= "</tr>\n"; 449return $rv; 450} 451 452=head2 ui_columns_end 453 454Returns HTML to end a table started by ui_columns_start. 455 456=cut 457sub ui_columns_end 458{ 459return &theme_ui_columns_end(@_) if (defined(&theme_ui_columns_end)); 460return "</table>\n"; 461} 462 463=head2 ui_columns_table(&headings, width-percent, &data, &types, no-sort, title, empty-msg) 464 465Returns HTML for a complete table, typically generated internally by 466ui_columns_start, ui_columns_row and ui_columns_end. The parameters are : 467 468=item headings - An array ref of heading HTML. 469 470=item width-percent - Preferred total width 471 472=item data - A 2x2 array ref of table contents. Each can either be a simple string, or a hash ref like : 473 474 { 'type' => 'group', 'desc' => 'Some section title' } 475 { 'type' => 'string', 'value' => 'Foo', 'colums' => 3, 476 'nowrap' => 1 } 477 { 'type' => 'checkbox', 'name' => 'd', 'value' => 'foo', 478 'label' => 'Yes', 'checked' => 1, 'disabled' => 1 } 479 { 'type' => 'radio', 'name' => 'd', 'value' => 'foo', ... } 480 481=item types - An array ref of data types, such as 'string', 'number', 'bytes' or 'date' 482 483=item no-sort - Set to 1 to disable sorting by theme. 484 485=item title - Text to appear above the table. 486 487=item empty-msg - Message to display if no data. 488 489=cut 490sub ui_columns_table 491{ 492return &theme_ui_columns_table(@_) if (defined(&theme_ui_columns_table)); 493my ($heads, $width, $data, $types, $nosort, $title, $emptymsg) = @_; 494my $rv; 495 496# Just show empty message if no data 497if ($emptymsg && !@$data) { 498 $rv .= &ui_subheading($title) if ($title); 499 $rv .= "<span class='ui_emptymsg'><b>$emptymsg</b></span><p>\n"; 500 return $rv; 501 } 502 503# Are there any checkboxes in each column? If so, make those columns narrow 504my @tds = map { "valign='top'" } @$heads; 505my $maxwidth = 0; 506foreach my $r (@$data) { 507 my $cc = 0; 508 foreach my $c (@$r) { 509 if (ref($c) && 510 ($c->{'type'} eq 'checkbox' || $c->{'type'} eq 'radio')) { 511 $tds[$cc] .= " width='5'" if ($tds[$cc] !~ /width=/); 512 } 513 $cc++; 514 } 515 $maxwidth = $cc if ($cc > $maxwidth); 516 } 517$rv .= &ui_columns_start($heads, $width, 0, \@tds, $title); 518 519# Add the data rows 520foreach my $r (@$data) { 521 my $c0; 522 if (ref($r->[0]) && ($r->[0]->{'type'} eq 'checkbox' || 523 $r->[0]->{'type'} eq 'radio')) { 524 # First column is special 525 $c0 = $r->[0]; 526 $r = [ @$r[1..(@$r-1)] ]; 527 } 528 # Turn data into HTML 529 my @rtds = @tds; 530 my @cols; 531 my $cn = 0; 532 $cn++ if ($c0); 533 foreach my $c (@$r) { 534 if (!ref($c)) { 535 # Plain old string 536 push(@cols, $c); 537 } 538 elsif ($c->{'type'} eq 'checkbox') { 539 # Checkbox in non-first column 540 push(@cols, &ui_checkbox($c->{'name'}, $c->{'value'}, 541 $c->{'label'}, $c->{'checked'}, 542 $c->{'tags'}, 543 $c->{'disabled'})); 544 } 545 elsif ($c->{'type'} eq 'radio') { 546 # Radio button in non-first column 547 push(@cols, &ui_oneradio($c->{'name'}, $c->{'value'}, 548 $c->{'label'}, $c->{'checked'}, 549 $c->{'tags'}, 550 $c->{'disabled'})); 551 } 552 elsif ($c->{'type'} eq 'group') { 553 # Header row that spans whole table 554 $rv .= &ui_columns_header([ $c->{'desc'} ], 555 [ "colspan=$width" ]); 556 next; 557 } 558 elsif ($c->{'type'} eq 'string') { 559 # A string, which might be special 560 push(@cols, $c->{'value'}); 561 if ($c->{'columns'} > 1) { 562 splice(@rtds, $cn, $c->{'columns'}, 563 "colspan=".$c->{'columns'}); 564 } 565 if ($c->{'nowrap'}) { 566 $rtds[$cn] .= " nowrap"; 567 } 568 $rtds[$cn] .= " ".$c->{'td'} if ($c->{'td'}); 569 } 570 $cn++; 571 } 572 # Add the row 573 if (!$c0) { 574 $rv .= &ui_columns_row(\@cols, \@rtds); 575 } 576 elsif ($c0->{'type'} eq 'checkbox') { 577 $rv .= &ui_checked_columns_row(\@cols, \@rtds, $c0->{'name'}, 578 $c0->{'value'}, $c0->{'checked'}, 579 $c0->{'disabled'}, 580 $c0->{'tags'}); 581 } 582 elsif ($c0->{'type'} eq 'radio') { 583 $rv .= &ui_radio_columns_row(\@cols, \@rtds, $c0->{'name'}, 584 $c0->{'value'}, $c0->{'checked'}, 585 $c0->{'disabled'}, 586 $c0->{'tags'}); 587 } 588 } 589 590$rv .= &ui_columns_end(); 591return $rv; 592} 593 594=head2 ui_form_columns_table(cgi, &buttons, select-all, &otherlinks, &hiddens, &headings, width-percent, &data, &types, no-sort, title, empty-msg, form-no) 595 596Similar to ui_columns_table, but wrapped in a form. Parameters are : 597 598=item cgi - URL to submit the form to. 599 600=item buttons - An array ref of buttons at the end of the form, similar to that taken by ui_form_end. 601 602=item select-all - If set to 1, include select all / invert links. 603 604=item otherslinks - An array ref of other links to put at the top of the table, each of which is a 3-element hash ref of url, text and alignment (left or right). 605 606=item hiddens - An array ref of hidden fields, each of which is a 2-element array ref containing the name and value. 607 608=item formno - Index of this form on the page. Defaults to 0, but should be set if there is more than one form on the page. 609 610All other parameters are the same as ui_columns_table. 611 612=cut 613sub ui_form_columns_table 614{ 615return &theme_ui_form_columns_table(@_) 616 if (defined(&theme_ui_form_columns_table)); 617my ($cgi, $buttons, $selectall, $others, $hiddens, 618 $heads, $width, $data, $types, $nosort, $title, $emptymsg, $formno) = @_; 619my $rv; 620 621# Build links 622my @leftlinks = map { "<a href='$_->[0]'>$_->[1]</a>" } 623 grep { $_->[2] ne 'right' } @$others; 624my @rightlinks = map { "<a href='$_->[0]'>$_->[1]</a>" } 625 grep { $_->[2] eq 'right' } @$others; 626my $links; 627 628# Add select links 629if (@$data) { 630 if ($selectall) { 631 my $cbname; 632 foreach my $r (@$data) { 633 foreach my $c (@$r) { 634 if (ref($c) && $c->{'type'} eq 'checkbox') { 635 $cbname = $c->{'name'}; 636 last; 637 } 638 } 639 } 640 if ($cbname) { 641 unshift(@leftlinks, &select_all_link($cbname, $formno), 642 &select_invert_link($cbname, $formno)); 643 } 644 } 645 } 646 647# Turn to HTML 648if (@rightlinks) { 649 $links = &ui_grid_table([ &ui_links_row(\@leftlinks), 650 &ui_links_row(\@rightlinks) ], 2, 100, 651 [ undef, "align='right'" ]); 652 } 653elsif (@leftlinks) { 654 $links = &ui_links_row(\@leftlinks); 655 } 656 657# Start the form, if we need one 658if (@$data) { 659 $rv .= &ui_form_start($cgi, "post"); 660 foreach my $h (@$hiddens) { 661 $rv .= &ui_hidden(@$h); 662 } 663 $rv .= $links; 664 } 665 666# Add the table 667$rv .= &ui_columns_table($heads, $width, $data, $types, $nosort, $title, 668 $emptymsg); 669 670# Add form end 671$rv .= $links; 672if (@$data) { 673 $rv .= &ui_form_end($buttons); 674 } 675 676return $rv; 677} 678 679####################### form generation functions 680 681=head2 ui_form_start(script, method, [target], [tags]) 682 683Returns HTML for the start of a a form that submits to some script. The 684parameters are : 685 686=item script - CGI script to submit to, like save.cgi. 687 688=item method - HTTP method, which must be one of 'get', 'post' or 'form-data'. If form-data is used, the target CGI must call ReadParseMime to parse parameters. 689 690=item target - Optional target window or frame for the form. 691 692=item tags - Additional HTML attributes for the form tag. 693 694=cut 695sub ui_form_start 696{ 697$ui_formcount ||= 0; 698return &theme_ui_form_start(@_) if (defined(&theme_ui_form_start)); 699my ($script, $method, $target, $tags) = @_; 700my $rv; 701$rv .= "<form class='ui_form' action='".&html_escape($script)."' ". 702 ($method eq "post" ? "method='post'" : 703 $method eq "form-data" ? 704 "method='post' enctype='multipart/form-data'" : 705 "method='get'"). 706 ($target ? " target='$target'" : ""). 707 ($tags ? " ".$tags : "").">\n"; 708return $rv; 709} 710 711=head2 ui_form_end([&buttons], [width], [nojs]) 712 713Returns HTML for the end of a form, optionally with a row of submit buttons. 714These are specified by the buttons parameter, which is an array reference 715of array refs, with the following elements : 716 717=item HTML value for the submit input for the button, or undef for none. 718 719=item Text to appear on the button. 720 721=item HTML or other inputs to appear after the button. 722 723=item Set to 1 if the button should be disabled. 724 725=item Additional HTML attributes to appear inside the button's input tag. 726 727=item Don't include generated javascript for ui_opt_textbox 728 729=cut 730sub ui_form_end 731{ 732$ui_formcount++; 733return &theme_ui_form_end(@_) if (defined(&theme_ui_form_end)); 734my ($buttons, $width, $nojs) = @_; 735my $rv; 736if ($buttons && @$buttons) { 737 $rv .= "<table class='ui_form_end_buttons' ".($width ? " width='$width'" : "")."><tr>\n"; 738 my $b; 739 foreach $b (@$buttons) { 740 if (ref($b)) { 741 $rv .= "<td".(!$width ? "" : 742 $b eq $buttons->[0] ? " align='left'" : 743 $b eq $buttons->[@$buttons-1] ? 744 " align='right'" : " align='center'").">". 745 &ui_submit($b->[1], $b->[0], $b->[3], $b->[4]). 746 ($b->[2] ? " ".$b->[2] : "")."</td>\n"; 747 } 748 elsif ($b) { 749 $rv .= "<td>$b</td>\n"; 750 } 751 else { 752 $rv .= "<td> </td>\n"; 753 } 754 } 755 $rv .= "</tr></table>\n"; 756 } 757$rv .= "</form>\n"; 758if ( !$nojs ) { 759 # When going back to a form, re-enable any text fields generated by 760 # ui_opt_textbox that aren't in the default state. 761 $rv .= "<script type='text/javascript'>\n"; 762 $rv .= "var opts = document.getElementsByClassName('ui_opt_textbox');\n"; 763 $rv .= "for(var i=0; i<opts.length; i++) {\n"; 764 $rv .= " opts[i].disabled = document.getElementsByName(opts[i].name+'_def')[0].checked;\n"; 765 $rv .= "}\n"; 766 $rv .= "</script>\n"; 767} 768return $rv; 769} 770 771=head2 ui_textbox(name, value, size, [disabled?], [maxlength], [tags]) 772 773Returns HTML for a text input box. The parameters are : 774 775=item name - Name for this input. 776 777=item value - Initial contents for the text box. 778 779=item size - Desired width in characters. 780 781=item disabled - Set to 1 if this text box should be disabled by default. 782 783=item maxlength - Maximum length of the string the user is allowed to input. 784 785=item tags - Additional HTML attributes for the <input> tag. 786 787=cut 788sub ui_textbox 789{ 790return &theme_ui_textbox(@_) if (defined(&theme_ui_textbox)); 791my ($name, $value, $size, $dis, $max, $tags) = @_; 792$size = &ui_max_text_width($size); 793return "<input class='ui_textbox' type='text' ". 794 "name=\"".&html_escape($name)."\" ". 795 "id=\"".&html_escape($name)."\" ". 796 "value=\"".&html_escape($value)."\" ". 797 "size=$size".($dis ? " disabled='true'" : ""). 798 ($max ? " maxlength='$max'" : ""). 799 ($tags ? " ".$tags : "").">"; 800} 801 802=head2 ui_filebox(name, value, size, [disabled?], [maxlength], [tags], [dir-only]) 803 804Returns HTML for a text box for choosing a file. Parameters are the same 805as ui_textbox, except for the extra dir-only option which limits the chooser 806to directories. 807 808=cut 809sub ui_filebox 810{ 811return &theme_ui_filebox(@_) if (defined(&theme_ui_filebox)); 812my ($name, $value, $size, $dis, $max, $tags, $dironly) = @_; 813return &ui_textbox($name, $value, $size, $dis, $max, $tags)." ". 814 &file_chooser_button($name, $dironly); 815} 816 817=head2 ui_bytesbox(name, bytes, [size], [disabled?]) 818 819Returns HTML for entering a number of bytes, but with friendly kB/MB/GB 820options. May truncate values to 2 decimal points! The parameters are : 821 822=item name - Name for this input. 823 824=item bytes - Initial number of bytes to show. 825 826=item size - Desired width of the text box part. 827 828=item disabled - Set to 1 if this text box should be disabled by default. 829 830=item tags - Additional HTML attributes for the <input> tag. 831 832=item defaultunits - Units mode selected by default 833 834=cut 835sub ui_bytesbox 836{ 837my ($name, $bytes, $size, $dis, $tags, $defaultunits) = @_; 838my $units = 1; 839if ($bytes eq '' && $defaultunits) { 840 $units = $defaultunits; 841 } 842elsif ($bytes >= 10*1024*1024*1024*1024) { 843 $units = 1024*1024*1024*1024; 844 } 845elsif ($bytes >= 10*1024*1024*1024) { 846 $units = 1024*1024*1024; 847 } 848elsif ($bytes >= 10*1024*1024) { 849 $units = 1024*1024; 850 } 851elsif ($bytes >= 10*1024) { 852 $units = 1024; 853 } 854else { 855 $units = 1; 856 } 857if ($bytes ne "") { 858 $bytes = sprintf("%.2f", ($bytes*1.0)/$units); 859 $bytes =~ s/\.00$//; 860 } 861$size = &ui_max_text_width($size || 8); 862return &ui_textbox($name, $bytes, $size, $dis, undef, $tags)." ". 863 &ui_select($name."_units", $units, 864 [ [ 1, "bytes" ], 865 [ 1024, "kB" ], 866 [ 1024*1024, "MB" ], 867 [ 1024*1024*1024, "GB" ], 868 [ 1024*1024*1024*1024, "TB" ] ], undef, undef, undef, $dis); 869} 870 871=head2 ui_upload(name, size, [disabled?], [tags]) 872 873Returns HTML for a file upload input, for use in a form with the form-data 874method. The parameters are : 875 876=item name - Name for this input. 877 878=item size - Desired width in characters. 879 880=item disabled - Set to 1 if this text box should be disabled by default. 881 882=item tags - Additional HTML attributes for the <input> tag. 883 884=item multiple - Set to 1 to allow uploading of multiple files 885 886=cut 887sub ui_upload 888{ 889return &theme_ui_upload(@_) if (defined(&theme_ui_upload)); 890my ($name, $size, $dis, $tags, $multiple) = @_; 891$size = &ui_max_text_width($size); 892return "<input class='ui_upload' type='file' ". 893 "name=\""."e_escape($name)."\" ". 894 "id=\""."e_escape($name)."\" ". 895 "size='$size'". 896 ($dis ? " disabled='true'" : ""). 897 ($multiple ? " multiple" : ""). 898 ($tags ? " ".$tags : "").">"; 899} 900 901=head2 ui_password(name, value, size, [disabled?], [maxlength], [tags]) 902 903Returns HTML for a password text input. Parameters are the same as ui_textbox, 904and behaviour is identical except that the user's input is not visible. 905 906=cut 907sub ui_password 908{ 909return &theme_ui_password(@_) if (defined(&theme_ui_password)); 910my ($name, $value, $size, $dis, $max, $tags) = @_; 911$size = &ui_max_text_width($size); 912return "<input class='ui_password' type='password' ". 913 "name=\""."e_escape($name)."\" ". 914 "id=\""."e_escape($name)."\" ". 915 ($value ne "" ? "value=\""."e_escape($value)."\" " : ""). 916 "size='$size'".($dis ? " disabled='true'" : ""). 917 ($max ? " maxlength='$max'" : ""). 918 ($tags ? " ".$tags : "").">"; 919} 920 921=head2 ui_hidden(name, value) 922 923Returns HTML for a hidden field with the given name and value. 924 925=cut 926sub ui_hidden 927{ 928return &theme_ui_hidden(@_) if (defined(&theme_ui_hidden)); 929my ($name, $value) = @_; 930return "<input class='ui_hidden' type='hidden' ". 931 "name=\""."e_escape($name)."\" ". 932 "id=\""."e_escape($name)."\" ". 933 "value=\""."e_escape($value)."\">\n"; 934} 935 936=head2 ui_select(name, value|&values, &options, [size], [multiple], [add-if-missing], [disabled?], [tags]) 937 938Returns HTML for a drop-down menu or multiple selection list. The parameters 939are : 940 941=item name - Name for this input. 942 943=item value - Either a single initial value, or an array reference of values if this is a multi-select list. 944 945=item options - An array reference of possible options. Each element can either be a scalar, or a two-element array ref containing a submitted value and displayed text. 946 947=item size - Desired vertical size in rows, which defaults to 1. For multi-select lists, this must be set to something larger. 948 949=item multiple - Set to 1 for a multi-select list, 0 for single. 950 951=item add-if-missing - If set to 1, any value that is not in the list of options will be automatically added (and selected). 952 953=item disabled - Set to 1 to disable this input. 954 955=item tags - Additional HTML attributes for the <select> input. 956 957=cut 958sub ui_select 959{ 960return &theme_ui_select(@_) if (defined(&theme_ui_select)); 961my ($name, $value, $opts, $size, $multiple, $missing, $dis, $tags) = @_; 962my $rv; 963$rv .= "<select class='ui_select' ". 964 "name=\""."e_escape($name)."\" ". 965 "id=\""."e_escape($name)."\" ". 966 ($size ? " size='$size'" : ""). 967 ($multiple ? " multiple" : ""). 968 ($dis ? " disabled=true" : "").($tags ? " ".$tags : "").">\n"; 969my ($o, %opt, $s); 970my %sel = ref($value) ? ( map { $_, 1 } @$value ) : ( $value, 1 ); 971foreach $o (@$opts) { 972 $o = [ $o ] if (!ref($o)); 973 $rv .= "<option value=\""."e_escape($o->[0])."\"". 974 ($sel{$o->[0]} ? " selected" : "").($o->[2] ne '' ? " ".$o->[2] : "").">". 975 ($o->[1] || $o->[0])."</option>\n"; 976 $opt{$o->[0]}++; 977 } 978foreach $s (keys %sel) { 979 if (!$opt{$s} && $missing) { 980 $rv .= "<option value=\""."e_escape($s)."\"". 981 " selected>".($s eq "" ? " " : $s)."</option>\n"; 982 } 983 } 984$rv .= "</select>\n"; 985return $rv; 986} 987 988=head2 ui_multi_select(name, &values, &options, size, [add-if-missing], [disabled?], [options-title, values-title], [width]) 989 990Returns HTML for selecting many of many from a list. By default, this is 991implemented using two <select> lists and Javascript buttons to move elements 992between them. The resulting input value is \n separated. 993 994Parameters are : 995 996=item name - HTML name for this input. 997 998=item values - An array reference of two-element array refs, containing the submitted values and descriptions of items that are selected by default. 999 1000=item options - An array reference of two-element array refs, containing the submitted values and descriptions of items that the user can select from. 1001 1002=item size - Vertical size in rows. 1003 1004=item add-if-missing - If set to 1, any entries that are in values but not in options will be added automatically. 1005 1006=item disabled - Set to 1 to disable this input by default. 1007 1008=item options-title - Optional text to appear above the list of options. 1009 1010=item values-title - Optional text to appear above the list of selected values. 1011 1012=item width - Optional width of the two lists in pixels. 1013 1014=cut 1015sub ui_multi_select 1016{ 1017return &theme_ui_multi_select(@_) if (defined(&theme_ui_multi_select)); 1018my ($name, $values, $opts, $size, $missing, $dis, 1019 $opts_title, $vals_title, $width) = @_; 1020my $rv; 1021my %already = map { $_->[0], $_ } @$values; 1022my $leftover = [ grep { !$already{$_->[0]} } @$opts ]; 1023if ($missing) { 1024 my %optsalready = map { $_->[0], $_ } @$opts; 1025 push(@$opts, grep { !$optsalready{$_->[0]} } @$values); 1026 } 1027if (!defined($width)) { 1028 $width = "200"; 1029 } 1030my $wstyle = $width ? "style='width:$width'" : ""; 1031 1032if (!$main::ui_multi_select_donejs++) { 1033 $rv .= &ui_multi_select_javascript(); 1034 } 1035$rv .= "<table cellpadding=0 cellspacing=0 class='ui_multi_select'>"; 1036if (defined($opts_title)) { 1037 $rv .= "<tr class='ui_multi_select_heads'>". 1038 "<td><b>$opts_title</b></td> ". 1039 "<td></td><td><b>$vals_title</b></td></tr>"; 1040 } 1041$rv .= "<tr class='ui_multi_select_row'>"; 1042$rv .= "<td>".&ui_select($name."_opts", [ ], $leftover, 1043 $size, 1, 0, $dis, $wstyle)."</td>\n"; 1044$rv .= "<td>".&ui_button("▶", $name."_add", $dis, 1045 "onClick='multi_select_move(\"$name\", form, 1)'")."<br>". 1046 &ui_button("◀", $name."_remove", $dis, 1047 "onClick='multi_select_move(\"$name\", form, 0)'")."</td>\n"; 1048$rv .= "<td>".&ui_select($name."_vals", [ ], $values, 1049 $size, 1, 0, $dis, $wstyle)."</td>\n"; 1050$rv .= "</tr></table>\n"; 1051$rv .= &ui_hidden($name, join("\n", map { $_->[0] } @$values)); 1052return $rv; 1053} 1054 1055=head2 ui_multi_select_javascript 1056 1057Returns <script> section for left/right select boxes. For internal use only. 1058 1059=cut 1060sub ui_multi_select_javascript 1061{ 1062return &theme_ui_multiselect_javascript() 1063 if (defined(&theme_ui_multiselect_javascript)); 1064return <<EOF; 1065<script type='text/javascript'> 1066// Move an element from the options list to the values list, or vice-versa 1067function multi_select_move(name, f, dir) 1068{ 1069var opts = f.elements[name+"_opts"]; 1070var vals = f.elements[name+"_vals"]; 1071var opts_idx = opts.selectedIndex; 1072var vals_idx = vals.selectedIndex; 1073if (dir == 1 && opts_idx >= 0) { 1074 // Moving from options to selected list 1075 for(var i=0; i<opts.options.length; i++) { 1076 var o = opts.options[i]; 1077 if (o.selected) { 1078 vals.options[vals.options.length] = 1079 new Option(o.text, o.value); 1080 opts.remove(i); 1081 i--; 1082 } 1083 } 1084 } 1085else if (dir == 0 && vals_idx >= 0) { 1086 // Moving the other way 1087 for(var i=0; i<vals.options.length; i++) { 1088 var o = vals.options[i]; 1089 if (o.selected) { 1090 opts.options[opts.options.length] = 1091 new Option(o.text, o.value); 1092 vals.remove(i); 1093 i--; 1094 } 1095 } 1096 } 1097// Fill in hidden field 1098var hid = f.elements[name]; 1099if (hid) { 1100 var hv = new Array(); 1101 for(var i=0; i<vals.options.length; i++) { 1102 hv.push(vals.options[i].value); 1103 } 1104 hid.value = hv.join("\\n"); 1105 } 1106} 1107</script> 1108EOF 1109} 1110 1111=head2 ui_radio(name, value, &options, [disabled?]) 1112 1113Returns HTML for a series of radio buttons, of which one can be selected. The 1114parameters are : 1115 1116=item name - HTML name for the radio buttons. 1117 1118=item value - Value of the button that is selected by default. 1119 1120=item options - Array ref of radio button options, each of which is an array ref containing the submitted value and description for each button. 1121 1122=item disabled - Set to 1 to disable all radio buttons by default. 1123 1124=cut 1125sub ui_radio 1126{ 1127return &theme_ui_radio(@_) if (defined(&theme_ui_radio)); 1128my ($name, $value, $opts, $dis) = @_; 1129my $rv; 1130my $o; 1131foreach $o (@$opts) { 1132 my $id = "e_escape($name."_".$o->[0]); 1133 my $label = $o->[1] || $o->[0]; 1134 my $after; 1135 if ($label =~ /^([\000-\377]*?)((<a\s+href|<input|<select|<textarea|<span|<br|<p)[\000-\377]*)$/i) { 1136 $label = $1; 1137 $after = $2; 1138 } 1139 $rv .= "<input class='ui_radio' type='radio' ". 1140 "name=\""."e_escape($name)."\" ". 1141 "value=\""."e_escape($o->[0])."\"". 1142 ($o->[0] eq $value ? " checked" : ""). 1143 ($dis ? " disabled='true'" : ""). 1144 " id=\"$id\"". 1145 ($o->[2] ? " ".$o->[2] : "")."> <label for=\"$id\">". 1146 $label."</label>".$after."\n"; 1147 } 1148return $rv; 1149} 1150 1151=head2 ui_yesno_radio(name, value, [yes], [no], [disabled?]) 1152 1153Like ui_radio, but always displays just two inputs (yes and no). The parameters 1154are : 1155 1156=item name - HTML name of the inputs. 1157 1158=item value - Option selected by default, typically 1 or 0. 1159 1160=item yes - The value for the yes option, defaulting to 1. 1161 1162=item no - The value for the no option, defaulting to 0. 1163 1164=item disabled - Set to 1 to disable all radio buttons by default. 1165 1166=cut 1167sub ui_yesno_radio 1168{ 1169my ($name, $value, $yes, $no, $dis) = @_; 1170return &theme_ui_yesno_radio(@_) if (defined(&theme_ui_yesno_radio)); 1171$yes = 1 if (!defined($yes)); 1172$no = 0 if (!defined($no)); 1173if ( $value =~ /^[0-9,.E]+$/ || !$value) { 1174 $value = int($value); 1175} 1176return &ui_radio($name, $value, [ [ $yes, $text{'yes'} ], 1177 [ $no, $text{'no'} ] ], $dis); 1178} 1179 1180=head2 ui_checkbox(name, value, label, selected?, [tags], [disabled?]) 1181 1182Returns HTML for a single checkbox. Parameters are : 1183 1184=item name - HTML name of the checkbox. 1185 1186=item value - Value that will be submitted if it is checked. 1187 1188=item label - Text to appear next to the checkbox. 1189 1190=item selected - Set to 1 for it to be checked by default. 1191 1192=item tags - Additional HTML attributes for the <input> tag. 1193 1194=item disabled - Set to 1 to disable the checkbox by default. 1195 1196=cut 1197sub ui_checkbox 1198{ 1199return &theme_ui_checkbox(@_) if (defined(&theme_ui_checkbox)); 1200my ($name, $value, $label, $sel, $tags, $dis) = @_; 1201my $after; 1202if ($label =~ /^([^<]*)(<[\000-\377]*)$/) { 1203 $label = $1; 1204 $after = $2; 1205 } 1206return "<input class='ui_checkbox' type='checkbox' ". 1207 "name=\""."e_escape($name)."\" ". 1208 "value=\""."e_escape($value)."\" ". 1209 ($sel ? " checked" : "").($dis ? " disabled='true'" : ""). 1210 " id=\""."e_escape("${name}_${value}")."\"". 1211 ($tags ? " ".$tags : "")."> ". 1212 ($label eq "" ? $after : 1213 "<label for=\""."e_escape("${name}_${value}"). 1214 "\">$label</label>$after")."\n"; 1215} 1216 1217=head2 ui_oneradio(name, value, label, selected?, [tags], [disabled?]) 1218 1219Returns HTML for a single radio button. The parameters are : 1220 1221=item name - HTML name of the radio button. 1222 1223=item value - Value that will be submitted if it is selected. 1224 1225=item label - Text to appear next to the button. 1226 1227=item selected - Set to 1 for it to be selected by default. 1228 1229=item tags - Additional HTML attributes for the <input> tag. 1230 1231=item disabled - Set to 1 to disable the radio button by default. 1232 1233=cut 1234sub ui_oneradio 1235{ 1236return &theme_ui_oneradio(@_) if (defined(&theme_ui_oneradio)); 1237my ($name, $value, $label, $sel, $tags, $dis) = @_; 1238my $id = "e_escape("${name}_${value}"); 1239my $after; 1240if ($label =~ /^([^<]*)(<[\000-\377]*)$/) { 1241 $label = $1; 1242 $after = $2; 1243 } 1244my $ret = "<input class='ui_radio' type='radio' name=\""."e_escape($name)."\" ". 1245 "value=\""."e_escape($value)."\" ". 1246 ($sel ? " checked" : "").($dis ? " disabled='true'" : ""). 1247 " id=\"$id\"". 1248 ($tags ? " ".$tags : "").">"; 1249 $ret .= " <label for=\"$id\">$label</label>" if ($label ne ''); 1250 $ret .= "$after\n"; 1251 return $ret; 1252} 1253 1254=head2 ui_textarea(name, value, rows, cols, [wrap], [disabled?], [tags]) 1255 1256Returns HTML for a multi-line text input. The function parameters are : 1257 1258=item name - Name for this HTML <textarea>. 1259 1260=item value - Default value. Multiple lines must be separated by \n. 1261 1262=item rows - Number of rows, in lines. 1263 1264=item cols - Number of columns, in characters. 1265 1266=item wrap - Wrapping mode. Can be one of soft, hard or off. 1267 1268=item disabled - Set to 1 to disable this text area by default. 1269 1270=item tags - Additional HTML attributes for the <textarea> tag. 1271 1272=cut 1273sub ui_textarea 1274{ 1275return &theme_ui_textarea(@_) if (defined(&theme_ui_textarea)); 1276my ($name, $value, $rows, $cols, $wrap, $dis, $tags) = @_; 1277$cols = &ui_max_text_width($cols, 1); 1278return "<textarea class='ui_textarea' ". 1279 "name=\""."e_escape($name)."\" ". 1280 "id=\""."e_escape($name)."\" ". 1281 "rows='$rows' cols='$cols'".($wrap ? " wrap='$wrap'" : ""). 1282 ($dis ? " disabled='true'" : ""). 1283 ($tags ? " $tags" : "").">". 1284 &html_escape($value). 1285 "</textarea>"; 1286} 1287 1288=head2 ui_user_textbox(name, value, [form], [disabled?], [tags]) 1289 1290Returns HTML for an input for selecting a Unix user. Parameters are the 1291same as ui_textbox. 1292 1293=cut 1294sub ui_user_textbox 1295{ 1296my ($name, $value, $form, $dis, $tags) = @_; 1297return &theme_ui_user_textbox(@_) if (defined(&theme_ui_user_textbox)); 1298return &ui_textbox($name, $value, 13, $dis, undef, $tags)." ". 1299 &user_chooser_button($name, 0, $form); 1300} 1301 1302=head2 ui_users_textbox(name, value, [form], [disabled?], [tags]) 1303 1304Returns HTML for an input for selecting multiple Unix users. Parameters are the 1305same as ui_textbox. 1306 1307=cut 1308sub ui_users_textbox 1309{ 1310my ($name, $value, $form, $dis, $tags) = @_; 1311return &theme_ui_users_textbox(@_) if (defined(&theme_ui_users_textbox)); 1312return &ui_textbox($name, $value, 60, $dis, undef, $tags)." ". 1313 &user_chooser_button($name, 1, $form); 1314} 1315 1316=head2 ui_group_textbox(name, value, [form], [disabled?], [tags]) 1317 1318Returns HTML for an input for selecting a Unix group. Parameters are the 1319same as ui_textbox. 1320 1321=cut 1322sub ui_group_textbox 1323{ 1324my ($name, $value, $form, $dis, $tags) = @_; 1325return &theme_ui_group_textbox(@_) if (defined(&theme_ui_group_textbox)); 1326return &ui_textbox($name, $value, 13, $dis, undef, $tags)." ". 1327 &group_chooser_button($name, 0, $form); 1328} 1329 1330=head2 ui_groups_textbox(name, value, [form], [disabled?], [tags]) 1331 1332Returns HTML for an input for selecting Unix groups. Parameters are the 1333same as ui_textbox. 1334 1335=cut 1336sub ui_groups_textbox 1337{ 1338my ($name, $value, $form, $dis, $tags) = @_; 1339return &theme_ui_groups_textbox(@_) if (defined(&theme_ui_groups_textbox)); 1340return &ui_textbox($name, $value, 60, $dis, undef, $tags)." ". 1341 &group_chooser_button($name, 1, $form); 1342} 1343 1344=head2 ui_opt_textbox(name, value, size, option1, [option2], [disabled?], [&extra-fields], [max]) 1345 1346Returns HTML for a text field that is optional, implemented by default as 1347a field with radio buttons next to it. The parameters are : 1348 1349=item name - HTML name for the text box. The radio buttons will have the same name, but with _def appended. 1350 1351=item value - Initial value, or undef if you want the default radio button selected initially. 1352 1353=item size - Width of the text box in characters. 1354 1355=item option1 - Text for the radio button for selecting that no input is being given, such as 'Default'. 1356 1357=item option2 - Text for the radio button for selecting that you will provide input. 1358 1359=item disabled - Set to 1 to disable this input by default. 1360 1361=item extra-fields - An optional array ref of field names that should be disabled by Javascript when this field is disabled. 1362 1363=item max - Optional maximum allowed input length, in characters. 1364 1365=item tags - Additional HTML attributes for the text box 1366 1367=cut 1368sub ui_opt_textbox 1369{ 1370return &theme_ui_opt_textbox(@_) if (defined(&theme_ui_opt_textbox)); 1371my ($name, $value, $size, $opt1, $opt2, $dis, $extra, $max, $tags) = @_; 1372my $dis1 = &js_disable_inputs([ $name, @$extra ], [ ]); 1373my $dis2 = &js_disable_inputs([ ], [ $name, @$extra ]); 1374my $rv; 1375$size = &ui_max_text_width($size); 1376$rv .= &ui_radio($name."_def", $value eq '' ? 1 : 0, 1377 [ [ 1, $opt1, "onClick='$dis1'" ], 1378 [ 0, $opt2 || " ", "onClick='$dis2'" ] ], $dis)."\n"; 1379$rv .= "<input class='ui_opt_textbox' type='text' ". 1380 "name=\""."e_escape($name)."\" ". 1381 "id=\""."e_escape($name)."\" ". 1382 "size=$size value=\""."e_escape($value)."\"". 1383 ($dis ? " disabled='true'" : ""). 1384 ($max ? " maxlength='$max'" : ""). 1385 ($tags ? " ".$tags : "").">"; 1386return $rv; 1387} 1388 1389=head2 ui_submit(label, [name], [disabled?], [tags]) 1390 1391Returns HTML for a form submit button. Parameters are : 1392 1393=item label - Text to appear on the button. 1394 1395=item name - Optional HTML name for the button. Useful if the CGI it submits to needs to know which of several buttons was clicked. 1396 1397=item disabled - Set to 1 if this button should be disabled by default. 1398 1399=item tags - Additional HTML attributes for the <input> tag. 1400 1401=cut 1402sub ui_submit 1403{ 1404return &theme_ui_submit(@_) if (defined(&theme_ui_submit)); 1405my ($label, $name, $dis, $tags) = @_; 1406return "<input class='ui_submit' type='submit'". 1407 ($name ne '' ? " name=\""."e_escape($name)."\"" : ""). 1408 ($name ne '' ? " id=\""."e_escape($name)."\"" : ""). 1409 " value=\""."e_escape($label)."\"". 1410 ($dis ? " disabled='true'" : ""). 1411 ($tags ? " ".$tags : "").">\n"; 1412} 1413 1414=head2 ui_reset(label, [disabled?], [tags]) 1415 1416Returns HTML for a form reset button, which clears all fields when clicked. 1417Parameters are : 1418 1419=item label - Text to appear on the button. 1420 1421=item disabled - Set to 1 if this button should be disabled by default. 1422 1423=item tags - Additional HTML attributes for the <input> tag. 1424 1425=cut 1426sub ui_reset 1427{ 1428return &theme_ui_reset(@_) if (defined(&theme_ui_reset)); 1429my ($label, $dis, $tags) = @_; 1430return "<input class='ui_reset' type='reset' value=\""."e_escape($label)."\"". 1431 ($dis ? " disabled='true'" : ""). 1432 ($tags ? " ".$tags : "").">\n"; 1433} 1434 1435=head2 ui_button(label, [name], [disabled?], [tags]) 1436 1437Returns HTML for a form button, which doesn't do anything when clicked unless 1438you add some Javascript to it. The parameters are : 1439 1440=item label - Text to appear on the button. 1441 1442=item name - HTML name for this input. 1443 1444=item disabled - Set to 1 if this button should be disabled by default. 1445 1446=item tags - Additional HTML attributes for the <input> tag, typically Javascript inside an onClick attribute. 1447 1448=cut 1449sub ui_button 1450{ 1451return &theme_ui_button(@_) if (defined(&theme_ui_button)); 1452my ($label, $name, $dis, $tags) = @_; 1453return "<input class='ui_button' type='button'". 1454 ($name ne '' ? " name=\""."e_escape($name)."\"" : ""). 1455 ($name ne '' ? " id=\""."e_escape($name)."\"" : ""). 1456 " value=\""."e_escape($label)."\"". 1457 ($dis ? " disabled='true'" : ""). 1458 ($tags ? " ".$tags : "").">\n"; 1459} 1460 1461=head2 ui_date_input(day, month, year, day-name, month-name, year-name, [disabled?]) 1462 1463Returns HTML for a date-selection field, with day, month and year inputs. 1464The parameters are : 1465 1466=item day - Initial day of the month. 1467 1468=item month - Initial month of the year, indexed from 1. 1469 1470=item year - Initial year, four-digit. 1471 1472=item day-name - Name of the day input field. 1473 1474=item month-name - Name of the month select field. 1475 1476=item year-name - Name of the year input field. 1477 1478=item disabled - Set to 1 to disable all fields by default. 1479 1480=cut 1481sub ui_date_input 1482{ 1483return &theme_ui_date_input(@_) if (defined(&theme_ui_date_input)); 1484my ($day, $month, $year, $dayname, $monthname, $yearname, $dis) = @_; 1485my $rv; 1486$rv .= "<span class='ui_data'>"; 1487$rv .= &ui_textbox($dayname, $day, 3, $dis); 1488$rv .= "/"; 1489$rv .= &ui_select($monthname, $month, 1490 [ map { [ $_, $text{"smonth_$_"} ] } (1 .. 12) ], 1491 1, 0, 0, $dis); 1492$rv .= "/"; 1493$rv .= &ui_textbox($yearname, $year, 5, $dis); 1494$rv .= "</span>"; 1495return $rv; 1496} 1497 1498=head2 ui_buttons_start 1499 1500Returns HTML for the start of a block of action buttons with descriptions, as 1501generated by ui_buttons_row. Some example code : 1502 1503 print ui_buttons_start(); 1504 print ui_buttons_row('start.cgi', 'Start server', 1505 'Click this button to start the server process'); 1506 print ui_buttons_row('stop.cgi', 'Stop server', 1507 'Click this button to stop the server process'); 1508 print ui_buttons_end(); 1509 1510=cut 1511sub ui_buttons_start 1512{ 1513return &theme_ui_buttons_start(@_) if (defined(&theme_ui_buttons_start)); 1514return "<table width='100%' class='ui_buttons_table'>\n"; 1515} 1516 1517=head2 ui_buttons_end 1518 1519Returns HTML for the end of a block started by ui_buttons_start. 1520 1521=cut 1522sub ui_buttons_end 1523{ 1524return &theme_ui_buttons_end(@_) if (defined(&theme_ui_buttons_end)); 1525return "</table>\n"; 1526} 1527 1528=head2 ui_buttons_row(script, button-label, description, [hiddens], [after-submit], [before-submit]) 1529 1530Returns HTML for a button with a description next to it, and perhaps other 1531inputs. The parameters are : 1532 1533=item script - CGI script that this button submits to, like start.cgi. 1534 1535=item button-label - Text to appear on the button. 1536 1537=item description - Text to appear next to the button, describing in more detail what it does. 1538 1539=item hiddens - HTML for hidden fields to include in the form this function generates. 1540 1541=item after-submit - HTML for text or inputs to appear after the submit button. 1542 1543=item before-submit - HTML for text or inputs to appear before the submit button. 1544 1545=cut 1546sub ui_buttons_row 1547{ 1548return &theme_ui_buttons_row(@_) if (defined(&theme_ui_buttons_row)); 1549my ($script, $label, $desc, $hiddens, $after, $before) = @_; 1550if (ref($hiddens)) { 1551 $hiddens = join("\n", map { &ui_hidden(@$_) } @$hiddens); 1552 } 1553return "<form action='$script' class='ui_buttons_form' method='post'>\n". 1554 $hiddens. 1555 "<tr class='ui_buttons_row'> ". 1556 "<td nowrap width='20%' valign='top' class='ui_buttons_label'>". 1557 ($before ? $before." " : ""). 1558 &ui_submit($label).($after ? " ".$after : "")."</td>\n". 1559 "<td width='80%' valign='top' class='ui_buttons_value'>". 1560 $desc."</td></tr>\n". 1561 "</form>\n"; 1562} 1563 1564=head2 ui_buttons_hr([title]) 1565 1566Returns HTML for a separator row, for use inside a ui_buttons_start block. 1567 1568=cut 1569sub ui_buttons_hr 1570{ 1571my ($title) = @_; 1572return &theme_ui_buttons_hr(@_) if (defined(&theme_ui_buttons_hr)); 1573if ($title) { 1574 return "<tr class='ui_buttons_hr'><td colspan='2'><table cellpadding='0' cellspacing='0' width='100%'><tr><td width='50%'><hr></td><td nowrap>$title</td><td width='50%'><hr></td></tr></table></td></tr>\n"; 1575 } 1576else { 1577 return "<tr class='ui_buttons_hr'><td colspan='2'><hr></td></tr>\n"; 1578 } 1579} 1580 1581####################### header and footer functions 1582 1583=head2 ui_post_header([subtext]) 1584 1585Returns HTML to appear directly after a standard header() call. This is never 1586called directly - instead, ui_print_header calls it. But it can be overridden 1587by themes. 1588 1589=cut 1590sub ui_post_header 1591{ 1592return &theme_ui_post_header(@_) if (defined(&theme_ui_post_header)); 1593my ($text) = @_; 1594my $rv; 1595$rv .= "<center class='ui_post_header'><font size='+1'>$text</font></center>\n" if (defined($text)); 1596if (!$tconfig{'nohr'} && !$tconfig{'notophr'}) { 1597 $rv .= "<hr id='post_header_hr'>\n"; 1598 } 1599return $rv; 1600} 1601 1602=head2 ui_pre_footer 1603 1604Returns HTML to appear directly before a standard footer() call. This is never 1605called directly - instead, ui_print_footer calls it. But it can be overridden 1606by themes. 1607 1608=cut 1609sub ui_pre_footer 1610{ 1611return &theme_ui_pre_footer(@_) if (defined(&theme_ui_pre_footer)); 1612my $rv; 1613if (!$tconfig{'nohr'} && !$tconfig{'nobottomhr'}) { 1614 $rv .= "<hr id='pre_footer_hr'>\n"; 1615 } 1616return $rv; 1617} 1618 1619=head2 ui_print_header(subtext, image, [help], [config], [nomodule], [nowebmin], [rightside], [head-stuff], [body-stuff], [below]) 1620 1621Print HTML for a header with the post-header line. The args are the same 1622as those passed to header(), defined in web-lib-funcs.pl, with the addition 1623of the subtext parameter : 1624 1625=item subtext - Text to display below the title 1626 1627=item title - The text to show at the top of the page 1628 1629=item image - An image to show instead of the title text. This is typically left blank. 1630 1631=item help - If set, this is the name of a help page that will be linked to in the title. 1632 1633=item config - If set to 1, the title will contain a link to the module's config page. 1634 1635=item nomodule - If set to 1, there will be no link in the title section to the module's index. 1636 1637=item nowebmin - If set to 1, there will be no link in the title section to the Webmin index. 1638 1639=item rightside - HTML to be shown on the right-hand side of the title. Can contain multiple lines, separated by <br>. Typically this is used for links to stop, start or restart servers. 1640 1641=item head-stuff - HTML to be included in the <head> section of the page. 1642 1643=item body-stuff - HTML attributes to be include in the <body> tag. 1644 1645=item below - HTML to be displayed below the title. Typically this is used for application or server version information. 1646 1647 1648 1649=cut 1650sub ui_print_header 1651{ 1652&load_theme_library(); 1653return &theme_ui_print_header(@_) if (defined(&theme_ui_print_header)); 1654my ($text, @args) = @_; 1655&header(@args); 1656print &ui_post_header($text); 1657} 1658 1659=head2 ui_print_unbuffered_header(subtext, args...) 1660 1661Like ui_print_header, but ensures that output for this page is not buffered 1662or contained in a table. This should be called by scripts that are producing 1663output while performing some long-running process. 1664 1665=cut 1666sub ui_print_unbuffered_header 1667{ 1668my @args = @_; 1669&load_theme_library(); 1670return &theme_ui_print_unbuffered_header(@args) 1671 if (defined(&theme_ui_print_unbuffered_header)); 1672$| = 1; 1673$theme_no_table = 1; 1674$args[9] .= " " if ($args[9]); 1675$args[9] .= " data-pagescroll=true"; 1676&ui_print_header(@args); 1677} 1678 1679=head2 ui_print_footer(args...) 1680 1681Print HTML for a footer with the pre-footer line. Args are the same as those 1682passed to footer(). 1683 1684=cut 1685sub ui_print_footer 1686{ 1687return &theme_ui_print_footer(@_) if (defined(&theme_ui_print_footer)); 1688my @args = @_; 1689print &ui_pre_footer(); 1690&footer(@args); 1691} 1692 1693=head2 ui_config_link(text, &subs) 1694 1695Returns HTML for a module config link. The first non-null sub will be 1696replaced with the appropriate URL for the module's config page. 1697 1698=cut 1699sub ui_config_link 1700{ 1701return &theme_ui_config_link(@_) if (defined(&theme_ui_config_link)); 1702my ($text, $subs) = @_; 1703my $m = &get_module_name(); 1704my @subs = map { $_ || "../config.cgi?$m" } 1705 ($subs ? @$subs : ( undef )); 1706return "<p>".&text($text, @subs)."<p>\n"; 1707} 1708 1709=head2 ui_print_endpage(text) 1710 1711Prints HTML for an error message followed by a page footer with a link to 1712/, then exits. Good for main page error messages. 1713 1714=cut 1715sub ui_print_endpage 1716{ 1717return &theme_ui_print_endpage(@_) if (defined(&theme_ui_print_endpage)); 1718my ($text) = @_; 1719print $text,"<p class='ui_footer'>\n"; 1720print "</p>\n"; 1721&ui_print_footer("/", $text{'index'}); 1722exit; 1723} 1724 1725=head2 ui_subheading(text, ...) 1726 1727Returns HTML for a section heading whose message is the given text strings. 1728 1729=cut 1730sub ui_subheading 1731{ 1732return &theme_ui_subheading(@_) if (defined(&theme_ui_subheading)); 1733return "<h3 class='ui_subheading'>".join("", @_)."</h3>\n"; 1734} 1735 1736=head2 ui_links_row(&links) 1737 1738Returns HTML for a row of links, like select all / invert selection / add.. 1739Each element of the links array ref should be an HTML fragment like : 1740 1741 <a href='user_form.cgi'>Create new user</a> 1742 1743=cut 1744sub ui_links_row 1745{ 1746return &theme_ui_links_row(@_) if (defined(&theme_ui_links_row)); 1747my ($links) = @_; 1748return @$links ? join("\n|\n", @$links)."<br>\n" 1749 : ""; 1750} 1751 1752########################### collapsible section / tab functions 1753 1754=head2 ui_hidden_javascript 1755 1756Returns <script> and <style> sections for hiding functions and CSS. For 1757internal use only. 1758 1759=cut 1760sub ui_hidden_javascript 1761{ 1762return &theme_ui_hidden_javascript(@_) 1763 if (defined(&theme_ui_hidden_javascript)); 1764my $rv; 1765my $imgdir = "$gconfig{'webprefix'}/images"; 1766my ($jscb, $jstb) = ($cb, $tb); 1767$jscb =~ s/'/\\'/g; 1768$jstb =~ s/'/\\'/g; 1769 1770return <<EOF; 1771<style type='text/css'> 1772.opener_shown {display:inline} 1773.opener_hidden {display:none} 1774</style> 1775<script type='text/javascript'> 1776// Open or close a hidden section 1777function hidden_opener(divid, openerid) 1778{ 1779var divobj = document.getElementById(divid); 1780var openerobj = document.getElementById(openerid); 1781if (divobj.className == 'opener_shown') { 1782 divobj.className = 'opener_hidden'; 1783 openerobj.innerHTML = '<img border=0 src=$imgdir/closed.gif>'; 1784 } 1785else { 1786 divobj.className = 'opener_shown'; 1787 openerobj.innerHTML = '<img border=0 src=$imgdir/open.gif>'; 1788 } 1789} 1790 1791// Show a tab 1792function select_tab(name, tabname, form) 1793{ 1794var tabnames = document[name+'_tabnames']; 1795var tabtitles = document[name+'_tabtitles']; 1796for(var i=0; i<tabnames.length; i++) { 1797 var tabobj = document.getElementById('tab_'+tabnames[i]); 1798 var divobj = document.getElementById('div_'+tabnames[i]); 1799 var title = tabtitles[i]; 1800 if (tabnames[i] == tabname) { 1801 // Selected table 1802 tabobj.innerHTML = '<table cellpadding="0" cellspacing="0"><tr>'+ 1803 '<td valign=top $jscb>'+ 1804 '<img src=$imgdir/lc2.gif alt=""></td>'+ 1805 '<td $jscb nowrap>'+ 1806 ' <b>'+title+'</b> </td>'+ 1807 '<td valign=top $jscb>'+ 1808 '<img src=$imgdir/rc2.gif alt=""></td>'+ 1809 '</tr></table>'; 1810 divobj.className = 'opener_shown'; 1811 } 1812 else { 1813 // Non-selected tab 1814 tabobj.innerHTML = '<table cellpadding="0" cellspacing="0"><tr>'+ 1815 '<td valign="top" $jstb>'+ 1816 '<img src="$imgdir/lc1.gif" alt=""></td>'+ 1817 '<td $jstb nowrap>'+ 1818 ' <a href=\\'\\' onClick=\\'return select_tab("'+ 1819 name+'", "'+tabnames[i]+'")\\'>'+title+'</a> </td>'+ 1820 '<td valign="top" $jstb>'+ 1821 '<img src="$imgdir/rc1.gif" alt=""></td>'+ 1822 '</tr></table>'; 1823 divobj.className = 'opener_hidden'; 1824 } 1825 } 1826if (document.forms[0] && document.forms[0][name]) { 1827 document.forms[0][name].value = tabname; 1828 } 1829return false; 1830} 1831</script> 1832EOF 1833} 1834 1835=head2 ui_hidden_start(title, name, status, thisurl) 1836 1837Returns HTML for the start of a collapsible hidden section, such as for 1838advanced options. When clicked on, the section header will expand to display 1839whatever is between this function and ui_hidden_end. The parameters are : 1840 1841=item title - Text for the start of this hidden section. 1842 1843=item name - A unique name for this section. 1844 1845=item status - 1 if it should be initially open, 0 if not. 1846 1847=item thisurl - URL of the current page. This is used by themes on devices that don't support Javascript to implement the opening and closing. 1848 1849=cut 1850sub ui_hidden_start 1851{ 1852return &theme_ui_hidden_start(@_) if (defined(&theme_ui_hidden_start)); 1853my ($title, $name, $status, $url) = @_; 1854my $rv; 1855if (!$main::ui_hidden_start_donejs++) { 1856 $rv .= &ui_hidden_javascript(); 1857 } 1858my $divid = "hiddendiv_$name"; 1859my $openerid = "hiddenopener_$name"; 1860my $defimg = $status ? "open.gif" : "closed.gif"; 1861my $defclass = $status ? 'opener_shown' : 'opener_hidden'; 1862$rv .= "<a href=\"javascript:hidden_opener('$divid', '$openerid')\" id='$openerid'><img border=0 src='$gconfig{'webprefix'}/images/$defimg' alt='*'></a>\n"; 1863$rv .= "<a href=\"javascript:hidden_opener('$divid', '$openerid')\">$title</a><br>\n"; 1864$rv .= "<div class='$defclass' id='$divid'>\n"; 1865return $rv; 1866} 1867 1868=head2 ui_hidden_end(name) 1869 1870Returns HTML for the end of a hidden section, started by ui_hidden_start. 1871 1872=cut 1873sub ui_hidden_end 1874{ 1875return &theme_ui_hidden_end(@_) if (defined(&theme_ui_hidden_end)); 1876my ($name) = @_; 1877return "</div>\n"; 1878} 1879 1880=head2 ui_hidden_table_row_start(title, name, status, thisurl) 1881 1882Similar to ui_hidden_start, but for use within a table started with 1883ui_table_start. I recommend against using this where possible, as it can 1884be difficult for some themes to implement. 1885 1886=cut 1887sub ui_hidden_table_row_start 1888{ 1889return &theme_ui_hidden_table_row_start(@_) 1890 if (defined(&theme_ui_hidden_table_row_start)); 1891my ($title, $name, $status, $url) = @_; 1892my ($rv, $rrv); 1893if (!$main::ui_hidden_start_donejs++) { 1894 $rv .= &ui_hidden_javascript(); 1895 } 1896my $divid = "hiddendiv_$name"; 1897my $openerid = "hiddenopener_$name"; 1898my $defimg = $status ? "open.gif" : "closed.gif"; 1899my $defclass = $status ? 'opener_shown' : 'opener_hidden'; 1900if ($title) { 1901 $rrv .= "<a href=\"javascript:hidden_opener('$divid', '$openerid')\" id='$openerid'><img border=0 src='$gconfig{'webprefix'}/images/$defimg'></a>\n"; 1902 $rrv .= "<a href=\"javascript:hidden_opener('$divid', '$openerid')\">$title</a><br>\n"; 1903 $rv .= &ui_table_row(undef, $rrv, $main::ui_table_cols); 1904 } 1905$rv .= "</table>\n"; 1906$rv .= "<div class='$defclass' id='$divid'>\n"; 1907$rv .= "<table width='100%'>\n"; 1908return $rv; 1909} 1910 1911=head2 ui_hidden_table_row_end(name) 1912 1913Returns HTML to end a block started by ui_hidden_table_start. 1914 1915=cut 1916sub ui_hidden_table_row_end 1917{ 1918return &theme_ui_hidden_table_row_end(@_) 1919 if (defined(&theme_ui_hidden_table_row_end)); 1920my ($name) = @_; 1921return "</table></div><table width='100%'>\n"; 1922} 1923 1924=head2 ui_hidden_table_start(heading, [tabletags], [cols], name, status, [&default-tds], [rightheading]) 1925 1926Returns HTML for the start of a form block into which labelled inputs can 1927be placed, which is collapsible by clicking on the header. Basically the same 1928as ui_table_start, and must contain HTML generated by ui_table_row. 1929 1930The parameters are : 1931 1932=item heading - Text to show at the top of the form. 1933 1934=item tabletags - HTML attributes to put in the outer <table>, typically something like width=100%. 1935 1936=item cols - Desired number of columns for labels and fields. Defaults to 4, but can be 2 for forms with lots of wide inputs. 1937 1938=item name - A unique name for this table. 1939 1940=item status - Set to 1 if initially open, 0 if initially closed. 1941 1942=item default-tds - An optional array reference of HTML attributes for the <td> tags in each row of the table. 1943 1944=item right-heading - HTML to appear in the heading, aligned to the right. 1945 1946=cut 1947sub ui_hidden_table_start 1948{ 1949return &theme_ui_hidden_table_start(@_) 1950 if (defined(&theme_ui_hidden_table_start)); 1951my ($heading, $tabletags, $cols, $name, $status, $tds, $rightheading) = @_; 1952my $rv; 1953if (!$main::ui_hidden_start_donejs++) { 1954 $rv .= &ui_hidden_javascript(); 1955 } 1956my $divid = "hiddendiv_$name"; 1957my $openerid = "hiddenopener_$name"; 1958my $defimg = $status ? "open.gif" : "closed.gif"; 1959my $defclass = $status ? 'opener_shown' : 'opener_hidden'; 1960my $text = defined($tconfig{'cs_text'}) ? $tconfig{'cs_text'} : 1961 defined($gconfig{'cs_text'}) ? $gconfig{'cs_text'} : "000000"; 1962$rv .= "<table class='ui_table' border $tabletags>\n"; 1963my $colspan = 1; 1964if (defined($heading) || defined($rightheading)) { 1965 $rv .= "<tr".($tb ? " ".$tb : "")."><td>"; 1966 if (defined($heading)) { 1967 $rv .= "<a href=\"javascript:hidden_opener('$divid', '$openerid')\" id='$openerid'><img border=0 src='$gconfig{'webprefix'}/images/$defimg'></a> <a href=\"javascript:hidden_opener('$divid', '$openerid')\"><b><font color='#$text'>$heading</font></b></a></td>"; 1968 } 1969 if (defined($rightheading)) { 1970 $rv .= "<td align='right'>$rightheading</td>"; 1971 $colspan++; 1972 } 1973 $rv .= "</td> </tr>\n"; 1974 } 1975$rv .= "<tr".($cb ? " ".$cb : "")."><td colspan='$colspan'><div class='$defclass' id='$divid'><table width='100%'>\n"; 1976$main::ui_table_cols = $cols || 4; 1977$main::ui_table_pos = 0; 1978$main::ui_table_default_tds = $tds; 1979return $rv; 1980} 1981 1982=head2 ui_hidden_table_end(name) 1983 1984Returns HTML for the end of a form block with hiding, as started by 1985ui_hidden_table_start. 1986 1987=cut 1988sub ui_hidden_table_end 1989{ 1990my ($name) = @_; 1991return &theme_ui_hidden_table_end(@_) if (defined(&theme_ui_hidden_table_end)); 1992return "</table></div></td></tr></table>\n"; 1993} 1994 1995=head2 ui_tabs_start(&tabs, name, selected, show-border) 1996 1997Returns a row of tabs from which one can be selected, displaying HTML 1998associated with that tab. The parameters are : 1999 2000=item tabs - An array reference of array refs, each of which contains the value and user-visible text for a tab. 2001 2002=item name - Name of the HTML field into which the selected tab will be placed. 2003 2004=item selected - Value for the tab selected by default. 2005 2006=item show-border - Set to 1 if there should be a border around the contents of the tabs. 2007 2008Example code : 2009 2010 @tabs = ( [ 'list', 'List services' ], 2011 [ 'install', 'Install new service' ] ); 2012 print ui_tabs_start(\@tabs, 'mode', 'list'); 2013 2014 print ui_tabs_start_tab('mode', 'list'); 2015 generate_service_list(); 2016 print ui_tabs_end_tab('mode', 'list'); 2017 2018 print ui_tabs_start_tab('mode', 'install'); 2019 generate_install_form(); 2020 print ui_tabs_end_tab('mode', 'install); 2021 2022 print ui_tabs_end(); 2023 2024=cut 2025sub ui_tabs_start 2026{ 2027return &theme_ui_tabs_start(@_) if (defined(&theme_ui_tabs_start)); 2028my ($tabs, $name, $sel, $border) = @_; 2029my $rv; 2030if (!$main::ui_hidden_start_donejs++) { 2031 $rv .= &ui_hidden_javascript(); 2032 } 2033 2034# Build list of tab titles and names 2035my $tabnames = "[".join(",", map { "\""."e_escape($_->[0])."\"" } @$tabs)."]"; 2036my $tabtitles = "[".join(",", map { "\""."e_escape($_->[1])."\"" } @$tabs)."]"; 2037$rv .= "<script type='text/javascript'>\n"; 2038$rv .= "document.${name}_tabnames = $tabnames;\n"; 2039$rv .= "document.${name}_tabtitles = $tabtitles;\n"; 2040$rv .= "</script>\n"; 2041 2042# Output the tabs 2043my $imgdir = "$gconfig{'webprefix'}/images"; 2044$rv .= &ui_hidden($name, $sel)."\n"; 2045$rv .= "<table border='0' cellpadding='0' cellspacing='0' class='ui_tabs'>\n"; 2046$rv .= "<tr><td bgcolor='#ffffff' colspan='".(scalar(@$tabs)*2+1)."'>"; 2047if ($ENV{'HTTP_USER_AGENT'} !~ /msie/i) { 2048 # For some reason, the 1-pixel space above the tabs appears huge on IE! 2049 $rv .= "<img src='$imgdir/1x1.gif'>"; 2050 } 2051$rv .= "</td></tr>\n"; 2052$rv .= "<tr>\n"; 2053$rv .= "<td bgcolor='#ffffff' width='1'><img src='$imgdir/1x1.gif'></td>\n"; 2054foreach my $t (@$tabs) { 2055 if ($t ne $$tabs[0]) { 2056 # Spacer 2057 $rv .= "<td width='2' bgcolor='#ffffff' class='ui_tab_spacer'>". 2058 "<img src='$imgdir/1x1.gif'></td>\n"; 2059 } 2060 my $tabid = "tab_".$t->[0]; 2061 $rv .= "<td id='${tabid}' class='ui_tab'>"; 2062 $rv .= "<table cellpadding='0' cellspacing='0' border='0'><tr>"; 2063 if ($t->[0] eq $sel) { 2064 # Selected tab 2065 $rv .= "<td valign='top'".($cb ? " ".$cb : "")." class='selectedTabLeft'>". 2066 "<img src='$imgdir/lc2.gif' alt=\"\"></td>"; 2067 $rv .= "<td".($cb ? " ".$cb : "")." nowrap class='selectedTabMiddle'>". 2068 " <b>$t->[1]</b> </td>"; 2069 $rv .= "<td valign=top".($cb ? " ".$cb : "")." class='selectedTabRight'>". 2070 "<img src=$imgdir/rc2.gif alt=\"\"></td>"; 2071 } 2072 else { 2073 # Other tab (which has a link) 2074 $rv .= "<td valign='top'".($tb ? " ".$tb : "").">". 2075 "<img src='$imgdir/lc1.gif' alt=\"\"></td>"; 2076 $rv .= "<td".($tb ? " ".$tb : "")." nowrap>". 2077 " <a href='$t->[2]' ". 2078 "onClick='return select_tab(\"$name\", \"$t->[0]\")'>". 2079 "$t->[1]</a> </td>"; 2080 $rv .= "<td valign='top'".($tb ? " ".$tb : "")." >". 2081 "<img src='$imgdir/rc1.gif' ". 2082 "alt=\"\"></td>"; 2083 $rv .= "</td>\n"; 2084 } 2085 $rv .= "</tr></table>"; 2086 $rv .= "</td>\n"; 2087 } 2088$rv .= "<td bgcolor='#ffffff' width='1'><img src='$imgdir/1x1.gif'></td>\n"; 2089$rv .= "</table>\n"; 2090 2091if ($border) { 2092 # All tabs are within a grey box 2093 $rv .= "<table width='100%' cellpadding='0' cellspacing='0' border='0' ". 2094 "class='ui_tabs_box'>\n"; 2095 $rv .= "<tr> <td bgcolor='#ffffff' rowspan='3' width='1'><img src='$imgdir/1x1.gif'></td>\n"; 2096 $rv .= "<td".($cb ? " ".$cb : "")." colspan='3' height='2'><img src='$imgdir/1x1.gif'></td> </tr>\n"; 2097 $rv .= "<tr> <td".($cb ? " ".$cb : "")." width='2'><img src='$imgdir/1x1.gif'></td>\n"; 2098 $rv .= "<td valign='top'>"; 2099 } 2100$main::ui_tabs_selected = $sel; 2101return $rv; 2102} 2103 2104=head2 ui_tabs_end(show-border) 2105 2106Returns HTML to end a block started by ui_tabs_start. The show-border parameter 2107must match the parameter with the same name in the start function. 2108 2109=cut 2110sub ui_tabs_end 2111{ 2112return &theme_ui_tabs_end(@_) if (defined(&theme_ui_tabs_end)); 2113my ($border) = @_; 2114my $rv; 2115my $imgdir = "$gconfig{'webprefix'}/images"; 2116if ($border) { 2117 $rv .= "</td>\n"; 2118 $rv .= "<td".($cb ? " ".$cb : "")." width='2'><img src='$imgdir/1x1.gif'></td>\n"; 2119 $rv .= "</tr>\n"; 2120 $rv .= "<tr> <td".($cb ? " ".$cb : "")." colspan='3' height='2'><img src='$imgdir/1x1.gif'></td> </tr>\n"; 2121 $rv .= "</table>\n"; 2122 } 2123return $rv; 2124} 2125 2126=head2 ui_tabs_start_tab(name, tab) 2127 2128Must be called before outputting the HTML for the named tab, and returns HTML 2129for the required <div> block. 2130 2131=cut 2132sub ui_tabs_start_tab 2133{ 2134return &theme_ui_tabs_start_tab(@_) if (defined(&theme_ui_tabs_start_tab)); 2135my ($name, $tab) = @_; 2136my $defclass = $tab eq $main::ui_tabs_selected ? 2137 'opener_shown' : 'opener_hidden'; 2138my $rv = "<div id='div_$tab' class='$defclass ui_tabs_start'>\n"; 2139return $rv; 2140} 2141 2142=head2 ui_tabs_start_tabletab(name, tab) 2143 2144Behaves like ui_tabs_start_tab, but for use within a ui_table_start block. 2145I recommend against using this where possible, as it is difficult for themes 2146to implement. 2147 2148=cut 2149sub ui_tabs_start_tabletab 2150{ 2151return &theme_ui_tabs_start_tabletab(@_) 2152 if (defined(&theme_ui_tabs_start_tabletab)); 2153my $div = &ui_tabs_start_tab(@_); 2154return "</table>\n".$div."<table width='100%'>\n"; 2155} 2156 2157=head2 ui_tabs_end_tab 2158 2159Returns HTML for the end of a block started by ui_tabs_start_tab. 2160 2161=cut 2162sub ui_tabs_end_tab 2163{ 2164return &theme_ui_tabs_end_tab(@_) if (defined(&theme_ui_tabs_end_tab)); 2165return "</div>\n"; 2166} 2167 2168=head2 ui_tabs_end_tabletab 2169 2170Returns HTML for the end of a block started by ui_tabs_start_tabletab. 2171 2172=cut 2173sub ui_tabs_end_tabletab 2174{ 2175return &theme_ui_tabs_end_tabletab(@_) 2176 if (defined(&theme_ui_tabs_end_tabletab)); 2177return "</table></div><table width='100%'>\n"; 2178} 2179 2180=head2 ui_max_text_width(width, [text-area?]) 2181 2182Returns a new width for a text field, based on theme settings. For internal 2183use only. 2184 2185=cut 2186sub ui_max_text_width 2187{ 2188my ($w, $ta) = @_; 2189my $max = $ta ? $tconfig{'maxareawidth'} : $tconfig{'maxboxwidth'}; 2190return $max && $w > $max ? $max : $w; 2191} 2192 2193####################### radio hidden functions 2194 2195=head2 ui_radio_selector(&opts, name, selected, [dropdown-mode]) 2196 2197Returns HTML for a set of radio buttons, each of which shows a different 2198block of HTML when selected. The parameters are : 2199 2200=item opts - An array ref to arrays containing [ value, label, html ] 2201 2202=item name - HTML name for the radio buttons 2203 2204=item selected - Value for the initially selected button. 2205 2206=item dropdown - Use a <select> dropdown menu instead of radio buttons 2207 2208=cut 2209sub ui_radio_selector 2210{ 2211return &theme_ui_radio_selector(@_) if (defined(&theme_ui_radio_selector)); 2212my ($opts, $name, $sel, $dropdown) = @_; 2213my $rv; 2214if (!$main::ui_radio_selector_donejs++) { 2215 $rv .= &ui_radio_selector_javascript(); 2216 } 2217my $optnames = 2218 "[".join(",", map { "\"".&html_escape($_->[0])."\"" } @$opts)."]"; 2219if ($dropdown) { 2220 $rv .= &ui_select($name, $sel, 2221 [ map { [ $_->[0], $_->[1] ] } @$opts ], 2222 1, 0, 0, 0, 2223 "onChange='selector_show(\"$name\", $name.value, $optnames)'"); 2224 } 2225else { 2226 foreach my $o (@$opts) { 2227 $rv .= &ui_oneradio($name, $o->[0], $o->[1], $sel eq $o->[0], 2228 "onClick='selector_show(\"$name\", \"$o->[0]\", $optnames)'"); 2229 } 2230 } 2231$rv .= "<br>\n"; 2232foreach my $o (@$opts) { 2233 my $cls = $o->[0] eq $sel ? "selector_shown" : "selector_hidden"; 2234 $rv .= "<div id='sel_${name}_$o->[0]' class='$cls'>".$o->[2]."</div>\n"; 2235 } 2236return $rv; 2237} 2238 2239sub ui_radio_selector_javascript 2240{ 2241return <<EOF; 2242<style type='text/css'> 2243.selector_shown {display:inline} 2244.selector_hidden {display:none} 2245</style> 2246<script type='text/javascript'> 2247function selector_show(name, value, values) 2248{ 2249for(var i=0; i<values.length; i++) { 2250 var divobj = document.getElementById('sel_'+name+'_'+values[i]); 2251 divobj.className = value == values[i] ? 'selector_shown' 2252 : 'selector_hidden'; 2253 } 2254} 2255</script> 2256EOF 2257} 2258 2259####################### grid layout functions 2260 2261=head2 ui_grid_table(&elements, columns, [width-percent], [&tds], [tabletags], [title]) 2262 2263Given a list of HTML elements, formats them into a table with the given 2264number of columns. However, themes are free to override this to use fewer 2265columns where space is limited. Parameters are : 2266 2267=item elements - An array reference of table elements, each of which can be any HTML you like. 2268 2269=item columns - Desired number of columns in the grid. 2270 2271=item width-percent - Optional desired width as a percentage. 2272 2273=item tds - Array ref of HTML attributes for <td> tags in the tables. 2274 2275=item tabletags - HTML attributes for the <table> tag. 2276 2277=item title - Optional title to add to the top of the grid. 2278 2279=cut 2280sub ui_grid_table 2281{ 2282return &theme_ui_grid_table(@_) if (defined(&theme_ui_grid_table)); 2283my ($elements, $cols, $width, $tds, $tabletags, $title) = @_; 2284return "" if (!@$elements); 2285my $rv = "<table class='ui_grid_table'". 2286 ($width ? " width='$width%'" : ""). 2287 ($tabletags ? " ".$tabletags : ""). 2288 ">\n"; 2289my $i; 2290for($i=0; $i<@$elements; $i++) { 2291 $rv .= "<tr class='ui_grid_row'>" if ($i%$cols == 0); 2292 $rv .= "<td ".$tds->[$i%$cols]." valign='top' class='ui_grid_cell'>". 2293 $elements->[$i]."</td>\n"; 2294 $rv .= "</tr>" if ($i%$cols == $cols-1); 2295 } 2296if ($i%$cols) { 2297 while($i%$cols) { 2298 $rv .= "<td ".$tds->[$i%$cols]." class='ui_grid_cell'>". 2299 "<br></td>\n"; 2300 $i++; 2301 } 2302 $rv .= "</tr>\n"; 2303 } 2304$rv .= "</table>\n"; 2305if (defined($title)) { 2306 $rv = "<table class='ui_table border' ". 2307 ($width ? " width=$width%" : "").">\n". 2308 ($title ? "<tr".($tb ? " ".$tb : "")."><td><b>$title</b></td></tr>\n" : ""). 2309 "<tr".($cb ? " ".$cb : "")."><td>$rv</td></tr>\n". 2310 "</table>"; 2311 } 2312return $rv; 2313} 2314 2315=head2 ui_radio_table(name, selected, &rows, [no-bold]) 2316 2317Returns HTML for a table of radio buttons, each of which has a label and 2318some associated inputs to the right. The parameters are : 2319 2320=item name - Unique name for this table, which is also the radio buttons' name. 2321 2322=item selected - Value for the initially selected radio button. 2323 2324=item rows - Array ref of array refs, one per button. The elements of each are the value for this option, a label, and option additional HTML to appear next to it. 2325 2326=item no-bold - When set to 1, labels in the table will not be bolded 2327 2328=cut 2329sub ui_radio_table 2330{ 2331return &theme_ui_radio_table(@_) if (defined(&theme_ui_radio_table)); 2332my ($name, $sel, $rows, $nobold) = @_; 2333return "" if (!@$rows); 2334my $rv = "<table class='ui_radio_table'>\n"; 2335foreach my $r (@$rows) { 2336 $rv .= "<tr>\n"; 2337 $rv .= "<td valign='top'".(defined($r->[2]) ? "" : " colspan='2'").">". 2338 ($nobold ? "" : "<b>"). 2339 &ui_oneradio($name, $r->[0], $r->[1], $r->[0] eq $sel, $r->[3]). 2340 ($nobold ? "" : "</b>"). 2341 "</td>\n"; 2342 if (defined($r->[2])) { 2343 $rv .= "<td valign='top'>".$r->[2]."</td>\n"; 2344 } 2345 $rv .= "</tr>\n"; 2346 } 2347$rv .= "</table>\n"; 2348return $rv; 2349} 2350 2351=head2 ui_up_down_arrows(uplink, downlink, up-show, down-show) 2352 2353Returns HTML for moving some objects in a table up or down. The parameters are : 2354 2355=item uplink - URL for the up-arrow link. 2356 2357=item downlink - URL for the down-arrow link. 2358 2359=item up-show - Set to 1 if the up-arrow should be shown, 0 if not. 2360 2361=item down-show - Set to 1 if the down-arrow should be shown, 0 if not. 2362 2363=item up-icon - Optional path to icon for up link 2364 2365=item down-icon - Optional path to icon for down link 2366 2367=cut 2368sub ui_up_down_arrows 2369{ 2370return &theme_ui_up_down_arrows(@_) if (defined(&theme_ui_up_down_arrows)); 2371my ($uplink, $downlink, $upshow, $downshow, $upicon, $downicon) = @_; 2372my $mover; 2373my $imgdir = "$gconfig{'webprefix'}/images"; 2374$upicon ||= "$imgdir/moveup.gif"; 2375$downicon ||= "$imgdir/movedown.gif"; 2376if ($downshow) { 2377 $mover .= "<a class='ui_up_down_arrows_down' href='$downlink'>". 2378 "<img class='ui_up_down_arrows_down' src='$downicon' border='0'></a>"; 2379 } 2380else { 2381 $mover .= "<img class='ui_up_down_arrows_gap' src='$imgdir/movegap.gif'>"; 2382 } 2383if ($upshow) { 2384 $mover .= "<a class='ui_up_down_arrows_up' href='$uplink'>". 2385 "<img class='ui_up_down_arrows_up' src='$upicon' border='0'></a>"; 2386 } 2387else { 2388 $mover .= "<img class='ui_up_down_arrows_gap' src='$imgdir/movegap.gif'>"; 2389 } 2390return $mover; 2391} 2392 2393=head2 ui_hr 2394 2395Returns a horizontal row tag, typically just an <hr> 2396 2397=item tags - Additional HTML attributes for the <hr> tag. 2398 2399=cut 2400sub ui_hr 2401{ 2402return &theme_ui_hr(@_) if (defined(&theme_ui_hr)); 2403my ($tags) = @_; 2404return "<hr class='ui_hr'".($tags ? " ".$tags : "").">\n"; 2405} 2406 2407=head2 ui_nav_link(direction, url, disabled) 2408 2409Returns an arrow icon linking to the provided url. 2410 2411=cut 2412sub ui_nav_link 2413{ 2414return &theme_ui_nav_link(@_) if (defined(&theme_ui_nav_link)); 2415my ($direction, $url, $disabled) = @_; 2416my $alt = $direction eq "left" ? '<-' : '->'; 2417if ($disabled) { 2418 return "<img class='ui_nav_link' alt=\"$alt\" align=\"middle\"" 2419 . "src=\"$gconfig{'webprefix'}/images/$direction-grey.gif\">\n"; 2420 } 2421else { 2422 return "<a class='ui_nav_link' href=\"$url\"><img class='ui_nav_link' alt=\"$alt\" align=\"middle\"" 2423 . "src=\"$gconfig{'webprefix'}/images/$direction.gif\"></a>\n"; 2424 } 2425} 2426 2427=head2 ui_confirmation_form(cgi, message, &hiddens, [&buttons], [otherinputs], [extra-warning]) 2428 2429Returns HTML for a form asking for confirmation before performing some 2430action, such as deleting a user. The parameters are : 2431 2432=item cgi - Script to which the confirmation form submits, like delete.cgi. 2433 2434=item message - Warning message for the user to see. 2435 2436=item hiddens - Array ref of two-element array refs, containing hidden form field names and values. 2437 2438=item buttons - Array ref of two-element array refs, containing form button names and labels. 2439 2440=item otheirinputs - HTML for extra inputs to include in their form. 2441 2442=item extra-warning - An additional separate warning message to show. 2443 2444=cut 2445sub ui_confirmation_form 2446{ 2447my ($cgi, $message, $hiddens, $buttons, $others, $warning) = @_; 2448my $rv; 2449$rv .= "<center class='ui_confirmation'>\n"; 2450$rv .= &ui_form_start($cgi, "post"); 2451foreach my $h (@$hiddens) { 2452 $rv .= &ui_hidden(@$h); 2453 } 2454$rv .= "<b>$message</b><p>\n"; 2455if ($warning) { 2456 $rv .= "<b><font color='#ff0000'>$warning</font></b><p>\n"; 2457 } 2458if ($others) { 2459 $rv .= $others."<p>\n"; 2460 } 2461$rv .= &ui_form_end($buttons); 2462$rv .= "</center>\n"; 2463return $rv; 2464} 2465 2466=head2 ui_text_color(text, type) 2467 2468Returns HTML for a text string, with its color determined by $type. 2469 2470=item text - contains any text string 2471 2472=item type - returned text color 2473 2474=cut 2475 2476sub ui_text_color 2477{ 2478my ($text, $type) = @_; 2479my ($color); 2480 2481if (defined (&theme_ui_text_color)) { 2482 return &theme_ui_text_color(@_); 2483 } 2484if ($type eq "success") { $color = "#3c763d"; } 2485elsif ($type eq "info") { $color = "#31708f"; } 2486elsif ($type eq "warn") { $color = "#8a6d3b"; } 2487elsif ($type eq "danger") { $color = "#a94442"; } 2488return "<span class=\"ui_text_color text_type_$type\" style=\"color: $color\">$text</span>\n"; 2489} 2490 2491=head2 ui_alert_box(msg, type) 2492 2493Returns HTML for an alert box, with background color determined by $type. 2494 2495$msg contains any text or HTML to be contained within the alert box, and 2496can include forms. 2497 2498Type of alert: 2499 2500=item success - green 2501 2502=item info - blue 2503 2504=item warn - yellow 2505 2506=item danger - red 2507 2508=cut 2509 2510sub ui_alert_box 2511{ 2512my ($msg, $type) = @_; 2513my ($rv, $color); 2514 2515if (defined (&theme_ui_alert_box)) { 2516 return &theme_ui_alert_box(@_); 2517 } 2518 2519if ($type eq "success") { $color = "DFF0D8"; } 2520elsif ($type eq "info") { $color = "D9EDF7"; } 2521elsif ($type eq "warn") { $color = "FCF8E3"; } 2522elsif ($type eq "danger") { $color = "F2DEDE"; } 2523 2524$rv .= "<table class='ui_alert_box' width='100%'><tr bgcolor='#$color'><td align='center'><p>\n"; 2525$rv .= "$msg\n"; 2526$rv .= "<p></td></tr></table><p>\n"; 2527 2528return $rv; 2529} 2530 2531####################### javascript functions 2532 2533=head2 js_disable_inputs(&disable-inputs, &enable-inputs, [tag]) 2534 2535Returns Javascript to disable some form elements and enable others. Mainly 2536for internal use. 2537 2538=cut 2539sub js_disable_inputs 2540{ 2541my $rv; 2542my $f; 2543foreach $f (@{$_[0]}) { 2544 $rv .= "e = form.elements[\"$f\"]; e.disabled = true; "; 2545 $rv .= "for(i=0; i<e.length; i++) { e[i].disabled = true; } "; 2546 } 2547foreach $f (@{$_[1]}) { 2548 $rv .= "e = form.elements[\"$f\"]; e.disabled = false; "; 2549 $rv .= "for(i=0; i<e.length; i++) { e[i].disabled = false; } "; 2550 } 2551foreach $f (@{$_[1]}) { 2552 if ($f =~ /^(.*)_def$/ && &indexof($1, @{$_[1]}) >= 0) { 2553 # When enabling both a _def field and its associated text field, 2554 # disable the text if the _def is set to 1 2555 my $tf = $1; 2556 $rv .= "e = form.elements[\"$f\"]; for(i=0; i<e.length; i++) { if (e[i].checked && e[i].value == \"1\") { form.elements[\"$tf\"].disabled = true } } "; 2557 } 2558 } 2559return $_[2] ? "$_[2]='$rv'" : $rv; 2560} 2561 2562=head2 ui_page_flipper(message, [inputs, cgi], left-link, right-link, [far-left-link], [far-right-link], [below]) 2563 2564Returns HTML for moving left and right in some large list, such as an inbox 2565or database table. If only 5 parameters are given, no far links are included. 2566If any link is undef, that array will be greyed out. The parameters are : 2567 2568=item message - Text or display between arrows. 2569 2570=item inputs - Additional HTML inputs to show after message. 2571 2572=item cgi - Optional CGI for form wrapping arrows to submit to. 2573 2574=item left-link - Link for left-facing arrow. 2575 2576=item right-link - Link for right-facing arrow. 2577 2578=item far-left-link - Link for far left-facing arrow, optional. 2579 2580=item far-right-link - Link for far right-facing arrow, optional. 2581 2582=item below - HTML to display below the arrows. 2583 2584=cut 2585sub ui_page_flipper 2586{ 2587return &theme_ui_page_flipper(@_) if (defined(&theme_ui_page_flipper)); 2588my ($msg, $inputs, $cgi, $left, $right, $farleft, $farright, $below) = @_; 2589my $rv = "<center class='ui_page_flipper'>"; 2590$rv .= &ui_form_start($cgi) if ($cgi); 2591 2592# Far left link, if needed 2593if (@_ > 5) { 2594 if ($farleft) { 2595 $rv .= "<a href='$farleft'>". 2596 "<img src='$gconfig{'webprefix'}/images/first.gif' ". 2597 "border='0' align='middle'></a>\n"; 2598 } 2599 else { 2600 $rv .= "<img src='$gconfig{'webprefix'}/images/first-grey.gif' ". 2601 "border='0' align='middle'></a>\n"; 2602 } 2603 } 2604 2605# Left link 2606if ($left) { 2607 $rv .= "<a href='$left'>". 2608 "<img src=$gconfig{'webprefix'}/images/left.gif ". 2609 "border='0' align='middle'></a>\n"; 2610 } 2611else { 2612 $rv .= "<img src=$gconfig{'webprefix'}/images/left-grey.gif ". 2613 "border='0' align='middle'></a>\n"; 2614 } 2615 2616# Message and inputs 2617$rv .= $msg; 2618$rv .= " ".$inputs if ($inputs); 2619 2620# Right link 2621if ($right) { 2622 $rv .= "<a href='$right'>". 2623 "<img src='$gconfig{'webprefix'}/images/right.gif' ". 2624 "border='0' align='middle'></a>\n"; 2625 } 2626else { 2627 $rv .= "<img src='$gconfig{'webprefix'}/images/right-grey.gif' ". 2628 "border='0' align='middle'></a>\n"; 2629 } 2630 2631# Far right link, if needed 2632if (@_ > 5) { 2633 if ($farright) { 2634 $rv .= "<a href='$farright'>". 2635 "<img src='$gconfig{'webprefix'}/images/last.gif' ". 2636 "border='0' align='middle'></a>\n"; 2637 } 2638 else { 2639 $rv .= "<img src='$gconfig{'webprefix'}/images/last-grey.gif' ". 2640 "border='0' align='middle'></a>\n"; 2641 } 2642 } 2643 2644$rv .= "<br>".$below if ($below); 2645$rv .= &ui_form_end() if ($cgi); 2646$rv .= "</center>\n"; 2647return $rv; 2648} 2649 2650=head2 js_checkbox_disable(name, &checked-disable, &checked-enable, [tag]) 2651 2652For internal use only. 2653 2654=cut 2655sub js_checkbox_disable 2656{ 2657my $rv; 2658my $f; 2659foreach $f (@{$_[1]}) { 2660 $rv .= "form.elements[\"$f\"].disabled = $_[0].checked; "; 2661 } 2662foreach $f (@{$_[2]}) { 2663 $rv .= "form.elements[\"$f\"].disabled = !$_[0].checked; "; 2664 } 2665return $_[3] ? "$_[3]='$rv'" : $rv; 2666} 2667 2668=head2 js_redirect(url, [window-object]) 2669 2670Returns HTML to trigger a redirect to some URL. 2671 2672=cut 2673sub js_redirect 2674{ 2675my ($url, $window) = @_; 2676if (defined(&theme_js_redirect)) { 2677 return &theme_js_redirect(@_); 2678 } 2679$window ||= "window"; 2680if ($url =~ /^\//) { 2681 # If the URL is like /foo , add webprefix 2682 $url = $gconfig{'webprefix'}.$url; 2683 } 2684return "<script type='text/javascript'>${window}.location = '"."e_escape($url)."';</script>\n"; 2685} 2686 2687=head2 ui_webmin_link(module, page) 2688 2689Returns the URL for a link to this Webmin instance that can be used in an email 2690 2691=cut 2692sub ui_webmin_link 2693{ 2694my ($mod, $page) = @_; 2695if (defined(&theme_ui_webmin_link)) { 2696 return &theme_ui_webmin_link(@_); 2697 } 2698my %miniserv; 2699&get_miniserv_config(\%miniserv); 2700my $proto = $miniserv{'ssl'} ? 'https' : 'http'; 2701my $port = $miniserv{'port'}; 2702my $host = $ENV{'HTTP_HOST'} || &get_display_hostname(); 2703if ($host =~ /^([a-zA-Z0-9\-\_\.]+):(\d+)$/) { 2704 $host = $1; 2705 $port = $2; 2706 } 2707my $rv = $proto."://$host:$port"; 2708if ($mod) { 2709 $rv .= "/$mod"; 2710 } 2711if ($page) { 2712 $rv .= "/$page"; 2713 } 2714return $rv; 2715} 2716 2717=head2 ui_line_break_double() 2718 2719Create double line break, with accessible second break 2720 2721=cut 2722sub ui_line_break_double 2723{ 2724if (defined(&theme_ui_line_break_double)) { 2725 return &theme_ui_line_break_double(@_); 2726 } 2727return "<br><br data-x-br>\n"; 2728} 2729 2730=head2 ui_details(Config, Opened) 2731 2732Creates a disclosure widget in which information is visible only when 2733the widget is toggled into an "open" state. 2734 2735=cut 2736sub ui_details 2737{ 2738my ($c, $o) = @_; 2739if (defined(&theme_ui_details)) { 2740 return &theme_ui_details(@_); 2741 } 2742 2743my $rv; 2744if (!$c->{'html'}) { 2745 $c->{'title'} = &html_escape($c->{'title'}); 2746 $c->{'content'} = &html_escape($c->{'content'}); 2747 } 2748$c->{'class'} = " class=\"@{["e_escape($c->{'class'})]}\"" if($c->{'class'}); 2749$o = ' open' if ($o); 2750$rv = "<details$c->{'class'}$o>"; 2751$rv .= "<summary>$c->{'title'}</summary>"; 2752$rv .= "<span>$c->{'content'}</span>"; 2753$rv .= "</details>"; 2754return $rv; 2755} 2756 2757=head2 ui_read_file_contents_limit(\%data) 2758 2759Reads file content with options and 2760returns head and/or tail separated with 2761chomped message 2762 2763=cut 2764sub ui_read_file_contents_limit 2765{ 2766if (defined(&theme_ui_read_file_contents_limit)) { 2767 return &theme_ui_read_file_contents_limit(@_); 2768 } 2769my ($opts) = @_; 2770my $binary = -s $opts->{'file'} >= 128 && -B $opts->{'file'}; 2771my $data = &read_file_contents_limit($opts->{'file'}, $opts->{'limit'}, $opts); 2772my $error = $data->{'error'}; 2773if ($error) { 2774 return $error; 2775 } 2776my $nonulls = sub { 2777 $_[0] =~ s/[^[:print:]\n\r\t]/\ /g; 2778 return $_[0]; 2779 }; 2780my $head = $data->{'head'}; 2781my $tail = $data->{'tail'}; 2782my $chomped = $data->{'chomped'}; 2783my $fsize = $data->{'size'}; 2784my $flimit = $data->{'limit'}; 2785my $msg_type = !$head && $tail ? '_tail' : 2786 $head && !$tail ? '_head' : undef; 2787my $nlines = $nslines = $nelines = "\n" x 10; 2788$nslines = undef if (!$head); 2789$nelines = undef if (!$tail); 2790my $chomped_msg; 2791$chomped_msg = 2792"${nslines}[--- @{[&text(\"file_truncated_message$msg_type\", 2793 &nice_size($flimit), 2794 &nice_size($chomped), 2795 &nice_size($fsize))]} ---]$nelines" 2796 if ($chomped); 2797 2798# Trim nulls 2799$head = &$nonulls($head) 2800 if ($binary && $head); 2801$tail = &$nonulls($tail) 2802 if ($binary && $tail); 2803 2804# Return data 2805if ($head && $tail) { 2806 return $head . $chomped_msg . $tail; 2807 } 2808if ($tail) { 2809 return $chomped_msg . $tail; 2810 } 2811if ($head) { 2812 return $head . $chomped_msg; 2813 } 2814} 2815 28161; 2817 2818