Inside the MSC Theme
This chapter explains the inner workings of Webmin's default
theme, which makes use of almost all of the features available
to theme developers.
Theme design and graphics
This new theme has been the default in Webmin since 0.92, although
the old ‘classic' Webmin look and feel is still available. It
makes use of practically all theme-related features, such as
alternate graphics, CGI program replacement and a library that
replaces the standard header and footer functions. For these
reasons it is a good one to look at if you are planning on writing
your own theme.
The mscstyle3 directory that contains the theme has a sub-directory
for most of the standard modules, each of which contains an images/icons.gif
file. These override the corresponding standard module icons
that appear on Webmin's main menu. No other overriding images
exist though, such as for icons within modules, meaning that
the original images are still used.
The theme directory also contains an images subdirectory under
which all of the images used by the theme itself are located. The
heading that appears on every page is made up of numerous images,
such as those for the category icons, category title letters,
shaded background and logout button. Because they are specific
to the theme, most do not have any corresponding real image under
the top-level images directory to override.
Like all themes must, this one includes a theme.info file containing
its description that will appear on the Themes page in the Webmin
Configuration module. It also includes a config file, which
tells the Webmin API to read in theme.pl and use the functions
that it contains to replace the standard header and footer functions.
It also contains several lines starting with cs_ that set the
table background and heading color. Unlike other simpler themes,
the config file does not need to specify any alternate page text
or background colors, as these can be set directly by its theme_header
function.
The index.cgi program
Because most of the graphical customization done by this theme
occurs in the theme.pl script, its replacement index.cgi program
does not differ much from the standard index.cgi in the top-level
Webmin directory. The biggest difference is that it does not
output any special index page heading or image – instead, it just
gets the list of modules available to the current user with get_available_module_infos
and uses it to build a table of icons in the current category. Because
the theme's theme_header function also calls this function,
the list may already be in the global @msc_modules variable.
Theme CGI programs execute in the ‘original' directory instead
of the directory they are really in, thus the index.cgi program
in the MSC theme can use the line require ‘./web-lib.pl';.
It is possible to create quite a different theme just be replacing
index.cgi, without the need for a theme's function file. The
standard Caldera theme has an index.cgi script that actually
generates a frame set, in which the top frame displays categories
and modules, and the bottom shows actual pages within modules.
It is possible for a theme to include CGI programs that do not override
any real program, and are used only as part of the theme's user
interface – for example, the index_top.cgi program that the
Caldera theme uses to render the top frame. Again, such programs
are run in the corresponding real directory, not in the theme's
sub-directory.
The MSC theme's index.cgi and theme.pl scripts all make use of
%text and the text function to get messages to display to the user,
instead of hard-coding them into the Perl code. All of the messages
come from the appropriate file in Webmin's top-level lang directory.
If you are creating your own theme that overrides any CGI program
or function, the same thing should be done to take advantage of
the existing translations into many languages that Webmin already
includes.
The theme_header function
The theme_header function in theme.pl in the mscstyle3 directory
effectively replaces the standard header function that almost
all Webmin CGI programs call. Unlike the standard header, this
one produces HTML for a list of module category icons at the top
of most pages, allowing the user to easily switch to a different
category. As well, it outputs HTML for a link to www.webmin.com,
logout and feedback buttons, and the standard links like *Module
Index* and
Module Config. Finally, HTML is produced that puts
the entire rest of the page inside the white table box that you
can see on almost every page.
The table of categories is generated by calling get_available_module_infos,
checking to see which categories actually exist, reading the
file /etc/webmin/webmin.catnames to get alternate names,
then displaying an icon and name made up of letter images for each.
The theme has images for all of the standard categories, plus
a special question-mark image to be used if a non-standard or
user-defined category is found. Just generating a fixed table
of standard categories would not work, as it is possible that
the user only has access to modules in some of them.
Because the category titles may be in a different language that
uses characters outside of the standard English alphabet, this
theme includes images for every letter with ASCII codes between
32 and 255. Any other theme that uses letter images should do the
same, so that it will work in non-English languages as well. For
some languages (such as Chinese and Russian), it is impossible
to create an image for every single character, due to the thousands
that exist. The MSC theme checks the global variable $current_lang_info->{'titles'}
and if it is not set produces plain text category labels instead.
When Webmin is in session authentication mode (determined by
checking for the $main::session_id variable), a logout image
button is added to the top-right corner of every page. However,
if normal HTTP authentication is being used this is replaced
by a button for switching users, which links to a different CGI
program. The old Webmin theme only has these links on the main
menu. The code properly checks the $ENV{'SSL_USER'}, $ENV{'LOCAL_USER'}and
$ENV{'ANONYMOUS_USER'}environment variables, any of which
if set indicates that no logout or switch button should appear.
Similarly every page has a feedback button in the top-right corner
as well, unless $ENV{'ANONYMOUS_USER'}is set or the global
or per-user configuration indicates that feedback is not allowed.
This links to feedback_form.cgi with the current module name
as a parameter, so that any feedback sent it automatically associated
with the current module. This is a nice idea if you are writing
your own theme.
Below the row of category icons are several small tabs, for links
like
Module Index and
Module Config. The theme_header function
checks its parameters and $ENV variables to determine which
ones to show, just like the standard header function does. The
biggest difference is that no
Webmin Index link is ever produce,
as there is no need for it – the user can return to the module's category
by just clicking on the appropriate icon at the top of any page.
Below any tabs comes the page title, supplied to the theme_header
function as the first parameter. The MSC theme puts it in a small
tab above the page body using only text, unlike the old Webmin
theme which renders the title as a series of letter images.
Finally, the theme puts page content output by the CGI after header
is called into a large box, by producing HTML tags to start a table.
However, this is not done if the global variable $theme_no_table
is set – instead, the content will be just part of the page's body.
CGI programs that slowly generate progressive output should
set this variable, and themes that have their own custom theme_header
function should honor it if appropriate. Of course, if your theme
doesn't use this kind of table for layout then the variable can
be ignored.
The theme_footer function
This function is must simpler, and not very different to the standard
footer function from web-lib.pl. It just prints HTML to close
the table started by theme_header (unless $theme_no_table
is set), followed by links to previous pages as specified in the
parameters. Finally the required