# Virtualmin Framed Theme # Icons copyright David Vignoni, all other theme elements copyright 2005-2007 # Virtualmin, Inc. # Un-comment these when left.cgi is the same as in virtual-server-theme #$main::cloudmin_no_create_links = 1; #$main::cloudmin_no_edit_buttons = 1; #$main::cloudmin_no_global_links = 1; #$main::mailbox_no_addressbook_button = 1; #$main::mailbox_no_folder_button = 1; #$main::basic_virtualmin_menu = 1; #$main::nocreate_virtualmin_menu = 1; #$main::nosingledomain_virtualmin_mode = 1; # Global state for wrapper # if 0, wrapper isn't on, add one and open it, if 1 close it, if 2+, subtract # but don't close $main::WRAPPER_OPEN = 0; $main::COLUMNS_main::WRAPPER_OPEN = 0; # theme_ui_post_header([subtext]) # Returns HTML to appear directly after a standard header() call sub theme_ui_post_header { my ($text) = @_; my $rv; $rv .= "
$text
\n" if (defined($text)); $rv .= "

" if (!defined($text)); return $rv; } # theme_ui_pre_footer() # Returns HTML to appear directly before a standard footer() call sub theme_ui_pre_footer { my $rv; $rv .= "

\n"; return $rv; } # ui_print_footer(args...) # Print HTML for a footer with the pre-footer line. Args are the same as those # passed to footer() sub theme_ui_print_footer { local @args = @_; print &ui_pre_footer(); &footer(@args); } sub theme_icons_table { my ($i, $need_tr); my $cols = $_[3] ? $_[3] : 4; my $per = int(100.0 / $cols); print "
\n"; print "\n"; for($i=0; $i<@{$_[0]}; $i++) { if ($i%$cols == 0) { print "\n"; } print "\n"; if ($i%$cols == $cols-1) { print "\n"; } } while($i++%$cols) { print "\n"; $need_tr++; } print "\n" if ($need_tr); print "
\n"; &generate_icon($_[2]->[$i], $_[1]->[$i], $_[0]->[$i], $_[4], $_[5], $_[6], $_[7]->[$i], $_[8]->[$i]); print "
\n"; print "
\n"; } sub theme_generate_icon { my $w = !defined($_[4]) ? "width=48" : $_[4] ? "width=$_[4]" : ""; my $h = !defined($_[5]) ? "height=48" : $_[5] ? "height=$_[5]" : ""; if ($tconfig{'noicons'}) { if ($_[2]) { print "$_[6]$_[1]$_[7]\n"; } else { print "$_[6]$_[1]$_[7]\n"; } } elsif ($_[2]) { print "
\n", "\"\"
\n"; print "$_[6]$_[1]$_[7]\n"; } else { print "
\n", "\"\"", "
\n$_[6]$_[1]$_[7]\n"; } } # theme_post_save_domain(&domain, action) # Called by Virtualmin after a domain is updated, to refresh the left menu sub theme_post_save_domain { local ($d, $action) = @_; # Refresh left side, in case options have changed print "\n"; } # theme_post_save_domains([domain, action]+) # Called after multiple domains are updated, to refresh the left menu sub theme_post_save_domains { print "\n"; } # theme_post_save_server(&server, action) # Called by Cloudmin after a server is updated, to refresh the left menu sub theme_post_save_server { local ($s, $action) = @_; if ($action eq 'create' || $action eq 'delete' || !$done_theme_post_save_server++) { print "\n"; } } # theme_select_server(&server) # Called by Cloudmin when a page for a server is displayed, to select it on the # left menu. sub theme_select_server { local ($server) = @_; print < if (window.parent && window.parent.frames[0]) { var leftdoc = window.parent.frames[0].document; var leftform = leftdoc.forms[0]; if (leftform) { var serversel = leftform['sid']; if (serversel && serversel.value != '$server->{'id'}' || !serversel) { //if (serversel) { // // Need to change value of selector // serversel.value = '$server->{'id'}'; // } window.parent.frames[0].location = '$gconfig{'webprefix'}/left.cgi?mode=vm2&sid=$server->{'id'}'; } } } EOF } # theme_select_domain(&domain) # Called by Virtualmin when a page for a server is displayed, to select it on # the left menu. sub theme_select_domain { local ($d) = @_; print < if (window.parent && window.parent.frames[0]) { var leftdoc = window.parent.frames[0].document; var leftform = leftdoc.forms[0]; if (leftform) { var domsel = leftform['dom']; if (domsel && domsel.value != '$d->{'id'}') { // Need to change value // domsel.value = '$d->{'id'}'; window.parent.frames[0].location = '$gconfig{'webprefix'}/left.cgi?mode=virtualmin&dom=$d->{'id'}'; } } } EOF } # theme_post_save_folder(&folder, action) # Called after some folder is changed, to refresh the left frame. The action # may be 'create', 'delete', 'modify' or 'read' sub theme_post_save_folder { local ($folder, $action) = @_; my $ref; if ($action eq 'create' || $action eq 'delete' || $action eq 'modify') { # Always refresh $ref = 1; } else { # Only refesh if showing unread count if (defined(&mailbox::should_show_unread) && &mailbox::should_show_unread($folder)) { $ref = 1; } } if ($ref) { print "\n"; } } sub theme_post_change_modules { print < var url = '' + top.left.location; if ( url.match(/mode=.*/) ) { if ( url.indexOf('mode=webmin') > 0) { top.left.location = url; } } else { top.left.location = url; } EOF } sub theme_prebody { if ($script_name =~ /session_login.cgi/) { # Generate CSS link print "\n"; print "\n"; print "\n"; } } sub theme_prehead { print "\n"; print "\n"; print "\n"; print "\n"; print "\n"; if ($ENV{'HTTP_USER_AGENT'} =~ /Chrome/) { print "\n"; } } sub theme_popup_prehead { return &theme_prehead(); } # ui_table_start(heading, [tabletags], [cols], [&default-tds], [right-heading]) # A table with a heading and table inside sub theme_ui_table_start { my ($heading, $tabletags, $cols, $tds, $rightheading) = @_; if (! $tabletags =~ /width/) { $tabletages .= " width=100%"; } if (defined($main::ui_table_cols)) { # Push on stack, for nested call push(@main::ui_table_cols_stack, $main::ui_table_cols); push(@main::ui_table_pos_stack, $main::ui_table_pos); push(@main::ui_table_default_tds_stack, $main::ui_table_default_tds); } my $rv; my $colspan = 1; if (!$main::WRAPPER_OPEN) { $rv .= "\n"; $rv .= "\n"; $rv .= "
\n"; } $main::WRAPPER_OPEN++; $rv .= "\n"; if (defined($heading) || defined($rightheading)) { $rv .= ""; if (defined($heading)) { $rv .= "" } if (defined($rightheading)) { $rv .= ""; $colspan++; } $rv .= "\n"; } $rv .= "
$heading$rightheading
". "\n"; $main::ui_table_cols = $cols || 4; $main::ui_table_pos = 0; $main::ui_table_default_tds = $tds; return $rv; } # ui_table_row(label, value, [cols], [&td-tags]) # Returns HTML for a row in a table started by ui_table_start, with a 1-column # label and 1+ column value. sub theme_ui_table_row { my ($label, $value, $cols, $tds) = @_; $cols ||= 1; $tds ||= $main::ui_table_default_tds; my $rv; if ($main::ui_table_pos+$cols+1 > $main::ui_table_cols && $main::ui_table_pos != 0) { # If the requested number of cols won't fit in the number # remaining, start a new row $rv .= "\n"; $main::ui_table_pos = 0; } if (defined($label) && ($value =~ /id="([^"]+)"/ || $value =~ /id='([^']+)'/ || $value =~ /id=([^>\s]+)/)) { # Value contains an input with an ID my $id = $1; $label = ""; } $rv .= "\n" if ($main::ui_table_pos%$main::ui_table_cols == 0); $rv .= "\n" if (defined($label)); $rv .= "\n"; $main::ui_table_pos += $cols+(defined($label) ? 1 : 0); if ($main::ui_table_pos%$main::ui_table_cols == 0) { $rv .= "\n"; $main::ui_table_pos = 0; } return $rv; } # ui_table_end() # The end of a table started by ui_table_start sub theme_ui_table_end { my $rv; if ($main::ui_table_cols == 4 && $main::ui_table_pos) { # Add an empty block to balance the table $rv .= &ui_table_row(" ", " "); } if (@main::ui_table_cols_stack) { $main::ui_table_cols = pop(@main::ui_table_cols_stack); $main::ui_table_pos = pop(@main::ui_table_pos_stack); $main::ui_table_default_tds = pop(@main::ui_table_default_tds_stack); } else { $main::ui_table_cols = undef; $main::ui_table_pos = undef; $main::ui_table_default_tds = undef; } $rv .= "
[0]>$label[1]>$value
\n"; if ($main::WRAPPER_OPEN==1) { #$rv .= "\n"; $rv .= "
\n"; } $main::WRAPPER_OPEN--; return $rv; } # theme_ui_tabs_start(&tabs, name, selected, show-border) # Render a row of tabs from which one can be selected. Each tab is an array # ref containing a name, title and link. sub theme_ui_tabs_start { my ($tabs, $name, $sel, $border) = @_; my $rv; if (!$main::ui_hidden_start_donejs++) { $rv .= &ui_hidden_javascript(); } # Build list of tab titles and names my $tabnames = "[".join(",", map { "\""."e_escape($_->[0])."\"" } @$tabs)."]"; my $tabtitles = "[".join(",", map { "\""."e_escape($_->[1])."\"" } @$tabs)."]"; $rv .= "\n"; # Output the tabs my $imgdir = "$gconfig{'webprefix'}/images"; $rv .= &ui_hidden($name, $sel)."\n"; $rv .= "\n"; $rv .= "\n"; $rv .= "\n"; $rv .= "\n"; foreach my $t (@$tabs) { if ($t ne $tabs[0]) { # Spacer $rv .= "\n"; } my $tabid = "tab_".$t->[0]; $rv .= "\n"; } $rv .= "\n"; $rv .= "
"; if ($ENV{'HTTP_USER_AGENT'} !~ /msie/i) { # For some reason, the 1-pixel space above the tabs appears huge on IE! $rv .= ""; } $rv .= "
". ""; $rv .= ""; if ($t->[0] eq $sel) { # Selected tab $rv .= ""; $rv .= ""; $rv .= ""; } else { # Other tab (which has a link) $rv .= ""; $rv .= ""; $rv .= ""; $rv .= "\n"; } $rv .= "
". "\"\"". " $t->[1] ". "\"\"". "\"\"". " ". "$t->[1] ". "
"; $rv .= "
\n"; if ($border) { # All tabs are within a grey box $rv .= "\n"; $rv .= "\n"; $rv .= "\n"; $rv .= "\n"; $rv .= "". "". "\n"; } $rv .= "\n"; my $i; for($i=0; $i<@$heads; $i++) { $rv .= "\n"; } $rv .= "\n"; $theme_ui_columns_count++; return $rv; } # theme_ui_columns_row(&columns, &tdtags) # Returns HTML for a row in a multi-column table sub theme_ui_columns_row { $theme_ui_columns_row_toggle = $theme_ui_columns_row_toggle ? '0' : '1'; local ($cols, $tdtags) = @_; my $rv; $rv .= "\n"; my $i; for($i=0; $i<@$cols; $i++) { $rv .= "\n"; } $rv .= "\n"; return $rv; } # theme_ui_columns_end() # Returns HTML to end a table started by ui_columns_start sub theme_ui_columns_end { my $rv; $rv = "
"; } $main::ui_tabs_selected = $sel; return $rv; } # theme_ui_columns_start(&headings, [width-percent], [noborder], [&tdtags], [title]) # Returns HTML for a multi-column table, with the given headings sub theme_ui_columns_start { my ($heads, $width, $noborder, $tdtags, $title) = @_; my ($href) = grep { $_ =~ /\n"; $rv .= "
\n"; } if (!$noborder) { $main::COLUMNS_main::WRAPPER_OPEN++; } my @classes; push(@classes, "ui_table") if (!$noborder); push(@classes, "sortable") if (!$href); push(@classes, "ui_columns"); $rv .= "\n"; if ($title) { $rv .= "
$title
[$i].">". ($heads->[$i] eq "" ? "
" : $heads->[$i])."
[$i].">". ($cols->[$i] !~ /\S/ ? "
" : $cols->[$i])."
\n"; if ($main::COLUMNS_main::WRAPPER_OPEN == 1) { # Last wrapper $rv .= " \n"; } $main::COLUMNS_main::WRAPPER_OPEN--; return $rv; } # theme_ui_grid_table(&elements, columns, [width-percent], [tds], [tabletags], # [title]) # Given a list of HTML elements, formats them into a table with the given # number of columns. However, themes are free to override this to use fewer # columns where space is limited. sub theme_ui_grid_table { my ($elements, $cols, $width, $tds, $tabletags, $title) = @_; return "" if (!@$elements); my $rv = "\n"; $rv .= "
\n"; $rv .= "\n"; if ($title) { $rv .= " ". "\n"; } $rv .= "\n"; my $i; for($i=0; $i<@$elements; $i++) { $rv .= "" if ($i%$cols == 0); $rv .= "\n"; $rv .= "" if ($i%$cols == $cols-1); } if ($i%$cols) { while($i%$cols) { $rv .= "\n"; $i++; } $rv .= "\n"; } $rv .= "
$title
[$i%$cols]." valign=top class='ui_grid_cell'>". $elements->[$i]."
[$i%$cols]." class='ui_grid_cell'>
\n"; $rv .= "
\n"; # wrapper return $rv; } # theme_ui_hidden_table_start(heading, [tabletags], [cols], name, status, # [&default-tds], [rightheading]) # A table with a heading and table inside, and which is collapsible sub theme_ui_hidden_table_start { my ($heading, $tabletags, $cols, $name, $status, $tds, $rightheading) = @_; my $rv; if (!$main::ui_hidden_start_donejs++) { $rv .= &ui_hidden_javascript(); } my $divid = "hiddendiv_$name"; my $openerid = "hiddenopener_$name"; my $defimg = $status ? "open.gif" : "closed.gif"; my $defclass = $status ? 'opener_shown' : 'opener_hidden'; my $text = defined($tconfig{'cs_text'}) ? $tconfig{'cs_text'} : defined($gconfig{'cs_text'}) ? $gconfig{'cs_text'} : "000000"; if (!$main::WRAPPER_OPEN) { # If we're not already inside of a wrapper, wrap it $rv .= "\n"; $rv .= "
\n"; } $main::WRAPPER_OPEN++; my $colspan = 1; $rv .= "\n"; if (defined($heading) || defined($rightheading)) { $rv .= ""; if (defined($heading)) { $rv .= ""; } if (defined($rightheading)) { $rv .= ""; $colspan++; } $rv .= "\n"; } $rv .= "
$heading$rightheading
\n"; $main::ui_table_cols = $cols || 4; $main::ui_table_pos = 0; $main::ui_table_default_tds = $tds; return $rv; } # ui_hidden_table_end(name) # Returns HTML for the end of table with hiding, as started by # ui_hidden_table_start sub theme_ui_hidden_table_end { my ($name) = @_; local $rv = "
\n"; if ( $main::WRAPPER_OPEN == 1 ) { $main::WRAPPER_OPEN--; #$rv .= "\n"; $rv .= "
\n"; } elsif ($main::WRAPPER_OPEN) { $main::WRAPPER_OPEN--; } return $rv; } # theme_select_all_link(field, form, text) # Adds support for row highlighting to the normal select all sub theme_select_all_link { local ($field, $form, $text) = @_; $form = int($form); $text ||= $text{'ui_selall'}; return "$text"; } # theme_select_invert_link(field, form, text) # Adds support for row highlighting to the normal invert selection sub theme_select_invert_link { local ($field, $form, $text) = @_; $form = int($form); $text ||= $text{'ui_selinv'}; return "$text"; } sub theme_select_rows_link { local ($field, $form, $text, $rows) = @_; $form = int($form); my $js = "var sel = { ".join(",", map { "\""."e_escape($_)."\":1" } @$rows)." }; "; $js .= "for(var i=0; i$text"; } sub theme_ui_checked_columns_row { $theme_ui_columns_row_toggle = $theme_ui_columns_row_toggle ? '0' : '1'; local ($cols, $tdtags, $checkname, $checkvalue, $checked, $disabled, $tags) = @_; my $rv; my $cbid = "e_escape(quotemeta("${checkname}_${checkvalue}")); my $rid = "e_escape(quotemeta("row_${checkname}_${checkvalue}")); my $ridtr = "e_escape("row_${checkname}_${checkvalue}"); my $mycb = $cb; if ($checked) { $mycb =~ s/mainbody/mainsel/g; } $mycb =~ s/class='/class='row$theme_ui_columns_row_toggle ui_checked_columns /; $rv .= "\n"; $rv .= "[0].">". &ui_checkbox($checkname, $checkvalue, undef, $checked, $tags." "."onClick=\"document.getElementById('$rid').className = this.checked ? 'mainhighsel' : 'mainhigh';\"", $disabled). "\n"; my $i; for($i=0; $i<@$cols; $i++) { $rv .= "[$i+1].">"; if ($cols->[$i] !~ /"; } $rv .= ($cols->[$i] !~ /\S/ ? "
" : $cols->[$i]); if ($cols->[$i] !~ /\n"; $rv .= "[0]." class='ui_radio_radio'>". &ui_oneradio($checkname, $checkvalue, undef, $checked, "onClick=\"for(i=0; i\n"; my $i; for($i=0; $i<@$cols; $i++) { $rv .= "[$i+1].">"; if ($cols->[$i] !~ /"; } $rv .= ($cols->[$i] !~ /\S/ ? "
" : $cols->[$i]); if ($cols->[$i] !~ /'; if ($disabled) { return "\"$alt\"\n"; } else { return "\"$alt\"\n"; } } # theme_footer([page, name]+, [noendbody]) # Output a footer for returning to some page sub theme_footer { my $i; my $count = 0; my %module_info = get_module_info(get_module_name()); for($i=0; $i+1<@_; $i+=2) { local $url = $_[$i]; if ($url ne '/' || !$tconfig{'noindex'}) { if ($url eq '/') { $url = "/?cat=$module_info{'category'}"; } elsif ($url eq '' && get_module_name()) { $url = "/".get_module_name()."/". $module_info{'index_link'}; } elsif ($url =~ /^\?/ && get_module_name()) { $url = "/".get_module_name()."/$url"; } $url = "$gconfig{'webprefix'}$url" if ($url =~ /^\//); if ($count++ == 0) { print theme_ui_nav_link("left", $url); } else { print " |\n"; } print " ",&text('main_return', $_[$i+1]),"\n"; } } print "
\n"; if (!$_[$i]) { print "\n"; } } # theme_ui_hidden_javascript() # Returns EOF } =yui Functions for generating YUI CSS grids markup. =cut # ui_yui_grid_start(id, type) # Return a yui grid opening div. # Available types are: # g - 1/2,1/2 # gb - 1/3, 1/3, 1/3 # gc - 2/3, 1/3 # gd - 1/3, 2/3 # ge - 3/4, 1/4 # gf - 1/4, 3/4 sub theme_ui_yui_grid_start { my ($id, $type) = @_; return "
\n"; } sub theme_ui_yui_grid_end { my ($id) = @_; return "
\n"; } # ui_yui_grid_section_start(id, first?) # Return a yui grid markup section opening div. sub theme_ui_yui_grid_section_start { my ($id, $first) = @_; if ($first) { return "
\n"; } else { return "
\n"; } } sub theme_ui_yui_grid_section_end { my ($id) = @_; return "
\n"; } 1;