From 4b6ce0eebddfa813e746f5f51ef471bdbddb2b01 Mon Sep 17 00:00:00 2001 From: Jamie Cameron Date: Thu, 5 Mar 2009 21:42:37 +0000 Subject: [PATCH] Use new module --- Webmin/All.pm | 44 +++ Webmin/Button.pm | 61 ++++ Webmin/Checkbox.pm | 64 ++++ Webmin/Checkboxes.pm | 119 +++++++ Webmin/Columns.pm | 82 +++++ Webmin/ConfirmPage.pm | 47 +++ Webmin/Date.pm | 96 ++++++ Webmin/DynamicBar.pm | 119 +++++++ Webmin/DynamicHTML.pm | 76 +++++ Webmin/DynamicText.pm | 123 +++++++ Webmin/DynamicWait.pm | 129 ++++++++ Webmin/ErrorPage.pm | 20 ++ Webmin/File.pm | 70 ++++ Webmin/Form.pm | 405 +++++++++++++++++++++++ Webmin/Group.pm | 57 ++++ Webmin/Icon.pm | 64 ++++ Webmin/Input.pm | 134 ++++++++ Webmin/InputTable.pm | 137 ++++++++ Webmin/JavascriptButton.pm | 47 +++ Webmin/LinkTable.pm | 314 ++++++++++++++++++ Webmin/Menu.pm | 86 +++++ Webmin/Multiline.pm | 40 +++ Webmin/OptTextarea.pm | 120 +++++++ Webmin/OptTextbox.pm | 90 +++++ Webmin/Page.pm | 426 ++++++++++++++++++++++++ Webmin/Password.pm | 32 ++ Webmin/PlainText.pm | 97 ++++++ Webmin/Properties.pm | 132 ++++++++ Webmin/Radios.pm | 77 +++++ Webmin/ResultPage.pm | 20 ++ Webmin/Section.pm | 172 ++++++++++ Webmin/Select.pm | 135 ++++++++ Webmin/Submit.pm | 41 +++ Webmin/Table.pm | 660 +++++++++++++++++++++++++++++++++++++ Webmin/TableAction.pm | 92 ++++++ Webmin/Tabs.pm | 142 ++++++++ Webmin/Textarea.pm | 122 +++++++ Webmin/Textbox.pm | 80 +++++ Webmin/Time.pm | 168 ++++++++++ Webmin/TitleList.pm | 101 ++++++ Webmin/Upload.pm | 77 +++++ Webmin/User.pm | 57 ++++ 42 files changed, 5175 insertions(+) create mode 100644 Webmin/All.pm create mode 100644 Webmin/Button.pm create mode 100644 Webmin/Checkbox.pm create mode 100644 Webmin/Checkboxes.pm create mode 100644 Webmin/Columns.pm create mode 100644 Webmin/ConfirmPage.pm create mode 100644 Webmin/Date.pm create mode 100644 Webmin/DynamicBar.pm create mode 100644 Webmin/DynamicHTML.pm create mode 100644 Webmin/DynamicText.pm create mode 100644 Webmin/DynamicWait.pm create mode 100644 Webmin/ErrorPage.pm create mode 100644 Webmin/File.pm create mode 100644 Webmin/Form.pm create mode 100644 Webmin/Group.pm create mode 100644 Webmin/Icon.pm create mode 100644 Webmin/Input.pm create mode 100644 Webmin/InputTable.pm create mode 100644 Webmin/JavascriptButton.pm create mode 100644 Webmin/LinkTable.pm create mode 100644 Webmin/Menu.pm create mode 100644 Webmin/Multiline.pm create mode 100644 Webmin/OptTextarea.pm create mode 100644 Webmin/OptTextbox.pm create mode 100644 Webmin/Page.pm create mode 100644 Webmin/Password.pm create mode 100644 Webmin/PlainText.pm create mode 100644 Webmin/Properties.pm create mode 100644 Webmin/Radios.pm create mode 100644 Webmin/ResultPage.pm create mode 100644 Webmin/Section.pm create mode 100644 Webmin/Select.pm create mode 100644 Webmin/Submit.pm create mode 100644 Webmin/Table.pm create mode 100644 Webmin/TableAction.pm create mode 100644 Webmin/Tabs.pm create mode 100644 Webmin/Textarea.pm create mode 100644 Webmin/Textbox.pm create mode 100644 Webmin/Time.pm create mode 100644 Webmin/TitleList.pm create mode 100644 Webmin/Upload.pm create mode 100644 Webmin/User.pm diff --git a/Webmin/All.pm b/Webmin/All.pm new file mode 100644 index 000000000..529a542f0 --- /dev/null +++ b/Webmin/All.pm @@ -0,0 +1,44 @@ +use Webmin::Page; +use Webmin::ResultPage; +use Webmin::ErrorPage; +use Webmin::ConfirmPage; +use Webmin::Form; +use Webmin::Section; +use Webmin::Textbox; +use Webmin::OptTextbox; +use Webmin::OptTextarea; +use Webmin::Submit; +use Webmin::Password; +use Webmin::Checkbox; +use Webmin::Select; +use Webmin::Radios; +use Webmin::Checkboxes; +use Webmin::Table; +use Webmin::Menu; +use Webmin::LinkTable; +use Webmin::Tabs; +use Webmin::Textarea; +use Webmin::Upload; +use Webmin::DynamicText; +use Webmin::DynamicBar; +use Webmin::DynamicWait; +use Webmin::DynamicHTML; +use Webmin::Properties; +use Webmin::User; +use Webmin::Group; +use Webmin::File; +use Webmin::Button; +use Webmin::JavascriptButton; +use Webmin::PlainText; +use Webmin::Multiline; +use Webmin::Date; +use Webmin::Time; +use Webmin::TitleList; +use Webmin::Columns; +use Webmin::Icon; +use Webmin::TableAction; +use Webmin::InputTable; +use WebminCore; + +1; + diff --git a/Webmin/Button.pm b/Webmin/Button.pm new file mode 100644 index 000000000..2b2c739b8 --- /dev/null +++ b/Webmin/Button.pm @@ -0,0 +1,61 @@ +package Webmin::Button; +use Webmin::Input; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::Button(cgi, label, [name]) +Creates a button that when clicked will link to some other page +=cut +sub new +{ +if (defined(&Webmin::Theme::Button::new) && + caller() !~ /Webmin::Theme::Button/) { + return new Webmin::Theme::Button(@_[1..$#_]); + } +my ($self, $cgi, $value, $name) = @_; +$self = { }; +bless($self); +$self->set_cgi($cgi); +$self->set_value($value); +$self->set_name($name) if ($name); +return $self; +} + +=head2 html() +Returns HTML for this button +=cut +sub html +{ +my ($self) = @_; +my $rv = "
"; +foreach my $h (@{$self->{'hiddens'}}) { + $rv .= &ui_hidden($h->[0], $h->[1])."\n"; + } +$rv .= &ui_submit($self->get_value(), $self->get_name(), + $self->get_disabled())."
"; +return $rv; +} + +sub set_cgi +{ +my ($self, $cgi) = @_; +$self->{'cgi'} = $cgi; +} + +sub get_cgi +{ +my ($self) = @_; +return $self->{'cgi'}; +} + +=head2 add_hidden(name, value) +Adds some hidden input to this button, for passing to the CGI +=cut +sub add_hidden +{ +my ($self, $name, $value) = @_; +push(@{$self->{'hiddens'}}, [ $name, $value ]); +} + +1; + diff --git a/Webmin/Checkbox.pm b/Webmin/Checkbox.pm new file mode 100644 index 000000000..f1191e3ef --- /dev/null +++ b/Webmin/Checkbox.pm @@ -0,0 +1,64 @@ +package Webmin::Checkbox; +use Webmin::Input; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::Checkbox(name, return, label, checked, [disabled]) +Create a single checkbox field +=cut +sub new +{ +if (defined(&Webmin::Theme::Checkbox::new)) { + return new Webmin::Theme::Checkbox(@_[1..$#_]); + } +my ($self, $name, $return, $label, $checked, $disabled) = @_; +$self = { }; +bless($self); +$self->set_name($name); +$self->set_return($return); +$self->set_label($label); +$self->set_value($checked); +$self->set_disabled($disabled); +return $self; +} + +=head2 html() +Returns the HTML for this single checkbox +=cut +sub html +{ +my ($self) = @_; +my $dis = $self->{'form'}->get_changefunc($self); +return &ui_checkbox($self->get_name(), $self->get_return(), + $self->get_label(), $self->get_value(), + $dis ? "onClick='$dis'" : undef, + $self->get_disabled()). + &ui_hidden("ui_exists_".$self->get_name(), 1); +} + +sub set_return +{ +my ($self, $return) = @_; +$self->{'return'} = $return; +} + +sub set_label +{ +my ($self, $label) = @_; +$self->{'label'} = $label; +} + +sub get_return +{ +my ($self) = @_; +return $self->{'return'}; +} + +sub get_label +{ +my ($self) = @_; +return $self->{'label'}; +} + +1; + diff --git a/Webmin/Checkboxes.pm b/Webmin/Checkboxes.pm new file mode 100644 index 000000000..1cebd8236 --- /dev/null +++ b/Webmin/Checkboxes.pm @@ -0,0 +1,119 @@ +package Webmin::Checkboxes; +use Webmin::Input; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::Checkboxes(name, value|&values, &options, [disabled]) +Create a list of checkboxes, of which zero or more may be selected +=cut +sub new +{ +if (defined(&Webmin::Theme::Checkboxes::new)) { + return new Webmin::Theme::Checkboxes(@_[1..$#_]); + } +my ($self, $name, $value, $options, $disabled) = @_; +$self = { }; +bless($self); +$self->set_name($name); +$self->set_value($value); +$self->set_options($options); +$self->set_disabled($disabled); +return $self; +} + +=head2 add_option(name, [label]) +=cut +sub add_option +{ +my ($self, $name, $label) = @_; +push(@{$self->{'options'}}, [ $name, $label ]); +} + +=head2 html() +Returns the HTML for all the checkboxes, one after the other +=cut +sub html +{ +my ($self) = @_; +my $rv; +for(my $i=0; $i<@{$self->{'options'}}; $i++) { + $rv .= $self->one_html($i)."\n"; + } +return $rv; +} + +=head2 one_html(number) +Returns the HTML for a single one of the checkboxes +=cut +sub one_html +{ +my ($self, $num) = @_; +my $opt = $self->{'options'}->[$num]; +my $value = $self->get_value(); +my %sel = map { $_, 1 } (ref($value) ? @$value : ( $value )); +return &ui_checkbox($self->get_name(), $opt->[0], + defined($opt->[1]) ? $opt->[1] : $opt->[0], + $sel{$opt->[0]}, undef, $self->get_disabled()). + ($num == 0 ? &ui_hidden("ui_exists_".$self->get_name(), 1) : ""); +} + +=head2 get_value() +Returns a hash ref of all selected values +=cut +sub get_value +{ +my ($self) = @_; +my $in = $self->{'form'} ? $self->{'form'}->{'in'} : undef; +if ($in && (defined($in->{$self->{'name'}}) || + defined($in->{"ui_exists_".$self->{'name'}}))) { + return [ split(/\0/, $in->{$self->{'name'}}) ]; + } +elsif ($in && defined($in->{"ui_value_".$self->{'name'}})) { + return [ split(/\0/, $in->{"ui_value_".$self->{'name'}}) ]; + } +else { + return $self->{'value'}; + } +} + +sub set_options +{ +my ($self, $options) = @_; +$self->{'options'} = $options; +} + +sub get_options +{ +my ($self) = @_; +return $self->{'options'}; +} + +=head2 validate() +Returns a list of error messages for this field +=cut +sub validate +{ +my ($self) = @_; +my $value = $self->get_value(); +if ($self->{'mandatory'} && !@$value) { + return ( $self->{'mandmesg'} || $text{'ui_checkmandatory'} ); + } +return ( ); +} + +=head2 get_input_names() +Returns the actual names of all HTML elements that make up this input +=cut +sub get_input_names +{ +my ($self) = @_; +my @rv; +for(my $i=0; $i<@{$self->{'options'}}; $i++) { + push(@rv, $self->{'name'}."[".$i."]"); + } +return @rv; +} + +1; + + diff --git a/Webmin/Columns.pm b/Webmin/Columns.pm new file mode 100644 index 000000000..b2a26c44d --- /dev/null +++ b/Webmin/Columns.pm @@ -0,0 +1,82 @@ +package Webmin::Columns; +use WebminCore; + +=head2 new Webmin::Columns(cols) +Displays some page elements in a multi-column table +=cut +sub new +{ +my ($self, $cols) = @_; +if (defined(&Webmin::Theme::Columns::new)) { + return new Webmin::Theme::Columns(@_[1..$#_]); + } +$self = { 'columns' => 2 }; +bless($self); +$self->set_columns($cols) if (defined($cols)); +return $self; +} + +=head2 html() +Returns HTML for the objects, arranged in columns +=cut +sub html +{ +my ($self) = @_; +my $rv; +my $n = scalar(@{$self->{'contents'}}); +$rv .= "\n"; +my $h = int($n / $self->{'columns'})+1; +my $i = 0; +my $pc = int(100/$self->{'columns'}); +foreach my $c (@{$self->{'contents'}}) { + if ($i%$h == 0) { + $rv .= "\n"; + } + } +$rv .= "
"; + } + $rv .= $c->html()."

\n"; + $i++; + if ($i%$h == 0) { + $rv .= "

\n"; +return $rv; +} + +=head2 add(object) +Adds some Webmin:: object to this list +=cut +sub add +{ +my ($self, $object) = @_; +push(@{$self->{'contents'}}, $object); +if ($self->{'page'}) { + $object->set_page($self->{'page'}); + } +} + +sub set_columns +{ +my ($self, $columns) = @_; +$self->{'columns'} = $columns; +} + +sub get_columns +{ +my ($self) = @_; +return $self->{'columns'}; +} + +=head2 set_page(Webmin::Page) +Called when this menu is added to a page +=cut +sub set_page +{ +my ($self, $page) = @_; +$self->{'page'} = $page; +foreach my $c (@{$self->{'contents'}}) { + $c->set_page($page); + } +} + +1; + diff --git a/Webmin/ConfirmPage.pm b/Webmin/ConfirmPage.pm new file mode 100644 index 000000000..8e90af156 --- /dev/null +++ b/Webmin/ConfirmPage.pm @@ -0,0 +1,47 @@ +package Webmin::ConfirmPage; +use Webmin::Page; +use WebminCore; +@ISA = ( "Webmin::Page" ); + +=head2 new Webmin::ConfirmPage(subheading, title, message, cgi, &in, [ok-message], + [cancel-message], [help-name]) +Create a new page object that asks if the user is sure if he wants to +do something or not. +=cut +sub new +{ +if (defined(&Webmin::Theme::ConfirmPage::new)) { + return new Webmin::Theme::ConfirmPage(@_[1..$#_]); + } +my ($self, $subheading, $title, $message, $cgi, $in, $ok, $cancel, $help) = @_; +$self = new Webmin::Page($subheading, $title, $help); +$self->{'in'} = $in; +$self->add_message($message); +my $form = new Webmin::Form($cgi, "get"); +$form->set_input($in); +$self->add_form($form); +foreach my $i (keys %$in) { + foreach my $v (split(/\0/, $in->{$i})) { + $form->add_hidden($i, $v); + } + } +$form->add_button(new Webmin::Submit($ok || "OK", "ui_confirm")); +$form->add_button(new Webmin::Submit($cancel || $text{'cancel'}, "ui_cancel")); +bless($self); +return $self; +} + +sub get_confirm +{ +my ($self) = @_; +return $self->{'in'}->{'ui_confirm'} ? 1 : 0; +} + +sub get_cancel +{ +my ($self) = @_; +return $self->{'in'}->{'ui_cancel'} ? 1 : 0; +} + +1; + diff --git a/Webmin/Date.pm b/Webmin/Date.pm new file mode 100644 index 000000000..8c1c6af39 --- /dev/null +++ b/Webmin/Date.pm @@ -0,0 +1,96 @@ +package Webmin::Date; +use Webmin::Input; +use Time::Local; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::Date(name, time, [disabled]) +Create a new field for selecting a date +=cut +sub new +{ +if (defined(&Webmin::Theme::Date::new)) { + return new Webmin::Theme::Date(@_[1..$#_]); + } +my ($self, $name, $value, $disabled) = @_; +bless($self = { }); +$self->set_name($name); +$self->set_value($value); +$self->set_disabled($disabled) if (defined($disabled)); +return $self; +} + +=head2 html() +Returns the HTML for the date chooser +=cut +sub html +{ +my ($self) = @_; +my $rv; +my @tm = localtime($self->get_value()); +my $name = $self->get_name(); +$rv .= &ui_date_input($tm[3], $tm[4]+1, $tm[5]+1900, + "day_".$name, "month_".$name, "year_".$name, + $self->get_disabled())." ". + &date_chooser_button("day_".$name, "month_".$name, "year_".$name); +return $rv; +} + +=head2 get_value() +Returns the date as a Unix time number (for zero o'clock) +=cut +sub get_value +{ +my ($self) = @_; +my $in = $self->{'form'} ? $self->{'form'}->{'in'} : undef; +if ($in && defined($in->{"day_".$self->{'name'}})) { + my $rv = $self->to_time($in); + return defined($rv) ? $rv : $self->{'value'}; + } +elsif ($in && defined($in->{"ui_value_".$self->{'name'}})) { + return $in->{"ui_value_".$self->{'name'}}; + } +else { + return $self->{'value'}; + } +} + +sub to_time +{ +my ($self, $in) = @_; +my $day = $in->{"day_".$self->{'name'}}; +return undef if ($day !~ /^\d+$/); +my $month = $in->{"month_".$self->{'name'}}-1; +my $year = $in->{"year_".$self->{'name'}}-1900; +return undef if ($year !~ /^\d+$/); +my $rv = eval { timelocal(0, 0, 0, $day, $month, $year) }; +return $@ ? undef : $rv; +} + +sub set_validation_func +{ +my ($self, $func) = @_; +$self->{'validation_func'} = $func; +} + +=head2 validate() +Ensures that the date is valid +=cut +sub validate +{ +my ($self) = @_; +my $tm = $self->to_time($self->{'form'}->{'in'}); +if (!defined($tm)) { + return ( $text{'ui_edate'} ); + } +if ($self->{'validation_func'}) { + my $err = &{$self->{'validation_func'}}($self->get_value(), + $self->{'name'}, + $self->{'form'}); + return ( $err ) if ($err); + } +return ( ); +} + +1; + diff --git a/Webmin/DynamicBar.pm b/Webmin/DynamicBar.pm new file mode 100644 index 000000000..850bbaf86 --- /dev/null +++ b/Webmin/DynamicBar.pm @@ -0,0 +1,119 @@ +package Webmin::DynamicBar; +use WebminCore; + +=head2 new Webmin::DynamicBar(&start-function, max) +A page element for displaying progress towards some goal, like the download of +a file. +=cut +sub new +{ +my ($self, $func, $max) = @_; +$self = { 'func' => $func, + 'name' => "dynamic".++$dynamic_count, + 'width' => 80, + 'max' => $max }; +bless($self); +return $self; +} + +=head2 set_message(text) +Sets the text describing what we are waiting for +=cut +sub set_message +{ +my ($self, $message) = @_; +$self->{'message'} = $message; +} + +sub get_message +{ +my ($self) = @_; +return $self->{'message'}; +} + +=head2 html() +Returns the HTML for the text field +=cut +sub html +{ +my ($self) = @_; +my $rv; +if ($self->get_message()) { + $rv .= $self->get_message()."

\n"; + } +$rv .= "

{'name'}>"; +$rv .= "{'name'} size=$self->{'width'} disabled=true style='font-family: courier'>"; +$rv .= " "; +$rv .= "{'name'} size=3 disabled=true style='font-family: courier'>%"; +$rv .= "
"; +return $rv; +} + +=head2 start() +Called by the page to begin the progress +=cut +sub start +{ +my ($self) = @_; +&{$self->{'func'}}($self); +} + +=head2 update(pos) +Called by the function to update the position of the bar. +=cut +sub update +{ +my ($self, $pos) = @_; +my $pc = int(100*$pos/$self->{'max'}); +if ($pc != $self->{'lastpc'}) { + my $xn = int($self->{'width'}*$pos/$self->{'max'}); + my $xes = "X" x $xn; + print "\n"; + print "\n"; + $self->{'lastpc'} = $pc; + } +} + +=head2 set_wait(wait) +If called with a non-zero arg, generation of the page should wait until this +the progress is complete. Otherwise, the page will be generated completely before +the start function is called +=cut +sub set_wait +{ +my ($self, $wait) = @_; +$self->{'wait'} = $wait; +} + +sub get_wait +{ +my ($self) = @_; +return $self->{'wait'}; +} + +=head2 set_page(Webmin::Page) +Called when this dynamic text box is added to a page +=cut +sub set_page +{ +my ($self, $page) = @_; +$self->{'page'} = $page; +} + +sub set_width +{ +my ($self, $width) = @_; +$self->{'width'} = $width; +} + +=head2 needs_unbuffered() +Must return 1 if the page needs to be in un-buffered and no-table mode +=cut +sub needs_unbuffered +{ +return 0; +} + + +1; + diff --git a/Webmin/DynamicHTML.pm b/Webmin/DynamicHTML.pm new file mode 100644 index 000000000..80952e52c --- /dev/null +++ b/Webmin/DynamicHTML.pm @@ -0,0 +1,76 @@ +package Webmin::DynamicHTML; +use WebminCore; + +=head2 new Webmin::DynamicHTML(&function, &args, [before]) +When the page is being rendered, executes the given function and prints any +text that it returns. +=cut +sub new +{ +my ($self, $func, $args, $before) = @_; +$self = { 'func' => $func, + 'args' => $args, + 'before' => $before }; +bless($self); +return $self; +} + +=head2 set_before(text) +Sets the text describing what we are waiting for +=cut +sub set_before +{ +my ($self, $before) = @_; +$self->{'before'} = $before; +} + +sub get_before +{ +my ($self) = @_; +return $self->{'before'}; +} + +sub html +{ +my ($self) = @_; +my $rv; +if ($self->get_before()) { + $rv .= $self->get_before()."

\n"; + } +return $rv; +} + +=head2 start() +Called by the page to begin the dynamic output. +=cut +sub start +{ +my ($self) = @_; +&{$self->{'func'}}($self, @$args); +} + +sub get_wait +{ +my ($self) = @_; +return 1; +} + +=head2 needs_unbuffered() +Must return 1 if the page needs to be in un-buffered and no-table mode +=cut +sub needs_unbuffered +{ +return 1; +} + +=head2 set_page(Webmin::Page) +Called when this dynamic HTML element is added to a page +=cut +sub set_page +{ +my ($self, $page) = @_; +$self->{'page'} = $page; +} + +1; + diff --git a/Webmin/DynamicText.pm b/Webmin/DynamicText.pm new file mode 100644 index 000000000..279d28bb4 --- /dev/null +++ b/Webmin/DynamicText.pm @@ -0,0 +1,123 @@ +# XXX should support non-Javascript mode? +package Webmin::DynamicText; +use WebminCore; + +=head2 new Webmin::DynamicText(&start-function, &args) +A page element for displaying text that takes time to generate, such as from +a long-running script. Uses a non-editable text box, updated via Javascript. +The function will be called when it is time to start producing output, with this +object as a parameter. It must call the add_line function on the object for each +new line to be added. +=cut +sub new +{ +my ($self, $func, $args) = @_; +$self = { 'func' => $func, + 'args' => $args, + 'name' => "dynamic".++$dynamic_count, + 'rows' => 20, + 'cols' => 80 }; +bless($self); +return $self; +} + +=head2 set_message(text) +Sets the text describing what we are waiting for +=cut +sub set_message +{ +my ($self, $message) = @_; +$self->{'message'} = $message; +} + +sub get_message +{ +my ($self) = @_; +return $self->{'message'}; +} + +=head2 html() +Returns the HTML for the text box +=cut +sub html +{ +my ($self) = @_; +my $rv; +if ($self->get_message()) { + $rv .= $self->get_message()."

\n"; + } +$rv .= "

{'name'}>"; +$rv .= "\n"; +$rv .= "
"; +return $rv; +} + +=head2 start() +Called by the page to begin the dynamic output. +=cut +sub start +{ +my ($self) = @_; +&{$self->{'func'}}($self, @$args); +} + +=head2 add_line(line) +Called by the function to add a line of text to this output +=cut +sub add_line +{ +my ($self, $line) = @_; +$line =~ s/\r|\n//g; +$line = "e_escape($line); +print "\n"; +} + +=head2 set_wait(wait) +If called with a non-zero arg, generation of the page should wait until this +text box is complete. Otherwise, the page will be generated completely before the +start function is called +=cut +sub set_wait +{ +my ($self, $wait) = @_; +$self->{'wait'} = $wait; +} + +sub get_wait +{ +my ($self) = @_; +return $self->{'wait'}; +} + +=head2 set_page(Webmin::Page) +Called when this dynamic text box is added to a page +=cut +sub set_page +{ +my ($self, $page) = @_; +$self->{'page'} = $page; +} + +sub set_rows +{ +my ($self, $rows) = @_; +$self->{'rows'} = $rows; +} + +sub set_cols +{ +my ($self, $cols) = @_; +$self->{'cols'} = $cols; +} + +=head2 needs_unbuffered() +Must return 1 if the page needs to be in un-buffered and no-table mode +=cut +sub needs_unbuffered +{ +return 0; +} + +1; + diff --git a/Webmin/DynamicWait.pm b/Webmin/DynamicWait.pm new file mode 100644 index 000000000..e725e7c84 --- /dev/null +++ b/Webmin/DynamicWait.pm @@ -0,0 +1,129 @@ +package Webmin::DynamicWait; +use WebminCore; + +=head2 new Webmin::DynamicWait(&start-function, [&args]) +A page element indicating that something is happening. +=cut +sub new +{ +my ($self, $func, $args) = @_; +$self = { 'func' => $func, + 'args' => $args, + 'name' => "dynamic".++$dynamic_count, + 'width' => 80, + 'delay' => 20 }; +bless($self); +return $self; +} + +=head2 set_message(text) +Sets the text describing what we are waiting for +=cut +sub set_message +{ +my ($self, $message) = @_; +$self->{'message'} = $message; +} + +sub get_message +{ +my ($self) = @_; +return $self->{'message'}; +} + +=head2 html() +Returns the HTML for the text field used to indicate progress +=cut +sub html +{ +my ($self) = @_; +my $rv; +if ($self->get_message()) { + $rv .= $self->get_message()."

\n"; + } +$rv .= "

{'name'}>"; +$rv .= "{'name'} size=$self->{'width'} disabled=true style='font-family: courier'>"; +$rv .= "
"; +return $rv; +} + +=head2 start() +Called by the page to begin the progress. Also starts a process to update the +Javascript text box +=cut +sub start +{ +my ($self) = @_; +$self->{'pid'} = fork(); +if (!$self->{'pid'}) { + my $pos = 0; + while(1) { + select(undef, undef, undef, $self->{'delay'}/1000.0); + my $str = (" " x $pos) . ("x" x 10); + print "\n"; + $pos++; + $pos = 0 if ($pos == $self->{'width'}); + } + exit; + } +&{$self->{'func'}}($self, @{$self->{'args'}}); +} + +=head2 stop() +Called back by the function when whatever we were waiting for is done +=cut +sub stop +{ +my ($self) = @_; +if ($self->{'pid'}) { + kill('TERM', $self->{'pid'}); + } +my $str = (" " x ($self->{'width'}/2 - 2)) . "DONE"; +print "\n"; +} + +=head2 set_wait(wait) +If called with a non-zero arg, generation of the page should wait until this +the progress is complete. Otherwise, the page will be generated completely before +the start function is called +=cut +sub set_wait +{ +my ($self, $wait) = @_; +$self->{'wait'} = $wait; +} + +sub get_wait +{ +my ($self) = @_; +return $self->{'wait'}; +} + +=head2 set_page(Webmin::Page) +Called when this dynamic text box is added to a page +=cut +sub set_page +{ +my ($self, $page) = @_; +$self->{'page'} = $page; +} + +sub set_width +{ +my ($self, $width) = @_; +$self->{'width'} = $width; +} + +=head2 needs_unbuffered() +Must return 1 if the page needs to be in un-buffered and no-table mode +=cut +sub needs_unbuffered +{ +return 0; +} + + + + +1; + diff --git a/Webmin/ErrorPage.pm b/Webmin/ErrorPage.pm new file mode 100644 index 000000000..7f3756505 --- /dev/null +++ b/Webmin/ErrorPage.pm @@ -0,0 +1,20 @@ +package Webmin::ErrorPage; +use WebminCore; + +=head2 new Webmin::ErrorPage(subheading, title, message, [program-output], [help-name]) +Create a new page object for showing an error of some kind +=cut +sub new +{ +if (defined(&Webmin::Theme::ErrorPage::new)) { + return new Webmin::Theme::ErrorPage(@_[1..$#_]); + } +my ($self, $subheading, $title, $message, $output, $help) = @_; +$self = new Webmin::Page($subheading, $title, $help); +$self->add_message("",$text{'error'}," : ",$message,""); +$self->add_message("
",$output,"
"); +return $self; +} + +1; + diff --git a/Webmin/File.pm b/Webmin/File.pm new file mode 100644 index 000000000..92f14173a --- /dev/null +++ b/Webmin/File.pm @@ -0,0 +1,70 @@ +package Webmin::File; +use Webmin::Textbox; +use WebminCore; +@ISA = ( "Webmin::Textbox" ); + +=head2 new Webmin::File(name, value, size, [directory], [disabled]) +A text box for selecting a file +=cut +sub new +{ +if (defined(&Webmin::Theme::File::new)) { + return new Webmin::Theme::File(@_[1..$#_]); + } +my ($self, $name, $value, $size, $directory, $disabled) = @_; +$self = new Webmin::Textbox($name, $value, $size, $disabled); +bless($self); +$self->set_directory($directory); +return $self; +} + +=head2 html() +Returns the HTML for this file input +=cut +sub html +{ +my ($self) = @_; +my $rv = Webmin::Textbox::html($self); +my $name = $self->get_name(); +my $directory = $self->get_directory(); +my $add = 0; +my $chroot = $self->get_chroot(); +$rv .= "\n"; +return $rv; +} + +sub set_directory +{ +my ($self, $directory) = @_; +$self->{'directory'} = $directory; +} + +sub get_directory +{ +my ($self) = @_; +return $self->{'directory'}; +} + +sub set_chroot +{ +my ($self, $chroot) = @_; +$self->{'chroot'} = $chroot; +} + +sub get_chroot +{ +my ($self) = @_; +return $self->{'chroot'}; +} + +=head2 get_input_names() +Returns the actual names of all HTML elements that make up this input +=cut +sub get_input_names +{ +my ($self) = @_; +return ( $self->{'name'}, $self->{'name'}."_button" ); +} + +1; + diff --git a/Webmin/Form.pm b/Webmin/Form.pm new file mode 100644 index 000000000..d85dbc907 --- /dev/null +++ b/Webmin/Form.pm @@ -0,0 +1,405 @@ +package Webmin::Form; +use WebminCore; + +=head2 new Webmin::Form(cgi, [method]) +Creates a new form, which submits to the given CGI +=cut +sub new +{ +if (defined(&Webmin::Theme::Form::new)) { + return new Webmin::Theme::Form(@_[1..$#_]); + } +my ($self, $program, $method) = @_; +$self = { 'method' => 'get', + 'name' => "form".++$form_count }; +bless($self); +$self->set_program($program); +$self->set_method($method) if ($method); +return $self; +} + +=head2 html() +Returns the HTML that makes up this form +=cut +sub html +{ +my ($self) = @_; +my $rv; +if ($self->get_align()) { + $rv .= "
\n"; + } +$rv .= $self->form_start(); +if ($self->get_heading()) { + if (defined(&ui_subheading)) { + $rv .= &ui_subheading($self->get_heading()); + } + else { + $rv .= "

".$self->get_heading()."

\n"; + } + } + +# Add the sections +foreach my $h (@{$self->{'hiddens'}}) { + $rv .= &ui_hidden($h->[0], $h->[1])."\n"; + } +foreach my $s (@{$self->{'sections'}}) { + $rv .= $s->html(); + } + +# Check if we have any inputs that need disabling +my @dis = $self->list_disable_inputs(); +if (@dis) { + # Yes .. generate a function for them + $rv .= "\n"; + } + +# Add the buttons at the end of the form +my @buttonargs; +foreach my $b (@{$self->{'buttons'}}) { + if (ref($b)) { + # An array of inputs + my $ihtml = join(" ", map { $_->html() } @$b); + push(@buttonargs, $ihtml); + } + else { + # A spacer + push(@buttonargs, ""); + } + } +$rv .= &ui_form_end(\@buttonargs); + +if ($self->get_align()) { + $rv .= "
\n"; + } + +# Call the Javascript disable function +if (@dis) { + $rv .= "\n"; + } + +return $rv; +} + +sub form_start +{ +my ($self) = @_; +return "
{'method'} eq "post" ? "method=post" : + $self->{'method'} eq "form-data" ? + "method=post enctype=multipart/form-data" : + "method=get")." name=$self->{'name'}>\n"; +} + +=head2 add_section(section) +Adds a Webmin::Section object to this form +=cut +sub add_section +{ +my ($self, $section) = @_; +push(@{$self->{'sections'}}, $section); +$section->set_form($self); +} + +=head2 get_section(idx) +=cut +sub get_section +{ +my ($self, $idx) = @_; +return $self->{'sections'}->[$idx]; +} + +=head2 add_button(button, [beside, ...]) +Adds a Webmin::Submit object to this form, for display at the bottom +=cut +sub add_button +{ +my ($self, $button, @beside) = @_; +push(@{$self->{'buttons'}}, [ $button, @beside ]); +} + +=head2 add_button_spacer() +Adds a gap between buttons, for grouping +=cut +sub add_button_spacer +{ +my ($self, $spacer) = @_; +push(@{$self->{'buttons'}}, $spacer); +} + +=head2 add_hidden(name, value) +Adds some hidden input to this form, for passing to the CGI +=cut +sub add_hidden +{ +my ($self, $name, $value) = @_; +push(@{$self->{'hiddens'}}, [ $name, $value ]); +} + +=head2 validate() +Validates all form inputs, based on the current CGI input hash. Returns a list +of errors, each of which is field name and error message. +=cut +sub validate +{ +my ($self) = @_; +my @errs; +foreach my $s (@{$self->{'sections'}}) { + push(@errs, $s->validate($self->{'in'})); + } +return @errs; +} + +=head2 validate_redirect(page, [&extra-errors]) +Validates the form, and if any errors are found re-directs to the given page +with the errors, so that they can be displayed. +=cut +sub validate_redirect +{ +my ($self, $page, $extras) = @_; +if ($self->{'in'}->{'ui_redirecting'}) { + # If this page is displayed as part of a redirect, no need to validate! + return; + } +my @errs = $self->validate(); +push(@errs, @$extras); +if (@errs) { + my (@errlist, @vallist); + foreach my $e (@errs) { + push(@errlist, &urlize("ui_error_".$e->[0])."=". + &urlize($e->[1])); + } + foreach my $i ($self->list_inputs()) { + my $v = $i->get_value(); + my @vals = ref($v) ? @$v : ( $v ); + @vals = ( undef ) if (!@vals); + foreach $v (@vals) { + push(@vallist, + &urlize("ui_value_".$i->get_name())."=". + &urlize($v)); + } + } + foreach my $h (@{$self->{'hiddens'}}) { + push(@vallist, + &urlize($h->[0])."=".&urlize($h->[1])); + } + if ($page =~ /\?/) { $page .= "&"; } + else { $page .= "?"; } + &redirect($page.join("&", "ui_redirecting=1", @errlist, @vallist)); + exit(0); + } +} + +=head2 validate_error(whatfailed) +Validates the form, and if any errors are found displays an error page. +=cut +sub validate_error +{ +my ($self, $whatfailed) = @_; +my @errs = $self->validate(); +&error_setup($whatfailed); +if (@errs == 1) { + &error($errs[0]->[2] ? "$errs[0]->[2] : $errs[0]->[1]" + : $errs[0]->[1]); + } +elsif (@errs > 1) { + my $msg = $text{'ui_errors'}."
"; + foreach my $e (@errs) { + $msg .= $e->[2] ? "$e->[2] : $e->[1]
\n" + : "$e->[1]
\n"; + } + &error($msg); + } +} + +=head2 field_errors(name) +Returns a list of error messages associated with the field of some name, from +the input passed to set_input +=cut +sub field_errors +{ +my ($self, $name) = @_; +my @errs; +my $in = $self->{'in'}; +foreach my $i (keys %$in) { + if ($i eq "ui_error_".$name) { + push(@errs, split(/\0/, $in->{$i})); + } + } +return @errs; +} + +=head2 set_input(&input) +Passes the form input hash to this form object, for use by the validate +functions and for displaying errors next to fields. +=cut +sub set_input +{ +my ($self, $in) = @_; +$self->{'in'} = $in; +} + +=head2 get_value(input-name) +Returns the value of the input with the given name. +=cut +sub get_value +{ +my ($self, $name) = @_; +foreach my $s (@{$self->{'sections'}}) { + my $rv = $s->get_value($name); + return $rv if (defined($rv)); + } +return $self->{'in'}->{$name}; +} + +=head2 get_input(name) +Returns the input with the given name +=cut +sub get_input +{ +my ($self, $name) = @_; +foreach my $i ($self->list_inputs()) { + return $i if ($i->get_name() eq $name); + } +return undef; +} + +sub set_program +{ +my ($self, $program) = @_; +$self->{'program'} = $program; +} + +sub set_method +{ +my ($self, $method) = @_; +$self->{'method'} = $method; +} + +=head2 list_inputs() +Returns all inputs in all form sections +=cut +sub list_inputs +{ +my ($self) = @_; +my @rv; +foreach my $s (@{$self->{'sections'}}) { + push(@rv, $s->list_inputs()); + } +return @rv; +} + +=head2 list_disable_inputs() +Returns a list of inputs that have disable functions +=cut +sub list_disable_inputs +{ +my ($self) = @_; +my @dis; +foreach my $i ($self->list_inputs()) { + push(@dis, $i) if ($i->get_disable_code()); + } +return @dis; +} + +=head2 set_page(Webmin::Page) +Called when this form is added to a page +=cut +sub set_page +{ +my ($self, $page) = @_; +$self->{'page'} = $page; +} + +=head2 get_changefunc(&input) +Called by some input, to return the Javascript that should be called when this +input changes it's value. +=cut +sub get_changefunc +{ +my ($self, $input) = @_; +my @dis = $self->list_disable_inputs(); +if (@dis) { + return "ui_disable_".$self->{'name'}."(form)"; + } +return undef; +} + +=head2 set_heading(text) +Sets the heading to be displayed above the form +=cut +sub set_heading +{ +my ($self, $heading) = @_; +$self->{'heading'} = $heading; +} + +sub get_heading +{ +my ($self) = @_; +return $self->{'heading'}; +} + +=head2 get_formno() +Returns the index of this form on the page +=cut +sub get_formno +{ +my ($self) = @_; +my $n = 0; +foreach my $f (@{$self->{'page'}->{'contents'}}) { + if ($f eq $self) { + return $n; + } + elsif (ref($f) =~ /Form/) { + $n++; + } + } +return undef; +} + +=head2 add_onload(code) +Adds some Javascript code for inclusion in the onLoad tag +=cut +sub add_onload +{ +my ($self, $code) = @_; +push(@{$self->{'onloads'}}, $code); +} + +=head2 add_script(code) +Adds some Javascript code for putting in the section +=cut +sub add_script +{ +my ($self, $script) = @_; +push(@{$self->{'scripts'}}, $script); +} + +=head2 set_align(align) +Sets the alignment on the page (left, center, right) +=cut +sub set_align +{ +my ($self, $align) = @_; +$self->{'align'} = $align; +} + +sub get_align +{ +my ($self) = @_; +return $self->{'align'}; +} + +1; + diff --git a/Webmin/Group.pm b/Webmin/Group.pm new file mode 100644 index 000000000..cd45640e1 --- /dev/null +++ b/Webmin/Group.pm @@ -0,0 +1,57 @@ +package Webmin::Group; +use Webmin::Textbox; +use WebminCore; +@ISA = ( "Webmin::Textbox" ); + +=head2 new Webmin::Group(name, value, [multiple], [disabled]) +A text box for entering or selecting one or many Unix groupnames +=cut +sub new +{ +if (defined(&Webmin::Theme::Group::new)) { + return new Webmin::Theme::Group(@_[1..$#_]); + } +my ($self, $name, $value, $multiple, $disabled) = @_; +$self = new Webmin::Textbox($name, $value, $multiple ? 40 : 15, $disabled); +bless($self); +$self->set_multiple($multiple); +return $self; +} + +=head2 html() +Returns the HTML for this group input +=cut +sub html +{ +my ($self) = @_; +my $rv = Webmin::Textbox::html($self); +my $name = $self->get_name(); +my $multiple = $self->get_multiple(); +local $w = $multiple ? 500 : 300; +$rv .= " \n"; +return $rv; +} + +sub set_multiple +{ +my ($self, $multiple) = @_; +$self->{'multiple'} = $multiple; +} + +sub get_multiple +{ +my ($self) = @_; +return $self->{'multiple'}; +} + +=head2 get_input_names() +Returns the actual names of all HTML elements that make up this input +=cut +sub get_input_names +{ +my ($self) = @_; +return ( $self->{'name'}, $self->{'name'}."_button" ); +} + +1; + diff --git a/Webmin/Icon.pm b/Webmin/Icon.pm new file mode 100644 index 000000000..d1d460f77 --- /dev/null +++ b/Webmin/Icon.pm @@ -0,0 +1,64 @@ +package Webmin::Icon; +use WebminCore; + +=head2 Webmin::Icon(type, [message]) +This object generates an icon indicating some status. Possible types are : +ok - OK +critial - A serious problem +major - A relatively serious problem +minor - A small problem +Can be used inside tables and property lists +=cut +sub new +{ +if (defined(&Webmin::Theme::Icon::new) && caller() !~ /Webmin::Theme::Icon/) { + return new Webmin::Theme::Icon(@_[1..$#_]); + } +my ($self, $type, $message) = @_; +$self = { }; +bless($self); +$self->set_type($type); +$self->set_message($message) if (defined($message)); +return $self; +} + +=head2 html() +Returns HTML for the icon +=cut +sub html +{ +my ($self) = @_; +my $rv; +$rv .= "get_type().".gif align=middle>"; +if ($self->get_message()) { + $rv .= " ".$self->get_message(); + } +return $rv; +} + +sub set_type +{ +my ($self, $type) = @_; +$self->{'type'} = $type; +} + +sub get_type +{ +my ($self) = @_; +return $self->{'type'}; +} + +sub set_message +{ +my ($self, $message) = @_; +$self->{'message'} = $message; +} + +sub get_message +{ +my ($self) = @_; +return $self->{'message'}; +} + +1; + diff --git a/Webmin/Input.pm b/Webmin/Input.pm new file mode 100644 index 000000000..5f5504871 --- /dev/null +++ b/Webmin/Input.pm @@ -0,0 +1,134 @@ +package Webmin::Input; +use WebminCore; + +sub set_form +{ +my ($self, $form) = @_; +$self->{'form'} = $form; +} + +sub set_name +{ +my ($self, $name) = @_; +$self->{'name'} = $name; +} + +sub get_name +{ +my ($self) = @_; +return $self->{'name'}; +} + +sub set_disabled +{ +my ($self, $disabled) = @_; +$self->{'disabled'} = $disabled; +} + +sub get_disabled +{ +my ($self) = @_; +return $self->{'disabled'}; +} + +=head2 validate() +No validation is done by default +=cut +sub validate +{ +return ( ); +} + +sub set_value +{ +my ($self, $value) = @_; +$self->{'value'} = $value; +} + +=head2 get_value() +Returns the current value for this field as entered by the user, the value +set when the form is re-displayed due to an error, or the initial value. +=cut +sub get_value +{ +my ($self) = @_; +my $in = $self->{'form'} ? $self->{'form'}->{'in'} : undef; +if ($in && (defined($in->{$self->{'name'}}) || + defined($in->{"ui_exists_".$self->{'name'}}))) { + return $in->{$self->{'name'}}; + } +elsif ($in && defined($in->{"ui_value_".$self->{'name'}})) { + return $in->{"ui_value_".$self->{'name'}}; + } +else { + return $self->{'value'}; + } +} + +=head2 set_disable_code(javascript) +Must be provided with a Javascript expression that will return true when this +input should be disabled. May refer to other fields, via the variable 'form'. +ie. form.mode.value = "0" +Will be called every time any field's value changes. +=cut +sub set_disable_code +{ +my ($self, $code) = @_; +$self->{'disablecode'} = $code; +} + +sub get_disable_code +{ +my ($self) = @_; +return $self->{'disablecode'}; +} + +=head2 get_input_names() +Returns the actual names of all HTML elements that make up this input +=cut +sub get_input_names +{ +my ($self) = @_; +return ( $self->{'name'} ); +} + +=head2 set_label(text) +Sets HTML to be displayed before this field +=cut +sub set_label +{ +my ($self, $label) = @_; +$self->{'label'} = $label; +} + +sub get_label +{ +my ($self) = @_; +return $self->{'label'}; +} + +sub set_mandatory +{ +my ($self, $mandatory, $mandmesg) = @_; +$self->{'mandatory'} = $mandatory; +$self->{'mandmesg'} = $mandmesg if (defined($mandmesg)); +} + +sub get_mandatory +{ +my ($self) = @_; +return $self->{'mandatory'}; +} + +=head2 get_errors() +Returns a list of errors associated with this field +=cut +sub get_errors +{ +my ($self) = @_; +return $self->{'form'} ? $self->{'form'}->field_errors($self->get_name()) + : ( ); +} + +1; + diff --git a/Webmin/InputTable.pm b/Webmin/InputTable.pm new file mode 100644 index 000000000..020fa9e62 --- /dev/null +++ b/Webmin/InputTable.pm @@ -0,0 +1,137 @@ +package Webmin::InputTable; +use Webmin::Table; +use WebminCore; +@ISA = ( "Webmin::Table" ); + +=head2 new Webmin::InputTable(&headings, [width], [name], [heading]) +A table containing multiple rows of inputs, each of which is the same +=cut +sub new +{ +if (defined(&Webmin::Theme::InputTable::new) && + caller() !~ /Webmin::Theme::InputTable/) { + return new Webmin::Theme::InputTable(@_[1..$#_]); + } +my $self = defined(&Webmin::Theme::Table::new) ? Webmin::Theme::Table::new(@_) + : Webmin::Table::new(@_); +bless($self); +$self->{'rowcount'} = 0; +return $self; +} + +=head2 set_inputs(&inputs) +Sets the objects to be used for each row +=cut +sub set_inputs +{ +my ($self, $classes) = @_; +$self->{'classes'} = $classes; +} + +=head2 add_values(&values) +Adds a row of inputs, with the given values +=cut +sub add_values +{ +my ($self, $values) = @_; +my @row; +for(my $i=0; $i<@$values; $i++) { + my $cls = $self->{'classes'}->[$i]; + my $newin = { %$cls }; + bless($newin, ref($cls)); + $newin->set_value($values->[$i]); + $newin->set_name($newin->get_name()."_".$self->{'rowcount'}); + $newin->set_form($self->{'form'}) if ($self->{'form'}); + push(@row, $newin); + } +$self->add_row(\@row); +$self->{'rowcount'}++; +} + +=head2 get_values(row) +Returns the values of the inputs in the given row +=cut +sub get_values +{ +my ($self, $row) = @_; +my @rv; +foreach my $i (@{$self->{'rows'}->[$row]}) { + if (ref($i) && $i->isa("Webmin::Input")) { + push(@rv, $i->get_value()); + } + } +return @rv; +} + +=head2 list_inputs() +=cut +sub list_inputs +{ +my ($self) = @_; +my @rv = Webmin::Table::list_inputs($self); +foreach my $r (@{$self->{'rows'}}) { + foreach my $i (@$r) { + if ($i && ref($i) && $i->isa("Webmin::Input")) { + push(@rv, $i); + } + } + } +return @rv; +} + +sub get_rowcount +{ +my ($self) = @_; +return $self->{'rowcount'}; +} + +=head2 validate() +Validates all inputs, and returns a list of error messages +=cut +sub validate +{ +my ($self) = @_; +my $seli = $self->{'selectinput'}; +my @errs; +if ($seli) { + push(@errs, map { [ $seli->get_name(), $_ ] } $seli->validate()); + } +foreach my $i (@{$self->{'inputs'}}) { + foreach my $e ($i->validate()) { + push(@errs, [ $i->get_name(), $e ]); + } + } +my $k = 1; +foreach my $r (@{$self->{'rows'}}) { + my $j = 0; + my $skip; + if (defined($self->{'control'})) { + if ($r->[$self->{'control'}]->get_value() eq "") { + $skip = 1; + } + } + foreach my $i (@$r) { + if ($i && ref($i) && $i->isa("Webmin::Input") && !$skip) { + my $label = &text('ui_rowlabel', $k, $self->{'headings'}->[$j]); + foreach my $e ($i->validate()) { + push(@errs, [ $i->get_name(), $label." ".$e ]); + } + } + $j++; + } + $k++; + } +return @errs; +} + +=head2 set_control(column) +Sets the column for which an empty value means no validation should be done +=cut +sub set_control +{ +my ($self, $control) = @_; +$self->{'control'} = $control; +} + +1; + diff --git a/Webmin/JavascriptButton.pm b/Webmin/JavascriptButton.pm new file mode 100644 index 000000000..f09d780e1 --- /dev/null +++ b/Webmin/JavascriptButton.pm @@ -0,0 +1,47 @@ +package Webmin::JavascriptButton; +use Webmin::Input; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::JavascriptButton(label, script, [disabled]) +Create a button that runs some Javascript when clicked +=cut +sub new +{ +if (defined(&Webmin::Theme::JavascriptButton::new) && + caller() !~ /Webmin::Theme::JavascriptButton/) { + return new Webmin::Theme::JavascriptButton(@_[1..$#_]); + } +my ($self, $value, $script, $disabled) = @_; +$self = { }; +bless($self); +$self->set_value($value); +$self->set_script($script); +$self->set_disabled($disabled) if ($disabled); +return $self; +} + +=head2 html() +Returns the HTML for this text input +=cut +sub html +{ +my ($self) = @_; +return "get_value())."\" ". + "onClick=\"".$self->get_script()."\">"; +} + +sub set_script +{ +my ($self, $script) = @_; +$self->{'script'} = $script; +} + +sub get_script +{ +my ($self) = @_; +return $self->{'script'}; +} + +1; + diff --git a/Webmin/LinkTable.pm b/Webmin/LinkTable.pm new file mode 100644 index 000000000..4acc42dbf --- /dev/null +++ b/Webmin/LinkTable.pm @@ -0,0 +1,314 @@ +package Webmin::LinkTable; +use Webmin::Table; +use WebminCore; + +=head2 new Webmin::LinkTable(heading, [columns], [width], [name]) +Creates a new table that just displays links, like in the Users and Groups module +=cut +sub new +{ +if (defined(&Webmin::Theme::LinkTable::new) && + caller() !~ /Webmin::Theme::LinkTable/) { + return new Webmin::Theme::LinkTable(@_[1..$#_]); + } +my ($self, $heading, $columns, $width, $name) = @_; +$self = { 'sorter' => \&Webmin::Table::default_sorter, + 'columns' => 4, + 'sortable' => 1 }; +bless($self); +$self->set_heading($heading); +$self->set_name($name) if (defined($name)); +$self->set_width($width) if (defined($width)); +$self->set_columns($columns) if (defined($columns)); +return $self; +} + +=head2 add_entry(name, link) +Adds one item to appear in the table +=cut +sub add_entry +{ +my ($self, $name, $link) = @_; +push(@{$self->{'entries'}}, [ $name, $link ]); +} + +=head2 html() +Returns the HTML for this table. +=cut +sub html +{ +my ($self) = @_; + +# Prepare the selector +my @srows = @{$self->{'entries'}}; +my %selmap; +if (defined($self->{'selectinput'})) { + my $i = 0; + foreach my $r (@srows) { + $selmap{$r} = $self->{'selectinput'}->one_html($i); + $i++; + } + } + +# Sort the entries +my $sortdir = $self->get_sortdir(); +if (defined($sortdir)) { + my $func = $self->{'sorter'}; + @srows = sort { my $so = &$func($a->[0], $b->[0]); + $sortdir ? -$so : $so } @srows; + } + +# Build the sorter +my $head; +my $thisurl = $self->{'form'}->{'page'}->get_myurl(); +$thisurl .= $thisurl =~ /\?/ ? "&" : "?"; +my $name = $self->get_name(); +if ($self->get_sortable()) { + $head = ""; + $head .= "
".$self->get_heading()." "; + if (!defined($sortdir)) { + # Not sorting .. show grey button + $head .= "". + ""; + } + else { + # Sorting .. show button to switch mode + my $notsort = !$sortdir; + $head .= "". + ""; + } + $head .= "
"; + } +else { + $head = $self->get_heading(); + } + +# Find any errors +my $rv; +if ($self->{'selectinput'}) { + # Get any errors for inputs + my @errs = $self->{'form'}->field_errors( + $self->{'selectinput'}->get_name()); + if (@errs) { + foreach my $e (@errs) { + $rv .= "$e
\n"; + } + } + } + +# Create the actual table +$rv .= &ui_table_start($head, + defined($self->{'width'}) ? "width=$self->{'width'}" + : undef, 1); +$rv .= ""; +my $i = 0; +my $cols = $self->get_columns(); +my $pc = 100/$cols; +foreach my $r (@srows) { + $rv .= "\n" if ($i%$cols == 0); + $rv .= "\n"; + $rv .= "\n" if ($i%$cols == $cols-1); + $i++; + } +if ($i%$cols) { + # Finish off row + while($i++%$cols != $cols-1) { + $rv .= "\n"; + } + $rv .= "\n"; + } +$rv .= "
".$selmap{$r}."". + &html_escape($r->[0])."
"; +$rv .= &ui_table_end(); +return $rv; +} + +=head2 set_heading(text) +Sets the heading text to appear above the table +=cut +sub set_heading +{ +my ($self, $heading) = @_; +$self->{'heading'} = $heading; +} + +sub get_heading +{ +my ($self) = @_; +return $self->{'heading'}; +} + +sub set_name +{ +my ($self, $name) = @_; +$self->{'name'} = $name; +} + +=head2 get_name() +Returns the name for indentifying this table in HTML +=cut +sub get_name +{ +my ($self) = @_; +if (defined($self->{'name'})) { + return $self->{'name'}; + } +elsif ($self->{'form'}) { + my $secs = $self->{'form'}->{'sections'}; + for(my $i=0; $i<@$secs; $i++) { + return "table".$i if ($secs->[$i] eq $self); + } + } +return "table"; +} + +=head2 set_sorter(function) +Sets a function used for sorting fields. Will be called with two values to compare +=cut +sub set_sorter +{ +my ($self, $func) = @_; +$self->{'sorter'} = $func; +} + +=head2 default_sorter(value1, value2) +=cut +sub default_sorter +{ +my ($value1, $value2, $col) = @_; +return lc($value1) cmp lc($value2); +} + +=head2 set_sortable(sortable?) +Tells the table if sorting is allowed or not. By default, it is. +=cut +sub set_sortable +{ +my ($self, $sortable) = @_; +$self->{'sortable'} = $sortable; +} + +sub get_sortable +{ +my ($self) = @_; +return $self->{'sortable'}; +} + +=head2 get_sortdir() +Returns the order to sort in (1 for descending) +=cut +sub get_sortdir +{ +my ($self) = @_; +my $in = $self->{'form'} ? $self->{'form'}->{'in'} : undef; +my $name = $self->get_name(); +if ($in && defined($in->{"ui_sortdir_".$name})) { + return ( $in->{"ui_sortdir_".$name} ); + } +else { + return ( $self->{'sortdir'} ); + } +} + +=head2 set_sortdir(descending?) +Sets the default sort direction, unless overridden by the user. +=cut +sub set_sortcolumn +{ +my ($self, $desc) = @_; +$self->{'sortdir'} = $desc; +} + +=head2 set_width([number|number%]) +Sets the width of this entire table. Can be called with 100%, 500 or undef to use +the minimum possible width. +=cut +sub set_width +{ +my ($self, $width) = @_; +$self->{'width'} = $width; +} + +=head2 set_columns(cols) +Sets the number of columns to display +=cut +sub set_columns +{ +my ($self, $columns) = @_; +$self->{'columns'} = $columns; +} + +sub get_columns +{ +my ($self) = @_; +return $self->{'columns'}; +} + +=head2 set_form(form) +Called by the Webmin::Form object when this table is added to it +=cut +sub set_form +{ +my ($self, $form) = @_; +$self->{'form'} = $form; +if ($self->{'selectinput'}) { + $self->{'selectinput'}->set_form($form); + } +} + +=head2 set_selector(input) +Takes a Webmin::Checkboxes or Webmin::Radios object, and uses it to add checkboxes +to all the entries +=cut +sub set_selector +{ +my ($self, $input) = @_; +$self->{'selectinput'} = $input; +$input->set_form($form); +} + +=head2 get_selector() +Returns the UI element used for selecting rows +=cut +sub get_selector +{ +my ($self) = @_; +return $self->{'selectinput'}; +} + +=head2 validate() +Validates the selector input +=cut +sub validate +{ +my ($self) = @_; +my $seli = $self->{'selectinput'}; +if ($seli) { + return map { [ $seli->get_name(), $_ ] } $seli->validate(); + } +return ( ); +} + +=head2 get_value(input-name) +Returns the value of the input with the given name. +=cut +sub get_value +{ +my ($self, $name) = @_; +if ($self->{'selectinput'} && $self->{'selectinput'}->get_name() eq $name) { + return $self->{'selectinput'}->get_value(); + } +return undef; +} + +=head2 list_inputs() +Returns all inputs in all form sections +=cut +sub list_inputs +{ +my ($self) = @_; +return $self->{'selectinput'} ? ( $self->{'selectinput'} ) : ( ); +} + +1; + diff --git a/Webmin/Menu.pm b/Webmin/Menu.pm new file mode 100644 index 000000000..2f20503f5 --- /dev/null +++ b/Webmin/Menu.pm @@ -0,0 +1,86 @@ +package Webmin::Menu; +use WebminCore; + +=head2 new Webmin::Menu(&options, [columns]) +Generates a menu of options, typically using icons. +=cut +sub new +{ +my ($self, $options, $columns) = @_; +if (defined(&Webmin::Theme::Menu::new)) { + return new Webmin::Theme::Menu(@_[1..$#_]); + } +$self = { 'columns' => 4 }; +bless($self); +$self->set_options($options); +$self->set_columns($columns) if (defined($columns)); +return $self; +} + +=head2 html() +Returns the HTML for the table +=cut +sub html +{ +my ($self) = @_; +my (@links, @titles, @icons, @hrefs); +foreach my $o (@{$self->{'options'}}) { + push(@links, $o->{'link'}); + if ($o->{'link2'}) { + push(@titles, "$o->{'title'} $o->{'title2'}"); + } + else { + push(@titles, $o->{'title'}); + } + push(@icons, $o->{'icon'}); + push(@hrefs, $o->{'href'}); + } +my $rv = &capture_function_output(\&icons_table, + \@links, \@titles, \@icons, $self->get_columns(), + \@hrefs); +return $rv; +} + +=head2 add_option(&option) +=cut +sub add_option +{ +my ($self, $option) = @_; +push(@{$self->{'options'}}, $option); +} + +sub set_options +{ +my ($self, $options) = @_; +$self->{'options'} = $options; +} + +sub get_options +{ +my ($self) = @_; +return $self->{'options'}; +} + +sub set_columns +{ +my ($self, $columns) = @_; +$self->{'columns'} = $columns; +} + +sub get_columns +{ +my ($self) = @_; +return $self->{'columns'}; +} + +=head2 set_page(Webmin::Page) +Called when this menu is added to a page +=cut +sub set_page +{ +my ($self, $page) = @_; +$self->{'page'} = $page; +} + +1; + diff --git a/Webmin/Multiline.pm b/Webmin/Multiline.pm new file mode 100644 index 000000000..863c510cb --- /dev/null +++ b/Webmin/Multiline.pm @@ -0,0 +1,40 @@ +package Webmin::Multiline; +use Webmin::Textarea; +use WebminCore; +@ISA = ( "Webmin::Textarea" ); + +=head2 new Webmin::Multiline(name, &lines, rows, cols, [disabled]) +Create a new input for entering multiple text entries. By default, just uses +a textbox +=cut +sub new +{ +if (defined(&Webmin::Theme::Multiline::new)) { + return new Webmin::Theme::Multiline(@_[1..$#_]); + } +my ($self, $name, $lines, $rows, $cols, $wrap, $disabled) = @_; +$self = new Webmin::Textarea($name, join("\n", @$lines), $rows, $cols, undef, $disabled); +bless($self); +return $self; +} + +=head2 set_lines(&lines) +Sets the lines to display +=cut +sub set_lines +{ +my ($self, $lines) = @_; +$self->set_value(join("\n", @$lines)); +} + +=head2 get_lines() +Returns an array ref of lines to display +=cut +sub get_lines +{ +my ($self) = @_; +return [ split(/[\r|\n]+/, $self->get_value()) ]; +} + +1; + diff --git a/Webmin/OptTextarea.pm b/Webmin/OptTextarea.pm new file mode 100644 index 000000000..25e11bf79 --- /dev/null +++ b/Webmin/OptTextarea.pm @@ -0,0 +1,120 @@ +package Webmin::OptTextarea; +use Webmin::Textarea; +use WebminCore; +@ISA = ( "Webmin::Textarea" ); + +=head2 new Webmin::OptTextarea(name, value, rows, cols, [default-msg], [other-msg]) +Create a text area whose value is optional. +=cut +sub new +{ +if (defined(&Webmin::Theme::OptTextarea::new)) { + return new Webmin::Theme::OptTextarea(@_[1..$#_]); + } +my ($self, $name, $value, $rows, $cols, $default, $other) = @_; +$self = new Webmin::Textarea($name, $value, $rows, $cols); +bless($self); +$self->set_default($default || $text{'default'}); +$self->set_other($other) if ($other); +return $self; +} + +=head2 html() +Returns the HTML for this optional text input +=cut +sub html +{ +my ($self) = @_; +my $rv; +my $name = $self->get_name(); +my $value = $self->get_value(); +my $dis = $self->get_disabled(); +my $rows = $self->get_rows(); +my $columns = $self->get_cols(); +my $dis1 = &js_disable_inputs([ $name ], [ ]); +my $dis2 = &js_disable_inputs([ ], [ $name ]); +my $opt1 = $self->get_default(); +my $opt2 = $self->get_other(); +$rv .= " ".$opt1."\n"; +$rv .= " ".$opt2."
\n"; +$rv .= "\n"; +return $rv; + +} + +=head2 validate(&inputs) +=cut +sub validate +{ +my ($self, $in) = @_; +if (defined($self->get_value())) { + if ($self->get_value() eq "") { + return ( $text{'ui_nothing'} ); + } + return Webmin::Textbox::validate($self); + } +return ( ); +} + +sub set_default +{ +my ($self, $default) = @_; +$self->{'default'} = $default; +} + +sub get_default +{ +my ($self) = @_; +return $self->{'default'}; +} + +sub set_other +{ +my ($self, $other) = @_; +$self->{'other'} = $other; +} + +sub get_other +{ +my ($self) = @_; +return $self->{'other'}; +} + +=head2 get_value() +Returns the specified initial value for this field, or the value set when the +form is re-displayed due to an error. +=cut +sub get_value +{ +my ($self) = @_; +my $in = $self->{'form'} ? $self->{'form'}->{'in'} : undef; +if ($in && (defined($in->{$self->{'name'}}) || + defined($in->{$self->{'name'}.'_def'}))) { + return $in->{$self->{'name'}.'_def'} ? undef : $in->{$self->{'name'}}; + } +elsif ($in && defined($in->{"ui_value_".$self->{'name'}})) { + return $in->{"ui_value_".$self->{'name'}}; + } +else { + return $self->{'value'}; + } +} + +=head2 get_input_names() +Returns the actual names of all HTML elements that make up this input +=cut +sub get_input_names +{ +my ($self) = @_; +return ( $self->{'name'}, $self->{'name'}."_def[0]", + $self->{'name'}."_def[1]" ); +} + +1; + diff --git a/Webmin/OptTextbox.pm b/Webmin/OptTextbox.pm new file mode 100644 index 000000000..dd2d95f10 --- /dev/null +++ b/Webmin/OptTextbox.pm @@ -0,0 +1,90 @@ +package Webmin::OptTextbox; +use Webmin::Textbox; +use WebminCore; +@ISA = ( "Webmin::Textbox" ); + +=head2 new Webmin::OptTextbox(name, value, size, [default-msg], [other-msg]) +Create a text field whose value is optional. +=cut +sub new +{ +if (defined(&Webmin::Theme::OptTextbox::new)) { + return new Webmin::Theme::OptTextbox(@_[1..$#_]); + } +my ($self, $name, $value, $size, $default, $other) = @_; +$self = new Webmin::Textbox($name, $value, $size); +bless($self); +$self->set_default($default || $text{'default'}); +$self->set_other($other) if ($other); +return $self; +} + +=head2 html() +Returns the HTML for this optional text input +=cut +sub html +{ +my ($self) = @_; +return &ui_opt_textbox($self->get_name(), $self->get_value(), + $self->{'size'}, $self->{'default'}, + $self->{'other'}); +} + +=head2 validate(&inputs) +=cut +sub validate +{ +my ($self, $in) = @_; +if (defined($self->get_value())) { + if ($self->get_value() eq "") { + return ( $text{'ui_nothing'} ); + } + return Webmin::Textbox::validate($self); + } +return ( ); +} + +sub set_default +{ +my ($self, $default) = @_; +$self->{'default'} = $default; +} + +sub set_other +{ +my ($self, $other) = @_; +$self->{'other'} = $other; +} + +=head2 get_value() +Returns the specified initial value for this field, or the value set when the +form is re-displayed due to an error. +=cut +sub get_value +{ +my ($self) = @_; +my $in = $self->{'form'} ? $self->{'form'}->{'in'} : undef; +if ($in && (defined($in->{$self->{'name'}}) || + defined($in->{$self->{'name'}.'_def'}))) { + return $in->{$self->{'name'}.'_def'} ? undef : $in->{$self->{'name'}}; + } +elsif ($in && defined($in->{"ui_value_".$self->{'name'}})) { + return $in->{"ui_value_".$self->{'name'}}; + } +else { + return $self->{'value'}; + } +} + +=head2 get_input_names() +Returns the actual names of all HTML elements that make up this input +=cut +sub get_input_names +{ +my ($self) = @_; +return ( $self->{'name'}, $self->{'name'}."_def[0]", + $self->{'name'}."_def[1]" ); +} + +1; + diff --git a/Webmin/Page.pm b/Webmin/Page.pm new file mode 100644 index 000000000..4ee217be5 --- /dev/null +++ b/Webmin/Page.pm @@ -0,0 +1,426 @@ +package Webmin::Page; +use WebminCore; +use WebminCore; + +=head2 new Webmin::Page(subheading, title, [help-name], [show-config], + [no-module-index], [no-webmin-index], [rightside], + [header], [body-tags], [below-text]) +Create a new page object, with the given heading and other details +=cut +sub new +{ +if (defined(&Webmin::Theme::Page::new) && caller() !~ /Webmin::Theme::Page/) { + return new Webmin::Theme::Page(@_[1..$#_]); + } +my ($self, $subheading, $title, $help, $config, $noindex, $nowebmin, $right, + $header, $body, $below) = @_; +$self = { 'index' => 1, 'webmin' => 1, 'image' => "" }; +bless($self); +$self->set_subheading($subheading); +$self->set_title($title); +$self->set_help($help); +$self->set_config($config); +$self->set_index(!$noindex); +$self->set_webmin(!$nowebmin); +$self->set_right($right); +$self->set_header($header); +$self->set_body($body); +$self->set_below($below); +return $self; +} + +=head2 print() +Actually outputs this page +=cut +sub print +{ +my ($self) = @_; +my $rv; + +# Work out if we need buffering/table +foreach my $c (@{$self->{'contents'}}) { + if (ref($c) =~ /Dynamic/) { + $| = 1; + if ($c->needs_unbuffered()) { + $self->{'unbuffered'} = 1; + } + } + } + +# Show the page header +my $func = $self->{'unbuffered'} ? \&ui_print_unbuffered_header + : \&ui_print_header; +my $scripts; +foreach my $s (@{$self->{'scripts'}}, + (map { @{$_->{'scripts'}} } @{$self->{'contents'}})) { + $scripts .= "\n"; + } +my $onload; +my @onloads = ( @{$self->{'onloads'}}, + map { @{$_->{'onloads'}} } @{$self->{'contents'}} ); +if (@onloads) { + $onload = "onLoad='".join(" ", @onloads)."'"; + } +my @args = ( $self->{'subheading'}, $self->{'title'}, $self->{'image'}, + $self->{'help'}, $self->{'config'}, $self->{'index'} ? undef : 1, + $self->{'webmin'} ? undef : 1, $self->{'right'}, + $self->{'header'}.$scripts, $self->{'body'}.$onload, + $self->{'below'} ); +while(!defined($args[$#args])) { + pop(@args); + } +if ($self->get_refresh()) { + print "Refresh: ",$self->get_refresh(),"\r\n"; + } +&ui_print_header(@args); + +# Add the tab top +if ($self->{'tabs'}) { + print $self->{'tabs'}->top_html(); + } + +# Add any pre-content stuff +print $self->pre_content(); + +if ($self->{'errormsg'}) { + # Show the error only + print $self->get_errormsg_html(); + } +else { + # Generate the forms and other stuff + foreach my $c (@{$self->{'contents'}}) { + if (!ref($c)) { + # Just a message + print "$c

\n"; + } + else { + # Convert to HTML + eval { print $c->html(); }; + if ($@) { + print "

$@
"; + } + if (ref($c) =~ /Dynamic/ && $c->get_wait()) { + # Dynamic object .. execute now + $c->start(); + } + } + } + + # Generate buttons row + if ($self->{'buttons'}) { + print "
\n"; + print &ui_buttons_start(); + foreach my $b (@{$self->{'buttons'}}) { + print &ui_buttons_row(@$b); + } + print &ui_buttons_end(); + } + } + +# Add any post-content stuff +print $self->post_content(); + +# End of the tabs +if ($self->{'tabs'}) { + print $self->{'tabs'}->bottom_html(); + } + +# Print the footer +my @footerargs; +foreach my $f (@{$self->{'footers'}}) { + push(@footerargs, $f->[0], $f->[1]); + } +&ui_print_footer(@footerargs); + +# Start any dynamic objects +foreach my $c (@{$self->{'contents'}}) { + if (ref($c) =~ /Dynamic/ && !$c->get_wait()) { + $c->start(); + } + } +} + +=head2 add_footer(link, title) +Adds a return link, typically for display at the end of the page. +=cut +sub add_footer +{ +my ($self, $link, $title) = @_; +push(@{$self->{'footers'}}, [ $link, $title ]); +} + +=head2 get_footer(index) +Returns the link for the numbered footer +=cut +sub get_footer +{ +my ($self, $num) = @_; +return $self->{'footers'}->[$num]->[0]; +} + +=head2 add_message(text, ...) +Adds a text message, to appear at this point on the page +=cut +sub add_message +{ +my ($self, @message) = @_; +push(@{$self->{'contents'}}, join("", @message)); +} + +=head2 add_error(text, [command-output]) +Adds a an error message, possible accompanied by the command output +=cut +sub add_error +{ +my ($self, $message, $out) = @_; +$message = "$message"; +if ($out) { + $message .= "
$out
"; + } +push(@{$self->{'contents'}}, $message); +} + +=head2 add_message_after(&object, text, ...) +Adds a message after some existing object +=cut +sub add_message_after +{ +my ($self, $object, @message) = @_; +splice(@{$self->{'contents'}}, $self->position_of($object)+1, 0, + join("", @message)); +} + +=head2 add_error_after(&object, text, [command-output]) +Adds an error message after some existing object +=cut +sub add_error_after +{ +my ($self, $object, $message, $out) = @_; +$message = "$message"; +if ($out) { + $message .= "
$out
"; + } +splice(@{$self->{'contents'}}, $self->position_of($object)+1, 0, + $message); +} + +sub position_of +{ +my ($self, $object) = @_; +for(my $i=0; $i<@{$self->{'contents'}}; $i++) { + if ($self->{'contents'}->[$i] eq $object) { + return $i; + } + } +print STDERR "Could not find $object in ",join(" ",@{$self->{'contents'}}),"\n"; +return scalar(@{$self->{'contents'}}); +} + +=head2 add_form(Webmin::Form) +Adds a form to be displayed on this page +=cut +sub add_form +{ +my ($self, $form) = @_; +push(@{$self->{'contents'}}, $form); +$form->set_page($self); +} + +=head2 add_separator() +Adds some kind of separation between parts of this page, like an
+=cut +sub add_separator +{ +my ($self, $message) = @_; +push(@{$self->{'contents'}}, "
"); +} + +=head2 add_button(cgi, label, description, [&hiddens], [before-button], + [after-button]) +Adds an action button associated with this page, typically for display at the end +=cut +sub add_button +{ +my ($self, $cgi, $label, $desc, $hiddens, $before, $after) = @_; +push(@{$self->{'buttons'}}, [ $cgi, $label, $desc, join(" ", @$hiddens), + $before, $after ]); +} + +=head2 add_tabs(Webmin::Tags) +Tells the page to display the given set of tabs at the top +=cut +sub add_tabs +{ +my ($self, $tabs) = @_; +$self->{'tabs'} = $tabs; +} + +=head2 add_dynamic(Webmin::DynamicText|Webmin::DynamicProgress) +Adds an object that is dynamically generated, such as a text box or progress bar. +=cut +sub add_dynamic +{ +my ($self, $dyn) = @_; +push(@{$self->{'contents'}}, $dyn); +$dyn->set_page($self); +} + +sub set_subheading +{ +my ($self, $subheading) = @_; +$self->{'subheading'} = $subheading; +} + +sub set_title +{ +my ($self, $title) = @_; +$self->{'title'} = $title; +} + +sub set_help +{ +my ($self, $help) = @_; +$self->{'help'} = $help; +} + +sub set_config +{ +my ($self, $config) = @_; +$self->{'config'} = $config; +} + +sub set_index +{ +my ($self, $index) = @_; +$self->{'index'} = $index; +} + +sub set_webmin +{ +my ($self, $webmin) = @_; +$self->{'webmin'} = $webmin; +} + +sub set_right +{ +my ($self, $right) = @_; +$self->{'right'} = $right; +} + +sub set_header +{ +my ($self, $header) = @_; +$self->{'header'} = $header; +} + +sub set_body +{ +my ($self, $body) = @_; +$self->{'body'} = $body; +} + +sub set_below +{ +my ($self, $below) = @_; +$self->{'below'} = $below; +} + +sub set_unbuffered +{ +my ($self, $unbuffered) = @_; +$self->{'unbuffered'} = $unbuffered; +} + +=head2 set_popup(popup?) +If set to 1, then this is a popup window +=cut +sub set_popup +{ +my ($self, $popup) = @_; +$self->{'popup'} = $popup; +} + +=head2 get_myurl() +Returns the path part of the URL for this page, like /foo/bar.cgi +=cut +sub get_myurl +{ +my @args; +if ($ENV{'QUERY_STRING'} && $ENV{'REQUEST_METHOD'} ne 'POST') { + my %in; + &ReadParse(\%in); + foreach my $i (keys %in) { + if ($i !~ /^ui_/) { + foreach my $v (split(/\0/, $in{$i})) { + push(@args, &urlize($i)."=". + &urlize($v)); + } + } + } + } +return @args ? $ENV{'SCRIPT_NAME'}."?".join("&", @args) + : $ENV{'SCRIPT_NAME'}; +} + +=head2 set_refresh(seconds) +Sets the number of seconds between automatic page refreshes +=cut +sub set_refresh +{ +my ($self, $refresh) = @_; +$self->{'refresh'} = $refresh; +} + +sub get_refresh +{ +my ($self) = @_; +return $self->{'refresh'}; +} + +=head2 add_onload(code) +Adds some Javascript code for inclusion in the onLoad tag +=cut +sub add_onload +{ +my ($self, $code) = @_; +push(@{$self->{'onloads'}}, $code); +} + +=head2 add_script(code) +Adds some Javascript code for putting in the section +=cut +sub add_script +{ +my ($self, $script) = @_; +push(@{$self->{'scripts'}}, $script); +} + +sub pre_content +{ +my ($self) = @_; +return undef; +} + +sub post_content +{ +my ($self) = @_; +return undef; +} + +=head2 set_errormsg(message) +Sets an error message to be displayed instead of the page contents +=cut +sub set_errormsg +{ +my ($self, $errormsg) = @_; +$self->{'errormsg'} = $errormsg; +} + +sub get_errormsg_html +{ +my ($self) = @_; +return $self->{'errormsg'}."

\n"; +} + +1; + diff --git a/Webmin/Password.pm b/Webmin/Password.pm new file mode 100644 index 000000000..e3d57310e --- /dev/null +++ b/Webmin/Password.pm @@ -0,0 +1,32 @@ +package Webmin::Password; +@ISA = ( "Webmin::Textbox" ); +use Webmin::Textbox; +use WebminCore; + +=head2 new Webmin::Password(name, value, [size]) +Create a new text input field, for a password +=cut +sub new +{ +if (defined(&Webmin::Theme::Password::new)) { + return new Webmin::Theme::Password(@_[1..$#_]); + } +my ($self, $name, $value, $size) = @_; +$self = new Webmin::Textbox($name, $value, $size); +bless($self); +return $self; +} + +=head2 html() +Returns the HTML for this password input +=cut +sub html +{ +my ($self) = @_; +return &ui_password($self->get_name(), $self->get_value(), + $self->{'size'}, + $self->{'$disabled'}); +} + + + diff --git a/Webmin/PlainText.pm b/Webmin/PlainText.pm new file mode 100644 index 000000000..43c5ae61f --- /dev/null +++ b/Webmin/PlainText.pm @@ -0,0 +1,97 @@ +package Webmin::PlainText; +use WebminCore; + +=head2 new Webmin::PlainText(text, columns) +Displays a block of plain fixed-width text, within a page or form. +=cut +sub new +{ +if (defined(&Webmin::Theme::PlainText::new) && + caller() !~ /Webmin::Theme::PlainText/) { + return new Webmin::Theme::PlainText(@_[1..$#_]); + } +my ($self, $text, $columns) = @_; +$self = { 'columns' => 80 }; +bless($self); +$self->set_text($text); +$self->set_columns($columns) if (defined($columns)); +return $self; +} + +=head2 html() +=cut +sub html +{ +my ($self) = @_; +my $rv; +$rv .= "
";
+foreach my $l (&wrap_lines($self->get_text(), $self->get_columns())) {
+	if (length($l) < $self->get_columns()) {
+		$l .= (" " x $self->get_columns() - length($l));
+		}
+	$rv .= &html_escape($l)."\n";
+	}
+if (!$self->get_text()) {
+	print (" " x $self->get_columns()),"\n";
+	}
+$rv .= "
\n"; +return $rv; +} + +sub set_text +{ +my ($self, $text) = @_; +$self->{'text'} = $text; +} + +sub get_text +{ +my ($self) = @_; +return $self->{'text'}; +} + +sub set_columns +{ +my ($self, $columns) = @_; +$self->{'columns'} = $columns; +} + +sub get_columns +{ +my ($self) = @_; +return $self->{'columns'}; +} + +# wrap_lines(text, width) +# Given a multi-line string, return an array of lines wrapped to +# the given width +sub wrap_lines +{ +local @rv; +local $w = $_[1]; +foreach $rest (split(/\n/, $_[0])) { + if ($rest =~ /\S/) { + while($rest =~ /^(.{1,$w}\S*)\s*([\0-\377]*)$/) { + push(@rv, $1); + $rest = $2; + } + } + else { + # Empty line .. keep as it is + push(@rv, $rest); + } + } +return @rv; +} + +=head2 set_page(Webmin::Page) +Called when this form is added to a page +=cut +sub set_page +{ +my ($self, $page) = @_; +$self->{'page'} = $page; +} + +1; + diff --git a/Webmin/Properties.pm b/Webmin/Properties.pm new file mode 100644 index 000000000..5f084b997 --- /dev/null +++ b/Webmin/Properties.pm @@ -0,0 +1,132 @@ +package Webmin::Properties; +use WebminCore; + +=head2 new Webmin::Properties([heading], [columns], [width]) +Creates a read-only properties list +=cut +sub new +{ +if (defined(&Webmin::Theme::Properties::new) && + caller() !~ /Webmin::Theme::Properties/) { + return new Webmin::Theme::Properties(@_[1..$#_]); + } +my ($self, $heading, $columns, $width) = @_; +$self = { 'columns' => 2 }; +bless($self); +$self->set_heading($heading) if (defined($heading)); +$self->set_columns($columns) if (defined($columns)); +$self->set_width($width) if (defined($width)); +return $self; +} + +=head2 add_row(label, value, ...) +Adds one row to the properties table +=cut +sub add_row +{ +my ($self, @row) = @_; +push(@{$self->{'rows'}}, \@row); +} + +=head2 set_heading_row(head1, head2, ...) +Adds a row of headings +=cut +sub set_heading_row +{ +my ($self, @row) = @_; +$self->{'heading_row'} = \@row; +} + +=head2 html() +Returns the HTML for this properties list +=cut +sub html +{ +my ($self) = @_; +my $rv; +my $width = $self->get_width(); +$rv .= "\n"; +$rv .= "
\n"; +my $cols = $self->get_columns(); +if ($self->get_heading()) { + $rv .= "\n"; + } +if ($self->{'heading_row'}) { + $rv .= "\n"; + foreach my $r (@{$self->{'heading_row'}}) { + $rv .= "\n"; + } + $rv .= "\n"; + } +foreach my $r (@{$self->{'rows'}}) { + $rv .= "\n"; + $rv .= "\n"; + for(my $i=1; $i<@$r || $i<$cols; $i++) { + $rv .= "\n"; + } + $rv .= "\n"; + } +$rv .= "
". + $self->get_heading()."
$r
$r->[0]".(ref($r->[$i]) ? $r->[$i]->html() + : $r->[$i])."
\n"; +return $rv; +} + +=head2 set_width([number|number%]) +Sets the width of this section. Can be called with 100%, 500, or undef to use +the minimum possible width. +=cut +sub set_width +{ +my ($self, $width) = @_; +$self->{'width'} = $width; +} + +sub get_width +{ +my ($self) = @_; +return $self->{'width'}; +} + +=head2 set_columns(number) +Sets the number of columns in the properties table, including the title column +=cut +sub set_columns +{ +my ($self, $columns) = @_; +$self->{'columns'} = $columns; +} + +sub get_columns +{ +my ($self) = @_; +return $self->{'columns'}; +} + +=head2 set_heading(number) +Sets the heading to appear above the properties list +=cut +sub set_heading +{ +my ($self, $heading) = @_; +$self->{'heading'} = $heading; +} + +sub get_heading +{ +my ($self) = @_; +return $self->{'heading'}; +} + + +=head2 set_page(Webmin::Page) +Called when this form is added to a page +=cut +sub set_page +{ +my ($self, $page) = @_; +$self->{'page'} = $page; +} + +1; + diff --git a/Webmin/Radios.pm b/Webmin/Radios.pm new file mode 100644 index 000000000..fed61067c --- /dev/null +++ b/Webmin/Radios.pm @@ -0,0 +1,77 @@ +package Webmin::Radios; +use Webmin::Input; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::Radios(name, value, &options, [disabled]) +Create a list of radio buttons, of which one may be selected +=cut +sub new +{ +if (defined(&Webmin::Theme::Radios::new)) { + return new Webmin::Theme::Radios(@_[1..$#_]); + } +my ($self, $name, $value, $options, $disabled) = @_; +$self = { }; +bless($self); +$self->set_name($name); +$self->set_value($value); +$self->set_options($options); +$self->set_disabled($disabled); +return $self; +} + +=head2 add_option(name, [label]) +=cut +sub add_option +{ +my ($self, $name, $label) = @_; +push(@{$self->{'options'}}, [ $name, $label ]); +} + +=head2 html() +Returns the HTML for all the radio buttons, one after the other +=cut +sub html +{ +my ($self) = @_; +my $dis = $self->{'form'}->get_changefunc($self); +my $opts = $self->get_options(); +if ($dis) { + foreach my $o (@$opts) { + $o->[2] = "onClick='$dis'"; + } + } +return &ui_radio($self->get_name(), $self->get_value(), + $opts, $self->get_disabled()); +} + +=head2 one_html(number) +Returns the HTML for a single one of the radio buttons +=cut +sub one_html +{ +my ($self, $num) = @_; +my $opt = $self->{'options'}->[$num]; +my $dis = $self->{'form'}->get_changefunc($self); +return &ui_oneradio($self->get_name(), $opt->[0], + defined($opt->[1]) ? $opt->[1] : $opt->[0], + $self->get_value() eq $opt->[0], + $dis ? "onClick='$dis'" : undef, + $self->get_disabled()); +} + +sub set_options +{ +my ($self, $options) = @_; +$self->{'options'} = $options; +} + +sub get_options +{ +my ($self) = @_; +return $self->{'options'}; +} + +1; + diff --git a/Webmin/ResultPage.pm b/Webmin/ResultPage.pm new file mode 100644 index 000000000..127dae33f --- /dev/null +++ b/Webmin/ResultPage.pm @@ -0,0 +1,20 @@ +package Webmin::ResultPage; +use WebminCore; + +=head2 new Webmin::ResultPage(subheading, title, message, [help-name]) +Create a new page object for showing some success message. +=cut +sub new +{ +if (defined(&Webmin::Theme::ResultPage::new) && + caller() !~ /Webmin::Theme::ResultPage/) { + return new Webmin::Theme::ResultPage(@_[1..$#_]); + } +my ($self, $subheading, $title, $message, $help) = @_; +$self = new Webmin::Page($subheading, $title, $help); +$self->add_message("$message"); +return $self; +} + +1; + diff --git a/Webmin/Section.pm b/Webmin/Section.pm new file mode 100644 index 000000000..244d187f7 --- /dev/null +++ b/Webmin/Section.pm @@ -0,0 +1,172 @@ +package Webmin::Section; +use WebminCore; + +=head2 new Webmin::Section(header, [columns], [title], [width]) +Create a new form section, which has a header and contains some inputs +=cut +sub new +{ +if (defined(&Webmin::Theme::Section::new) && + caller() !~ /Webmin::Theme::Section/) { + return new Webmin::Theme::Section(@_[1..$#_]); + } +my ($self, $header, $columns, $title, $width) = @_; +$self = { 'columns' => 4 }; +bless($self); +$self->set_header($header); +$self->set_columns($columns) if (defined($columns)); +$self->set_title($title) if (defined($title)); +$self->set_width($width) if (defined($width)); +return $self; +} + +=head2 html() +Returns the HTML for this form section +=cut +sub html +{ +my ($self) = @_; +my $rv; +$rv .= &ui_table_start($self->{'header'}, + $self->{'width'} ? "width=$self->{'width'}" : undef, + $self->{'columns'}); +foreach my $i (@{$self->{'inputs'}}) { + if (is_input($i->[1])) { + my $errs; + my @errs = $self->{'form'}->field_errors($i->[1]->get_name()); + if (@errs) { + foreach my $e (@errs) { + $errs .= "
$e\n"; + } + } + $rv .= &ui_table_row($i->[0], $i->[1]->html().$errs, + $i->[2]); + } + else { + $rv .= &ui_table_row($i->[0], + ref($i->[1]) ? $i->[1]->html() : $i->[1], $i->[2]); + } + } +$rv .= &ui_table_end(); +return $rv; +} + +=head2 add_input(label, input, [columns]) +Adds some Webmin::Input object to this form section +=cut +sub add_input +{ +my ($self, $label, $input, $cols) = @_; +push(@{$self->{'inputs'}}, [ $label, $input, $cols ]); +$input->set_form($self->{'form'}); +} + +=head2 add_row(label, text, [columns]) +Adds a non-editable row to this form section +=cut +sub add_row +{ +my ($self, $label, $text, $cols) = @_; +push(@{$self->{'inputs'}}, [ $label, $text, $cols ]); +} + +=head2 add_separator() +Adds some kind of separator at this point in the section +=cut +sub add_separator +{ +my ($self) = @_; +push(@{$self->{'inputs'}}, [ undef, "


", $self->{'columns'} ]); +} + +sub set_header +{ +my ($self, $header) = @_; +$self->{'header'} = $header; +} + +sub set_columns +{ +my ($self, $columns) = @_; +$self->{'columns'} = $columns; +} + +sub set_title +{ +my ($self, $title) = @_; +$self->{'title'} = $title; +} + +=head2 set_width([number|number%]) +Sets the width of this section. Can be called with 100%, 500, or undef to use +the minimum possible width. +=cut +sub set_width +{ +my ($self, $width) = @_; +$self->{'width'} = $width; +} + +=head2 validate() +Validates all form inputs, based on the given CGI input hash. Returns a list +of errors, each of which is field name, error message and field label. +=cut +sub validate +{ +my ($self) = @_; +my @errs; +foreach my $i (@{$self->{'inputs'}}) { + if (is_input($i->[1])) { + foreach my $e ($i->[1]->validate()) { + push(@errs, [ $i->[1]->get_name(), $e, $i->[0] ]); + } + } + } +return @errs; +} + +=head2 get_value(input-name) +Returns the value of the input with the given name. +=cut +sub get_value +{ +my ($self, $name) = @_; +foreach my $i (@{$self->{'inputs'}}) { + if (is_input($i->[1]) && $i->[1]->get_name() eq $name) { + return $i->[1]->get_value(); + } + } +return undef; +} + +=head2 set_form(form) +Called by the Webmin::Form object when this section is added to it +=cut +sub set_form +{ +my ($self, $form) = @_; +$self->{'form'} = $form; +foreach my $i (@{$self->{'inputs'}}) { + if (is_input($i->[1])) { + $i->[1]->set_form($form); + } + } +} + +sub list_inputs +{ +my ($self) = @_; +return map { $_->[1] } grep { is_input($_->[1]) } @{$self->{'inputs'}}; +} + +=head2 is_input(object) +=cut +sub is_input +{ +my ($object) = @_; +return ref($object) && ref($object) =~ /::/ && + $object->isa("Webmin::Input"); +} + +1; + diff --git a/Webmin/Select.pm b/Webmin/Select.pm new file mode 100644 index 000000000..f1d5844ce --- /dev/null +++ b/Webmin/Select.pm @@ -0,0 +1,135 @@ +package Webmin::Select; +use Webmin::Input; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::Select(name, value|&values, &options, [multiple-size], + [add-missing], [disabled]) +Create a menu or multiple-selection field +=cut +sub new +{ +if (defined(&Webmin::Theme::Select::new)) { + return new Webmin::Theme::Select(@_[1..$#_]); + } +my ($self, $name, $value, $options, $size, $missing, $disabled) = @_; +$self = { 'size' => 1 }; +bless($self); +$self->set_name($name); +$self->set_value($value); +$self->set_options($options); +$self->set_size($size) if (defined($size)); +$self->set_missing($missing); +$self->set_disabled($disabled); +return $self; +} + +=head2 add_option(name, [label]) +=cut +sub add_option +{ +my ($self, $name, $label) = @_; +push(@{$self->{'options'}}, [ $name, $label ]); +} + +=head2 html() +Returns the HTML for this menu or multi-select input +=cut +sub html +{ +my ($self) = @_; +my $dis = $self->{'form'}->get_changefunc($self); +return &ui_select($self->get_name(), $self->get_value(), + $self->get_options(), + $self->get_size() > 1 ? $self->get_size() : undef, + $self->get_size() > 1 ? 1 : 0, + undef, + $self->get_disabled(), + $dis ? "onChange='$dis'" : undef). + ($self->get_size() > 1 ? + &ui_hidden("ui_exists_".$self->get_name(), 1) : ""); +} + +=head2 get_value() +For a multi-select field, returns an array ref of all values. For a menu, +return just the one value. +=cut +sub get_value +{ +my ($self) = @_; +my $in = $self->{'form'} ? $self->{'form'}->{'in'} : undef; +if ($in && (defined($in->{$self->{'name'}}) || + defined($in->{"ui_exists_".$self->{'name'}}))) { + if ($self->get_size() > 1) { + return [ split(/\0/, $in->{$self->{'name'}}) ]; + } + else { + return $in->{$self->{'name'}}; + } + } +elsif ($in && defined($in->{"ui_value_".$self->{'name'}})) { + if ($self->get_size() > 1) { + return [ split(/\0/, $in->{"ui_value_".$self->{'name'}}) ]; + } + else { + return $in->{"ui_value_".$self->{'name'}}; + } + } +else { + return $self->{'value'}; + } +} + +sub set_options +{ +my ($self, $options) = @_; +$self->{'options'} = $options; +} + +sub set_size +{ +my ($self, $size) = @_; +$self->{'size'} = $size; +} + +sub set_missing +{ +my ($self, $missing) = @_; +$self->{'missing'} = $missing; +} + +sub get_options +{ +my ($self) = @_; +return $self->{'options'}; +} + +sub get_size +{ +my ($self) = @_; +return $self->{'size'}; +} + +sub get_missing +{ +my ($self) = @_; +return $self->{'missing'}; +} + +=head2 validate() +Returns a list of error messages for this field +=cut +sub validate +{ +my ($self) = @_; +if ($self->{'size'} > 1) { + my $value = $self->get_value(); + if ($self->{'mandatory'} && !@$value) { + return ( $self->{'mandatorymsg'} || $text{'ui_mandatory'} ); + } + } +return ( ); +} + +1; + diff --git a/Webmin/Submit.pm b/Webmin/Submit.pm new file mode 100644 index 000000000..68f4520b3 --- /dev/null +++ b/Webmin/Submit.pm @@ -0,0 +1,41 @@ +package Webmin::Submit; +use Webmin::Input; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::Submit(label, [name], [disabled]) +Create a form submit button +=cut +sub new +{ +if (defined(&Webmin::Theme::Submit::new) && + caller() !~ /Webmin::Theme::Submit/) { + return new Webmin::Theme::Submit(@_[1..$#_]); + } +my ($self, $value, $name, $disabled) = @_; +$self = { }; +bless($self); +$self->set_value($value); +$self->set_name($name) if ($name); +$self->set_disabled($disabled) if ($disabled); +return $self; +} + +=head2 html() +Returns the HTML for this form submit button +=cut +sub html +{ +my ($self) = @_; +return &ui_submit($self->get_value(), $self->get_name(), + $self->get_disabled()); +} + +sub get_value +{ +my ($self) = @_; +return $self->{'value'}; +} + +1; + diff --git a/Webmin/Table.pm b/Webmin/Table.pm new file mode 100644 index 000000000..e35ef5738 --- /dev/null +++ b/Webmin/Table.pm @@ -0,0 +1,660 @@ +package Webmin::Table; +use Webmin::JavascriptButton; +use WebminCore; + +=head2 new Webmin::Table(&headings, [width], [name], [heading]) +Create a multi-column table, with support for sorting, paging and so on +=cut +sub new +{ +if (defined(&Webmin::Theme::Table::new) && + caller() !~ /Webmin::Theme::Table/) { + return new Webmin::Theme::Table(@_[1..$#_]); + } +my ($self, $headings, $width, $name, $heading) = @_; +$self = { 'sorter' => [ map { \&default_sorter } @$headings ] }; +bless($self); +$self->set_headings($headings); +$self->set_name($name) if (defined($name)); +$self->set_width($width) if (defined($width)); +$self->set_heading($heading) if (defined($heading)); +$self->set_all_sortable(1); +$self->set_paging(1); +return $self; +} + +=head2 add_row(&fields) +Adds a row to the table. Each element in the row can be either an input of some +kind, or a piece of text. +=cut +sub add_row +{ +my ($self, $fields) = @_; +push(@{$self->{'rows'}}, $fields); +} + +=head2 html() +Returns the HTML for this table. The actual ordering may depend upon sort headers +clicked by the user. The rows to display may be limited by the page size. +=cut +sub html +{ +my ($self) = @_; +my @srows = @{$self->{'rows'}}; +my $thisurl = $self->{'form'}->{'page'}->get_myurl(); +my $name = $self->get_name(); +my $rv; + +# Add the heading +if ($self->get_heading()) { + $rv .= &ui_subheading($self->get_heading())."\n"; + } + +my $sm = $self->get_searchmax(); +if (defined($sm) && @srows > $sm) { + # Too many rows to show .. add a search form. This will need to close + # the parent form, and then re-open it after the search form, as nested + # forms aren't allowed! + if ($self->get_searchmsg()) { + $rv .= $self->get_searchmsg()."
\n"; + } + + my $form = new Webmin::Form($thisurl, "get"); + $form->set_input($self->{'form'}->{'in'}); + my $section = new Webmin::Section(undef, 2); + $form->add_section($section); + + my $col = new Webmin::Select("ui_searchcol_".$name, undef); + my $i = 0; + foreach my $h (@{$self->get_headings()}) { + if ($self->{'sortable'}->[$i]) { + $col->add_option($i, $h); + } + $i++; + } + $section->add_input($text{'ui_searchcol'}, $col); + + my $for = new Webmin::Textbox("ui_searchfor_".$name, undef, 30); + $section->add_input($text{'ui_searchfor'}, $for); + + $rv .= $section->html(); + my $url = $self->make_url(undef, undef, undef, undef, 1); + my $jsb = new Webmin::JavascriptButton($text{'ui_searchok'}, + "window.location = '$url'+'&'+'ui_searchfor_${name}'+'='+escape(form.ui_searchfor_${name}.value)+'&'+'ui_searchcol_${name}'+'='+escape(form.ui_searchcol_${name}.selectedIndex)"); + $rv .= $jsb->html(); + $rv .= "
\n"; + + # Limit records to current search + if (defined($col->get_value())) { + my $sf = $for->get_value(); + @srows = grep { $_->[$col->get_value()] =~ /\Q$sf\E/i } @srows; + } + else { + @srows = ( ); + } + } + +# Prepare the selector +my $selc = $self->{'selectcolumn'}; +my $seli = $self->{'selectinput'}; +my %selmap; +if (defined($selc)) { + my $i = 0; + foreach my $r (@srows) { + $selmap{$r,$selc} = $seli->one_html($i); + $i++; + } + } + +# Sort the rows +my ($sortcol, $sortdir) = $self->get_sortcolumn(); +if (defined($sortcol)) { + my $func = $self->{'sorter'}->[$sortcol]; + @srows = sort { my $so = &$func($a->[$sortcol], $b->[$sortcol], $sortcol); + $sortdir ? -$so : $so } @srows; + } + +# Build the td attributes +my @tds = map { "valign=top" } @{$self->{'headings'}}; +if ($self->{'widths'}) { + my $i = 0; + foreach my $w (@{$self->{'widths'}}) { + $tds[$i++] .= " width=$w"; + } + } +if ($self->{'aligns'}) { + my $i = 0; + foreach my $a (@{$self->{'aligns'}}) { + $tds[$i++] .= " align=$a"; + } + } + +# Find the page we want +my $page = $self->get_pagepos(); +my ($start, $end, $origsize); +if ($self->get_paging() && $self->get_pagesize()) { + # Restrict view to rows within some page + $start = $self->get_pagesize()*$page; + $end = $self->get_pagesize()*($page+1) - 1; + if ($start >= @srows) { + # Gone off end! + $start = 0; + $end = $self->get_pagesize()-1; + } + if ($end >= @srows) { + # End is too far + $end = @srows-1; + } + $origsize = scalar(@srows); + @srows = @srows[$start..$end]; + } + +# Generate the headings, with sorters +$thisurl .= $thisurl =~ /\?/ ? "&" : "?"; +my @sheadings; +my $i = 0; +foreach my $h (@{$self->get_headings()}) { + if ($self->{'sortable'}->[$i]) { + # Column can be sorted! + my $hh = ""; + $hh .= "
$h "; + if (!defined($sortcol) || $sortcol != $i) { + # Not sorting on this column .. show grey button + my $url = $self->make_url($i, 0, undef, undef); + $hh .= "". + ""; + } + else { + # Sorting .. show button to switch mode + my $notsort = !$sortdir; + my $url = $self->make_url($i, $sortdir ? 0 : 1, undef, undef); + $hh .= "". + ""; + } + $hh .= "
"; + push(@sheadings, $hh); + } + else { + push(@sheadings, $h); + } + $i++; + } + +# Get any errors for inputs +my @errs = map { $_->get_errors() } $self->list_inputs(); +if (@errs) { + foreach my $e (@errs) { + $rv .= "$e
\n"; + } + } + +# Build links for top and bottom +my $links; +if (ref($seli) =~ /Checkboxes/) { + # Add select all/none links + my $formno = $self->{'form'}->get_formno(); + $links .= &select_all_link($seli->get_name(), $formno, + $text{'ui_selall'})."\n"; + $links .= &select_invert_link($seli->get_name(), $formno, + $text{'ui_selinv'})."\n"; + $links .= " \n"; + } +foreach my $l (@{$self->{'links'}}) { + $links .= "
$l->[1]\n"; + } +$links .= "
" if ($links); + +# Build list of inputs for bottom +my $inputs; +foreach my $i (@{$self->{'inputs'}}) { + $inputs .= $i->html()."\n"; + } +$inputs .= "
" if ($inputs); + +# Create the pager +if ($self->get_paging() && $origsize) { + my $lastpage = int(($origsize-1)/$self->get_pagesize()); + $rv .= "
"; + if ($page != 0) { + # Add start and left arrows + my $surl = $self->make_url(undef, undef, undef, 0); + $rv .= "\n"; + my $lurl = $self->make_url(undef, undef, undef, $page-1); + $rv .= "\n"; + } + else { + # Start and left are disabled + $rv .= "\n"; + $rv .= "\n"; + } + $rv .= &text('ui_paging', $start+1, $end+1, $origsize); + if ($end < $origsize-1) { + # Add right and end arrows + my $rurl = $self->make_url(undef, undef, undef, $page+1); + $rv .= "\n"; + my $eurl = $self->make_url(undef, undef, undef, $lastpage); + $rv .= "\n"; + } + else { + # Right and end are disabled + $rv .= "\n"; + $rv .= "\n"; + } + $rv .= "
\n"; + } + +# Create actual table +if (@srows) { + $rv .= $links; + $rv .= &ui_columns_start(\@sheadings, $self->{'width'}, 0, \@tds); + foreach my $r (@srows) { + my @row; + for(my $i=0; $i<@$r || $i<@sheadings; $i++) { + if (ref($r->[$i]) eq "ARRAY") { + my $j = $r->[$i]->[0] && + $r->[$i]->[0]->isa("Webmin::TableAction") + ? " | " : " "; + $row[$i] = $selmap{$r,$i}. + join($j, map { ref($_) ? $_->html() : $_ } + @{$r->[$i]}); + } + elsif (ref($r->[$i])) { + $row[$i] = $selmap{$r,$i}.$r->[$i]->html(); + } + else { + $row[$i] = $selmap{$r,$i}.$r->[$i]; + } + } + $rv .= &ui_columns_row(\@row, \@tds); + } + $rv .= &ui_columns_end(); + } +elsif ($self->{'emptymsg'}) { + $rv .= $self->{'emptymsg'}."

\n"; + } +$rv .= $links; +$rv .= $inputs; +return $rv; +} + +=head2 set_form(form) +Called by the Webmin::Form object when this table is added to it +=cut +sub set_form +{ +my ($self, $form) = @_; +$self->{'form'} = $form; +foreach my $i ($self->list_inputs()) { + $i->set_form($form); + } +} + +=head2 set_sorter(function, [column]) +Sets a function used for sorting fields. Will be called with two field values to +compare, and a column number. +=cut +sub set_sorter +{ +my ($self, $func, $col) = @_; +if (defined($col)) { + $self->{'sorter'}->[$col] = $func; + } +else { + $self->{'sorter'} = [ map { $func } @{$self->{'headings'}} ]; + } +} + +=head2 default_sorter(value1, value2, col) +=cut +sub default_sorter +{ +my ($value1, $value2, $col) = @_; +if (ref($value1) && $value1->isa("Webmin::TableAction")) { + $value1 = $value1->get_value(); + $value2 = $value2->get_value(); + } +return lc($value1) cmp lc($value2); +} + +=head2 numeric_sorter(value1, value2, col) +=cut +sub numeric_sorter +{ +my ($value1, $value2, $col) = @_; +return $value1 <=> $value2; +} + +=head2 set_sortable(column, sortable?) +Tells the table if some column should allow sorting or not. By default, all are. +=cut +sub set_sortable +{ +my ($self, $col, $sortable) = @_; +$self->{'sortable'}->[$col] = $sortable; +} + +=head2 set_all_sortable(sortable?) +Enabled or disables sorting for all columns +=cut +sub set_all_sortable +{ +my ($self, $sortable) = @_; +$self->{'sortable'} = [ map { $sortable } @{$self->{'headings'}} ]; +} + +=head2 get_sortcolumn() +Returns the column to sort on and the order (1 for descending), or undef for none +=cut +sub get_sortcolumn +{ +my ($self) = @_; +my $in = $self->{'form'} ? $self->{'form'}->{'in'} : undef; +my $name = $self->get_name(); +if ($in && defined($in->{"ui_sortcolumn_".$name})) { + return ( $in->{"ui_sortcolumn_".$name}, + $in->{"ui_sortdir_".$name} ); + } +else { + return ( $self->{'sortcolumn'}, $self->{'sortdir'} ); + } +} + +=head2 set_sortcolumn(num, descending?) +Sets the default column on which sorting will be done, unless overridden by +the user. +=cut +sub set_sortcolumn +{ +my ($self, $col, $desc) = @_; +$self->{'sortcolumn'} = $col; +$self->{'sortdir'} = $desc; +} + +=head2 get_paging() +Returns 1 if page-by-page display should be used +=cut +sub get_paging +{ +my ($self) = @_; +my $in = $self->{'form'} ? $self->{'form'}->{'in'} : undef; +my $name = $self->get_name(); +if ($in && defined($in->{"ui_paging_".$name})) { + return ( $in->{"ui_paging_".$name} ); + } +else { + return ( $self->{'paging'} ); + } +} + +=head2 set_paging(paging?) +Turns page-by-page display of the table on or off +=cut +sub set_paging +{ +my ($self, $paging) = @_; +$self->{'paging'} = $paging; +} + +sub set_name +{ +my ($self, $name) = @_; +$self->{'name'} = $name; +} + +=head2 get_name() +Returns the name for indentifying this table in HTML +=cut +sub get_name +{ +my ($self) = @_; +if (defined($self->{'name'})) { + return $self->{'name'}; + } +elsif ($self->{'form'}) { + my $secs = $self->{'form'}->{'sections'}; + for(my $i=0; $i<@$secs; $i++) { + return "table".$i if ($secs->[$i] eq $self); + } + } +return "table"; +} + +sub set_headings +{ +my ($self, $headings) = @_; +$self->{'headings'} = $headings; +} + +sub get_headings +{ +my ($self) = @_; +return $self->{'headings'}; +} + +=head2 set_selector(column, input) +Takes a Webmin::Checkboxes or Webmin::Radios object, and uses it to add checkboxes +in the specified column. +=cut +sub set_selector +{ +my ($self, $col, $input) = @_; +$self->{'selectcolumn'} = $col; +$self->{'selectinput'} = $input; +$input->set_form($form); +} + +=head2 get_selector() +Returns the UI element used for selecting rows +=cut +sub get_selector +{ +my ($self) = @_; +return wantarray ? ( $self->{'selectinput'}, $self->{'selectcolumn'} ) + : $self->{'selectinput'}; +} + +=head2 set_widths(&widths) +Given an array reference of widths (like 50 or 20%), uses them for the columns +in the table. +=cut +sub set_widths +{ +my ($self, $widths) = @_; +$self->{'widths'} = $widths; +} + +=head2 set_width([number|number%]) +Sets the width of this entire table. Can be called with 100%, 500 or undef to use +the minimum possible width. +=cut +sub set_width +{ +my ($self, $width) = @_; +$self->{'width'} = $width; +} + +=head2 set_aligns(&aligns) +Given an array reference of horizontal alignments (like left or right), uses them +for the columns in the table. +=cut +sub set_aligns +{ +my ($self, $aligns) = @_; +$self->{'aligns'} = $aligns; +} + +=head2 validate() +Validates all inputs, and returns a list of error messages +=cut +sub validate +{ +my ($self) = @_; +my $seli = $self->{'selectinput'}; +my @errs; +if ($seli) { + push(@errs, map { [ $seli->get_name(), $_ ] } $seli->validate()); + } +foreach my $i ($self->list_inputs()) { + foreach my $e ($i->validate()) { + push(@errs, [ $i->get_name(), $e ]); + } + } +return @errs; +} + +=head2 get_value(input-name) +Returns the value of the input with the given name. +=cut +sub get_value +{ +my ($self, $name) = @_; +if ($self->{'selectinput'} && $self->{'selectinput'}->get_name() eq $name) { + return $self->{'selectinput'}->get_value(); + } +foreach my $i ($self->list_inputs()) { + if ($i->get_name() eq $name) { + return $i->get_value(); + } + } +return undef; +} + +=head2 list_inputs() +Returns all inputs in all form sections +=cut +sub list_inputs +{ +my ($self) = @_; +my @rv = @{$self->{'inputs'}}; +push(@rv, $self->{'selectinput'}) if ($self->{'selectinput'}); +return @rv; +} + +=head2 set_searchmax(num, [message]) +Sets the maximum number of table rows to display before showing a search form +=cut +sub set_searchmax +{ +my ($self, $searchmax, $searchmsg) = @_; +$self->{'searchmax'} = $searchmax; +$self->{'searchmsg'} = $searchmsg; +} + +sub get_searchmax +{ +my ($self) = @_; +return $self->{'searchmax'}; +} + +sub get_searchmsg +{ +my ($self) = @_; +return $self->{'searchmsg'}; +} + +=head2 add_link(link, message) +Adds a link to the table, for example for adding a new entry +=cut +sub add_link +{ +my ($self, $link, $msg) = @_; +push(@{$self->{'links'}}, [ $link, $msg ]); +} + +=head2 add_input(input) +Adds some input to be displayed at the bottom of the table +=cut +sub add_input +{ +my ($self, $input) = @_; +push(@{$self->{'inputs'}}, $input); +$input->set_form($self->{'form'}); +} + +=head2 set_emptymsg(message) +Sets the message to display when the table is empty +=cut +sub set_emptymsg +{ +my ($self, $emptymsg) = @_; +$self->{'emptymsg'} = $emptymsg; +} + +=head2 set_heading(text) +Sets the heading text to appear above the table +=cut +sub set_heading +{ +my ($self, $heading) = @_; +$self->{'heading'} = $heading; +} + +sub get_heading +{ +my ($self) = @_; +return $self->{'heading'}; +} + +=head2 set_pagesize(pagesize) +Sets the size of a page. Set to 0 to turn off completely. +=cut +sub set_pagesize +{ +my ($self, $pagesize) = @_; +$self->{'pagesize'} = $pagesize; +} + +=head2 get_pagesize() +Returns the size of a page, or 0 if paging is turned off totally +=cut +sub get_pagesize +{ +my ($self) = @_; +return $self->{'pagesize'}; +} + +sub get_pagepos +{ +my ($self) = @_; +my $in = $self->{'form'} ? $self->{'form'}->{'in'} : undef; +my $name = $self->get_name(); +if ($in && defined($in->{"ui_pagepos_".$name})) { + return ( $in->{"ui_pagepos_".$name} ); + } +else { + return ( $self->{'pagepos'} ); + } +} + +=head2 make_url(sortcol, sortdir, paging, page, [no-searchargs], [no-pagearg]) +Returns a link to this table's page, with the defaults for the various state +fields overriden by the parameters (where defined) +=cut +sub make_url +{ +my ($self, $newsortcol, $newsortdir, $newpaging, $newpagepos, + $nosearch, $nopage) = @_; +my ($sortcol, $sortdir) = $self->get_sortcolumn(); +$sortcol = $newsortcol if (defined($newsortcol)); +$sortdir = $newsortdir if (defined($newsortdir)); +my $paging = $self->get_paging(); +$paging = $newpaging if (defined($newpaging)); +my $pagepos = $self->get_pagepos(); +$pagepos = $newpagepos if (defined($newpagepos)); + +my $thisurl = $self->{'form'}->{'page'}->get_myurl(); +my $name = $self->get_name(); +$thisurl .= $thisurl =~ /\?/ ? "&" : "?"; +my $in = $self->{'form'}->{'in'}; +return "${thisurl}ui_sortcolumn_${name}=$sortcol". + "&ui_sortdir_${name}=$sortdir". + "&ui_paging_${name}=$paging". + ($nopage ? "" : "&ui_pagepos_${name}=$pagepos"). + ($nosearch ? "" : "&ui_searchfor_${name}=". + &urlize($in->{"ui_searchfor_${name}"}). + "&ui_searchcol_${name}=". + &urlize($in->{"ui_searchcol_${name}"})); +} + +1; + diff --git a/Webmin/TableAction.pm b/Webmin/TableAction.pm new file mode 100644 index 000000000..8c9d4717a --- /dev/null +++ b/Webmin/TableAction.pm @@ -0,0 +1,92 @@ +package Webmin::TableAction; +use WebminCore; + +=head2 new Webmin::TableAction(cgi, label, &args, disabled) +An object of this class can be added to a table or properties object to create +a link or action button of some kind. +=cut +sub new +{ +if (defined(&Webmin::Theme::TableAction::new) && + caller() !~ /Webmin::Theme::TableAction/) { + return new Webmin::Theme::TableAction(@_[1..$#_]); + } +my ($self, $cgi, $value, $args, $disabled) = @_; +$self = { }; +bless($self); +$self->set_value($value); +$self->set_cgi($cgi); +$self->set_args($args) if (defined($args)); +$self->set_disabled($disabled) if (defined($disabled)); +return $self; +} + +sub html +{ +my ($self) = @_; +my $rv; +if ($self->get_disabled()) { + $rv .= "".$self->get_value().""; + } +else { + my $link = $self->get_cgi(); + my $i = 0; + foreach my $a (@{$self->get_args()}) { + $link .= ($i++ ? "&" : "?"); + $link .= &urlize($a->[0])."=".&urlize($a->[1]); + } + $rv .= "".$self->get_value().""; + } +return $rv; +} + +sub set_value +{ +my ($self, $value) = @_; +$self->{'value'} = $value; +} + +sub get_value +{ +my ($self) = @_; +return $self->{'value'}; +} + +sub set_cgi +{ +my ($self, $cgi) = @_; +$self->{'cgi'} = $cgi; +} + +sub get_cgi +{ +my ($self) = @_; +return $self->{'cgi'}; +} + +sub set_args +{ +my ($self, $args) = @_; +$self->{'args'} = $args; +} + +sub get_args +{ +my ($self) = @_; +return $self->{'args'}; +} + +sub set_disabled +{ +my ($self, $disabled) = @_; +$self->{'disabled'} = $disabled; +} + +sub get_disabled +{ +my ($self) = @_; +return $self->{'disabled'}; +} + +1; + diff --git a/Webmin/Tabs.pm b/Webmin/Tabs.pm new file mode 100644 index 000000000..01fb29a48 --- /dev/null +++ b/Webmin/Tabs.pm @@ -0,0 +1,142 @@ +package Webmin::Tabs; +use WebminCore; + +=head2 new Webmin::Tabs([&tabs]) +Displayed at the top of a page, to allow selection of various pages +=cut +sub new +{ +my ($self, $tabs) = @_; +if (defined(&Webmin::Theme::Tabs::new)) { + return new Webmin::Theme::Tabs(@_[1..$#_]); + } +$self = { 'tabs' => [ ], + 'tab' => 0 }; +bless($self); +$self->set_tabs($tabs) if (defined($tabs)); +return $self; +} + +=head2 add_tab(name, link) +=cut +sub add_tab +{ +my ($self, $name, $link) = @_; +push(@{$self->{'tabs'}}, [ $name, $link ]); +} + +=head2 html() +Returns the HTML for the top of the page +=cut +sub top_html +{ +my ($self) = @_; +my $rv; +$rv .= ""; +$rv .= "\n"; +$rv .= "
"; +$rv .= ""; +my $i = 0; +my ($high, $low) = ("#cccccc", "#9999ff"); +my ($lowlc, $lowrc) = ( "/images/lc1.gif", "/images/rc1.gif" ); +my ($highlc, $highrc) = ( "/images/lc2.gif", "/images/rc2.gif" ); +foreach my $t (@{$self->get_tabs()}) { + if ($i == $self->get_tab()) { + # This is the selected tab + $rv .= ""; + if ($self->get_link()) { + # Link + $rv .= ""; + } + else { + # Don't link + $rv .= ""; + } + $rv .= "\n"; + } + else { + # Not selected + $rv .= ""; + $rv .= ""; + $rv .= "\n"; + } + $i++; + if ($self->{'wrap'} && $i%$self->{'wrap'} == 0) { + # New row + $rv .= ""; + } + } +$rv .= "
". + "\"\" ". + "[1]>$t->[0]  $t->[0] ". + "\"\"". + "\"\" ". + "[1]>$t->[0] ". + "\"\"
\n"; +$rv .= "
\n"; +return $rv; +} + +=head2 bottom_html() +Returns the HTML for the bottom of the page +=cut +sub bottom_html +{ +my ($self) = @_; +my $rv = "
\n"; +return $rv; +} + +=head2 set_tab(number|link) +Sets the tab that is currently highlighted +=cut +sub set_tab +{ +my ($self, $tab) = @_; +if ($tab =~ /^\d+$/) { + $self->{'tab'} = $tab; + } +else { + for(my $i=0; $i<@{$self->{'tabs'}}; $i++) { + if ($self->{'tabs'}->[$i]->[1] eq $tab) { + $self->{'tab'} = $i; + } + } + } +} + +sub get_tab +{ +my ($self) = @_; +return $self->{'tab'}; +} + +=head2 set_link(link) +If called with a non-zero parameter, even the highlighted tab will be a link +=cut +sub set_link +{ +my ($self, $link) = @_; +$self->{'link'} = $link; +} + +sub get_link +{ +my ($self) = @_; +return $self->{'link'}; +} + +sub set_tabs +{ +my ($self, $tabs) = @_; +$self->{'tabs'} = $tabs; +} + +sub get_tabs +{ +my ($self) = @_; +return $self->{'tabs'}; +} + +1; + diff --git a/Webmin/Textarea.pm b/Webmin/Textarea.pm new file mode 100644 index 000000000..31af91856 --- /dev/null +++ b/Webmin/Textarea.pm @@ -0,0 +1,122 @@ +package Webmin::Textarea; +use Webmin::Input; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::Textarea(name, value, rows, cols, [wrap], [disabled]) +Create a new text box, with the given size +=cut +sub new +{ +if (defined(&Webmin::Theme::Textarea::new)) { + return new Webmin::Theme::Textarea(@_[1..$#_]); + } +my ($self, $name, $value, $rows, $cols, $wrap, $disabled) = @_; +$self = { }; +bless($self); +$self->set_name($name); +$self->set_value($value); +$self->set_rows($rows); +$self->set_cols($cols); +$self->set_disabled($disabled); +return $self; +} + +=head2 html() +Returns the HTML for this text area +=cut +sub html +{ +my ($self) = @_; +return &ui_textarea($self->get_name(), $self->get_value(), + $self->get_rows(), $self->get_cols(), + $self->get_wrap(), $self->get_disabled()); +} + +sub set_rows +{ +my ($self, $rows) = @_; +$self->{'rows'} = $rows; +} + +sub get_rows +{ +my ($self) = @_; +return $self->{'rows'}; +} + +sub set_cols +{ +my ($self, $cols) = @_; +$self->{'cols'} = $cols; +} + +sub get_cols +{ +my ($self) = @_; +return $self->{'cols'}; +} + +sub set_wrap +{ +my ($self, $wrap) = @_; +$self->{'wrap'} = $wrap; +} + +sub get_wrap +{ +my ($self) = @_; +return $self->{'wrap'}; +} + +sub set_validation_func +{ +my ($self, $func) = @_; +$self->{'validation_func'} = $func; +} + +=head2 set_validation_regexp(regexp, message) +=cut +sub set_validation_regexp +{ +my ($self, $regexp, $message) = @_; +$self->{'validation_regexp'} = $regexp; +$self->{'validation_message'} = $message; +} + +=head2 validate() +Returns a list of error messages for this field +=cut +sub validate +{ +my ($self) = @_; +my $value = $self->get_value(); +if ($self->{'mandatory'} && $value eq '') { + return ( $self->{'mandmesg'} || $text{'ui_mandatory'} ); + } +if ($self->{'validation_func'}) { + my $err = &{$self->{'validation_func'}}($value, $self->{'name'}, + $self->{'form'}); + return ( $err ) if ($err); + } +if ($self->{'validation_regexp'}) { + if ($value !~ /$self->{'validation_regexp'}/) { + return ( $self->{'validation_message'} ); + } + } +return ( ); +} + +=head2 get_value() +Returns the value, without any \r characters +=cut +sub get_value +{ +my ($self) = @_; +my $rv = Webmin::Input::get_value($self); +$rv =~ s/\r//g; +return $rv; +} + +1; + diff --git a/Webmin/Textbox.pm b/Webmin/Textbox.pm new file mode 100644 index 000000000..55c1fb26f --- /dev/null +++ b/Webmin/Textbox.pm @@ -0,0 +1,80 @@ +package Webmin::Textbox; +use Webmin::Input; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::Textbox(name, value, [size], [disabled]) +Create a new text input field +=cut +sub new +{ +if (defined(&Webmin::Theme::Textbox::new)) { + return new Webmin::Theme::Textbox(@_[1..$#_]); + } +my ($self, $name, $value, $size, $disabled) = @_; +$self = { 'size' => 30 }; +bless($self); +$self->{'name'} = $name; +$self->{'value'} = $value; +$self->{'size'} = $size if ($size); +$self->set_disabled($disabled) if (defined($disabled)); +return $self; +} + +=head2 html() +Returns the HTML for this text input +=cut +sub html +{ +my ($self) = @_; +return &ui_textbox($self->get_name(), $self->get_value(), + $self->{'size'}, + $self->{'$disabled'}); +} + +sub set_size +{ +my ($self, $size) = @_; +$self->{'size'} = $size; +} + +sub set_validation_func +{ +my ($self, $func) = @_; +$self->{'validation_func'} = $func; +} + +=head2 set_validation_regexp(regexp, message) +=cut +sub set_validation_regexp +{ +my ($self, $regexp, $message) = @_; +$self->{'validation_regexp'} = $regexp; +$self->{'validation_message'} = $message; +} + +=head2 validate() +Returns a list of error messages for this field +=cut +sub validate +{ +my ($self) = @_; +my $value = $self->get_value(); +if ($self->{'mandatory'} && $value eq '') { + return ( $self->{'mandmesg'} || $text{'ui_mandatory'} ); + } +if ($self->{'validation_func'}) { + my $err = &{$self->{'validation_func'}}($value, $self->{'name'}, + $self->{'form'}); + return ( $err ) if ($err); + } +if ($self->{'validation_regexp'}) { + if ($value !~ /$self->{'validation_regexp'}/) { + return ( $self->{'validation_message'} ); + } + } +return ( ); +} + +1; + diff --git a/Webmin/Time.pm b/Webmin/Time.pm new file mode 100644 index 000000000..b299b49ce --- /dev/null +++ b/Webmin/Time.pm @@ -0,0 +1,168 @@ +package Webmin::Time; +use Webmin::Input; +use Time::Local; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::Time(name, time, [disabled]) +Create a new field for selecting a time +=cut +sub new +{ +if (defined(&Webmin::Theme::Time::new)) { + return new Webmin::Theme::Time(@_[1..$#_]); + } +my ($self, $name, $value, $disabled) = @_; +bless($self = { }); +$self->set_name($name); +$self->set_value($value); +$self->set_disabled($disabled) if (defined($disabled)); +return $self; +} + +=head2 html() +Returns the HTML for the time chooser +=cut +sub html +{ +my ($self) = @_; +my $rv; +my $val = $self->get_value(); +my $hour = ($val/3600) % 24; +my $min = ($val/60) % 60; +my $sec = ($val/1) % 60; +my $name = $self->get_name(); +$rv .= &ui_textbox("hour_".$name, pad2($hour), 2,$self->get_disabled()).":". + &ui_textbox("min_".$name, pad2($min), 2, $self->get_disabled()).":". + &ui_textbox("sec_".$name, pad2($sec), 2, $self->get_disabled()); +return $rv; +} + +sub pad2 +{ +return $_[0] < 10 ? "0".$_[0] : $_[0]; +} + +sub set_value +{ +my ($self, $value) = @_; +$self->{'value'} = timegm(localtime($value)); +} + +=head2 get_value() +Returns the date as a Unix time number (for 1st jan 1970) +=cut +sub get_value +{ +my ($self) = @_; +my $in = $self->{'form'} ? $self->{'form'}->{'in'} : undef; +if ($in && defined($in->{"hour_".$self->{'name'}})) { + my $rv = $self->to_time($in); + return defined($rv) ? $rv : $self->{'value'}; + } +elsif ($in && defined($in->{"ui_value_".$self->{'name'}})) { + return $in->{"ui_value_".$self->{'name'}}; + } +else { + return $self->{'value'}; + } +} + +sub to_time +{ +my ($self, $in) = @_; +my $hour = $in->{"hour_".$self->{'name'}}; +return undef if ($hour !~ /^\d+$/ || $hour < 0 || $hour > 23); +my $min = $in->{"min_".$self->{'name'}}; +return undef if ($min !~ /^\d+$/ || $min < 0 || $min > 59); +my $sec = $in->{"sec_".$self->{'name'}}; +return undef if ($sec !~ /^\d+$/ || $sec < 0 || $sec > 59); +return $hour*60*60 + $min*60 + $sec; +} + +sub set_validation_func +{ +my ($self, $func) = @_; +$self->{'validation_func'} = $func; +} + +=head2 validate() +Ensures that the date is valid +=cut +sub validate +{ +my ($self) = @_; +my $tm = $self->to_time($self->{'form'}->{'in'}); +if (!defined($tm)) { + return ( $text{'ui_etime'} ); + } +if ($self->{'validation_func'}) { + my $err = &{$self->{'validation_func'}}($self->get_value(), + $self->{'name'}, + $self->{'form'}); + return ( $err ) if ($err); + } +return ( ); +} + +=head2 set_auto(auto?) +If set to 1, the time will be automatically incremented by Javascript +=cut +sub set_auto +{ +my ($self, $auto) = @_; +$self->{'auto'} = $auto; +if ($auto) { + # XXX incorrect!! + my $formno = $self->{'form'}->get_formno(); + $self->{'form'}->add_onload("F=[0]; timeInit(F); setTimeout(\"timeUpdate(F)\", 5000)"); + my $as = $autoscript; + $as =~ s/NAME/$self->{'name'}/g; + $self->{'form'}->add_script($as); + } +} + +$autoscript = <59 ){ + s -= 60; + m = parseInt(mins[i].value); + m= m ? m : 0; + m+=1; + if( m>59 ){ + m -= 60; + h = parseInt(hours[i].value); + h = h ? h : 0; + h+=1; + if( h>23 ){ + h -= 24; + } + hours[i].value = packNum(h); + } + mins[i].value = packNum(m); + } + secs[i].value = packNum(s); + } + setTimeout('timeUpdate(F)', 5000); +} +function packNum(t) { + return (t < 10 ? '0'+t : t); +} +EOF + +1; + diff --git a/Webmin/TitleList.pm b/Webmin/TitleList.pm new file mode 100644 index 000000000..f6409bc93 --- /dev/null +++ b/Webmin/TitleList.pm @@ -0,0 +1,101 @@ +package Webmin::TitleList; +use WebminCore; + +=head2 new Webmin::TitleList(title, &links, [alt-text]) +Generates a title with a list of links under it +=cut +sub new +{ +my ($self, $title, $links, $alt) = @_; +if (defined(&Webmin::Theme::TitleList::new)) { + return new Webmin::Theme::TitleList(@_[1..$#_]); + } +$self = { }; +bless($self); +$self->set_title($title); +$self->set_links($links); +$self->set_alt($alt) if (defined($alt)); +return $self; +} + +=head2 html() +Returns the list +=cut +sub html +{ +my ($self) = @_; +my $rv; +if (defined(&ui_subheading)) { + $rv .= &ui_subheading($self->get_title()); + } +else { + $rv .= "

".$self->get_title()."

\n"; + } +$rv .= "
\n"; +foreach my $l (@{$self->get_links()}) { + if ($l->[1]) { + $rv .= "$l->[0]
\n"; + } + else { + $rv .= $l->[0]."
\n"; + } + } +return $rv; +} + +sub set_title +{ +my ($self, $title) = @_; +$self->{'title'} = $title; +} + +sub get_title +{ +my ($self) = @_; +return $self->{'title'}; +} + +sub set_links +{ +my ($self, $links) = @_; +$self->{'links'} = $links; +} + +sub get_links +{ +my ($self) = @_; +return $self->{'links'}; +} + +sub set_alt +{ +my ($self, $alt) = @_; +$self->{'alt'} = $alt; +} + +sub get_alt +{ +my ($self) = @_; +return $self->{'alt'}; +} + +=head2 add_link(name, link) +Adds a link to be displayed in the list +=cut +sub add_link +{ +my ($self, $name, $link) = @_; +push(@{$self->{'links'}}, [ $name, $link ]); +} + +=head2 set_page(Webmin::Page) +Called when this menu is added to a page +=cut +sub set_page +{ +my ($self, $page) = @_; +$self->{'page'} = $page; +} + +1; + diff --git a/Webmin/Upload.pm b/Webmin/Upload.pm new file mode 100644 index 000000000..87c019d9c --- /dev/null +++ b/Webmin/Upload.pm @@ -0,0 +1,77 @@ +package Webmin::Upload; +use Webmin::Input; +use WebminCore; +@ISA = ( "Webmin::Input" ); + +=head2 new Webmin::Upload(name, [size]) +Create a new file upload field +=cut +sub new +{ +if (defined(&Webmin::Theme::Upload::new)) { + return new Webmin::Theme::Upload(@_[1..$#_]); + } +my ($self, $name, $size) = @_; +$self = { 'size' => 30 }; +bless($self); +$self->{'name'} = $name; +$self->{'size'} = $size if ($size); +return $self; +} + +=head2 html() +Returns the HTML for this text input +=cut +sub html +{ +my ($self) = @_; +return &ui_upload($self->get_name(), $self->{'size'}, + $self->{'$disabled'}); +} + +sub set_size +{ +my ($self, $size) = @_; +$self->{'size'} = $size; +} + +sub set_validation_func +{ +my ($self, $func) = @_; +$self->{'validation_func'} = $func; +} + +=head2 set_validation_regexp(regexp, message) +=cut +sub set_validation_regexp +{ +my ($self, $regexp, $message) = @_; +$self->{'validation_regexp'} = $regexp; +$self->{'validation_message'} = $message; +} + +=head2 validate() +Returns a list of error messages for this field +=cut +sub validate +{ +my ($self) = @_; +my $value = $self->get_value(); +if ($self->{'mandatory'} && $value eq '') { + return ( $self->{'mandmesg'} || $text{'ui_mandatory'} ); + } +if ($self->{'validation_func'}) { + my $err = &{$self->{'validation_func'}}($value, $self->{'name'}, + $self->{'in'}); + return ( $err ) if ($err); + } +if ($self->{'validation_regexp'}) { + if ($value !~ /$self->{'validation_regexp'}/) { + return ( $self->{'validation_message'} ); + } + } +return ( ); +} + +1; + diff --git a/Webmin/User.pm b/Webmin/User.pm new file mode 100644 index 000000000..d9b63c9ba --- /dev/null +++ b/Webmin/User.pm @@ -0,0 +1,57 @@ +package Webmin::User; +use Webmin::Textbox; +use WebminCore; +@ISA = ( "Webmin::Textbox" ); + +=head2 new Webmin::User(name, value, [multiple], [disabled]) +A text box for entering or selecting one or many Unix usernames +=cut +sub new +{ +if (defined(&Webmin::Theme::User::new)) { + return new Webmin::Theme::User(@_[1..$#_]); + } +my ($self, $name, $value, $multiple, $disabled) = @_; +$self = new Webmin::Textbox($name, $value, $multiple ? 40 : 15, $disabled); +bless($self); +$self->set_multiple($multiple); +return $self; +} + +=head2 html() +Returns the HTML for this user input +=cut +sub html +{ +my ($self) = @_; +my $rv = Webmin::Textbox::html($self); +my $name = $self->get_name(); +my $multiple = $self->get_multiple(); +local $w = $multiple ? 500 : 300; +$rv .= " \n"; +return $rv; +} + +sub set_multiple +{ +my ($self, $multiple) = @_; +$self->{'multiple'} = $multiple; +} + +sub get_multiple +{ +my ($self) = @_; +return $self->{'multiple'}; +} + +=head2 get_input_names() +Returns the actual names of all HTML elements that make up this input +=cut +sub get_input_names +{ +my ($self) = @_; +return ( $self->{'name'}, $self->{'name'}."_button" ); +} + +1; +