Guide to Maintain's Skinning Interface
Brad Morgan <morgabra@osuosl.org>
Michael Clay <claym@osuosl.org>
v1.0, 17 July, 2006
This document will outline the skinning interface in Maintain.
This document also contains a complete tutorial for creating a skin for Maintain, which will teach the user to change logos and color schemes, and if they want, completely change the base layout of the page.
It is highly recommended that you have a working knowlege of the following
before attempting to create a skin for Maintain.
Table of Contents
- Skins
- What is a "skin"?
- How did skinning come about?
- The Role of phpHtmlLib
- Creating a skin
- Structure of a skin
- What each file in your skin's directory does
- Making Your First Skin
- The includes.php File
- Images
- CSS (Cascading Style Sheets)
- In Review
- Advanced Skinning
- Overloading Overview
- Making a layout
- Overloading Tutorial
- The Big Picture
- Further Help
Skins
The Maintain skinning interface was designed to allow users to customize the look and feel of Maintain. This kind of functionality is beleive to be important,because no matter what kind of default interface is provided, it will never be perfect for everyone. The goal of skinning is to provide the means for users to tailor the look and feel of Maintain to their needs/likings.
What is a "skin"?
In Maintain, it was decided to make the definiton of a "skin" to be fairly broad. This means that a skin could be new images and logos, a new CSS style sheet, a new layout, or any combination of the three. The goal was to offer flexibility in design so that users can only change as much as they want/need to. This system now provides users a wide range of customization options.
How did skinning come about?
The idea of a skinning engine came about when the Maintain team started looking though the old htdoc and template system. It was almost frightnening to look at, very intimidating, and nearly impossible to follow. The original code weaved in and out of PHP and HTML in a rather chaotic fashion. It truly looked like it was just a jumble of text on the screen, or for those users with syntax highlighting,
a neon nightmare. If simply reading the code was a mess, changing it would be borderline impossible. So, we thought there there must be a better way.
The Role of phpHtmlLib
phpHtmlLib is a library for building HTML though the use of PHP objects. This interesting new approach meant that there would be no need to inline HTML code in the PHP, or for that matter, constatly echo things to the screen. These facts led to a few interesting ideas.
If there are actually HTML objects, (which there are), and there is no need to constantly echo, (which there isn't), then it should be possible to add things in any order and have it still look the desired way. This is acheived through the overloading of methods and instance variables. As a result, it is possible to make things appear at any part of the page.
The second idea was, since these are objects, that it would be possible to use this more so as a templating engine in and of itself. There really is no point in rewriting the same code over and over, it would be possible to make one object that is the page, and then just change the content appropriately.
When you meld these two ideas together, you get the skinning system of Maintain!
Creating a skin
Before starting anything, it is important to determine what level of customization you are going to need/want. There is no need to worry about extending layout classes and other more advanced things if you are happy with the layout as-is, and just want to change the images and define a new CSS. But first, some background about how all skins work.
Structure of a skin
All Maintain skins are located in the 'htdocs/skins/' folder. Each skin is completey self contained in a folder that matches the name of the skin. For example, if your skin was named 'MySkin', then it would reside in the folder 'myskin' in the 'htdoc/skins/' directory. Inside the 'myskin' directory will be an 'images' directory.
The contents of these directories are as follows:
htdocs/skins/myskin::
- style.css
- includes.php
- images.php
- images directory
htdocs/skins/myskin/images::
- This directory should contain all the icons and other images you want to use in your skin.
What each file in your skin's directory does
Remember that there are 4 items in your skin's directory.
- style.css
- includes.php
- images.php
- /images directory
The functions of each file are as follows. Don't worry about the specifics yet, just try to grasp the purpose of each file.
- style.css
- This is a Cascading Style Sheet file. Maintain looks to this file for information about where items on your page should be loosely located, and what colors should be used. If you are unfamiliar with how CSS works or what its capable of, I would suggest looking at 'www.w3.org/Style/CSS/'.
- includes.php
- This gives instruction to the skin about the layout of the page. If you just want to change colors and logos, this file requres very little attention. However, if you truly want to tap into the power of this skinning engine, you use this file to overload the default layout methods, allowing you to completely rearrange the location of information on the screen, or add or remove said information.
- images.php
- This file is technically not requred for your skin to run, however, I am going to strongly suggest it to enforce good object oriented procedures. This file should define constants for the path and filename of all the images in your /images directory. (i.e. All the images you want your skin to use)
- /images dir
- This directory should contain all the images that you want your skin to use. Furthermore, all the images here should have constants defined for them in the images.php file.
Making Your First Skin
It is always best to learn by doing, so lets make a simple skin.
First things first, you have to create a folder in the 'htdocs/skins/' directory with the name of the skin you are going to want to make. Just to keep things easy, we will call this folder 'myskin'.
As stated earlier, we need four files in our new skin's directory.
- style.css
- images.php
- includes.php
- /images directory
Once those files are in place, we can start telling the skin what we want it to do.
The includes.php File
Feel free to copy/paste this small piece of code into the includes.php file which should be located in the 'myskin' directory.
<?php
require_once(HTCLASS."/Skin.php");
class myskin extends Skin
{
}
?>
As you can see, this script defines a class, which should be the same name as your skin's folder. This class must extend class Skin, which is located in the 'class/htdocs/Skin.php' file.
In the preferences area of Maintain, you can select what skin you want to use "on the fly". If you look under "color template" and pull down the menu, you should see "myskin" as one of the options!
Go ahead and select "myskin" and then click the update button. This will make Maintain load the skin we just created, making it immediately visible. If you were starting with the default Maintain skin, notice not much has changed. If you were starting in another skin, it will look like the default Maintain skin. Although it doesn't look like much has changed, Maintain is now running 'myskin'.
'myskin' looks like the default maintain skin becuse myskin extends Skin, which contains all the default instructions for how the skin should be laid out. (And, consequently, is the reason why all the skins shipped with Maintain have the same basic layout, we just decided to change the colors and images)
In review, includes.php gives information to the skin about the LAYOUT of the page, Nothing else. By extending Skin, we are giving myskin all the information it needs to make a skin with Maintain's built in layout. If we want to edit this base layout, we must overload the proper functions, which will be described in detail later. For now, we are just going to stick with Maintain's default layout.
Notice the error at the top of the screen. This is in no way going to break Maintain, but if you examine the code where the error occurs, you will see that the culprit is that we have not set up our images correctly, which we will do next.
Images
Maintain allows you to decide which images that you use in your skin, from the main logo all the way to the smallest icon. The best way to tell Maintain about the images that you are going to use is the images.php file.
But first things first...
We need to come up with an icon/logo set for our new skin. Just for simplicity's sake, we will just use the default icon set that ships with Maintain. The default Maintain skin is no different than any other skin, and so that means that it has a directory in the htdocs/skins/ directory, and it also means that it has an images directory where all the images that it uses reside. So, navigate to htdocs/skins/maintain/images and copy all the images from that folder into htdocs/skins/myskin/images. Now, our folder has its own set of images to use.
Reload the Maintain page. Notice that there are no icons or images anywhere, and the error is still at the top of the screen. That is because we haven't told the skin where to find the images. That is where the images.php file comes in.
Feel free to copy the following code into your images.php file.
<?php
$i = 'images';
$images[IMG_LOGO] = "$i/maintain_logo.png";
$images[IMG_FAVICON] = "$i/favicon.png";
$images[IMG_HOME] = "$i/home.png";
$images[IMG_ADMIN] = "$i/admin.png";
$images[IMG_LOGOUT] = "$i/logout.png";
$images[IMG_SEARCH] = "$i/search.png";
$images[IMG_PREF] = "$i/prefs.png";
?>
If you save this file, (assuming you have images in your /images directory) and then relaod the Maintain page, you will see the main logo in the header, as well as all the icons in the sidebar.
As far as putting in your own images goes, there are a few ways to do things...
You could overload all functions that use images in includes.php, and replace the constants with hardcoded paths to your images. But if you did that, you would be selling your soul to satan.
The other two options are much, much nicer...
- Change your images to match the paths in images.php
- Change the paths in images.php to match your images
Either way is fine.
Also, for your referencing pleasure, I've put down the sizes of all the images in the default maintain skin, so if you want to keep the default layout, it would be wise to stay fairly close to the recommended sizes so everything will play nice.
- Main Logo = 255 X 62 pixels (Width isn't as big a deal as height)
- Small Logo = 64 X 64 (The icon on the login page is an example)
- Small Icons = 18 X 18 (The icons for the sidebar are an example)
So far our new skin should have a very functional layout, as well as the default images and icons. The next step of costomization will be applying our own CSS.
CSS (Cascading Style Sheets)
CSS allows you to style your weppage (aka Maintain) without embedding the instructions into the actual HTML. This works out great for us, because we use phphtmllib, so we don't really have access to raw html, and even if we did, it saves us from having to embed the styles into the code, which we all know makes changing it a real pain.
Anyway, I would suggest reading up on CSS at the link given at the beginning of this document if you don't know whats going on.
Feel free to copy and paste the following code.
/************** Standard Maintain Settings ****************/
body {
background: #EBEBEB;
font-family: sans-serif;
font-size: small;
}
a {
color: #3333CC;
font-size: small;
text-decoration: none;
font-family: sans-serif;
}
td {
vertical-align: top;
}
.form fieldset {
width: 70%;
margin-left: 40px;
}
/* things for non-css browsers only */
.hidden {
display: none;
}
.error {
color: #990000;
}
/************** General Page Layout ****************/
/***** The header block *******/
/* The entire header block */
.pageheader {
border-bottom: 2px solid black;
padding-bottom: 4px;
width: 100%;
overflow: auto;
}
/* The logo image itself */
#logo {
float: left;
margin-right: 2em;
}
/* The 'welcome to Maintain, you're logged in as:' section */
.headinfo {
float: left;
}
/* The Change Zone form */
.headform {
float: right;
}
/****** The content block *****/
.content-block {
padding: 10px;
}
.content-block .title {
font-weight: bold;
font-size: medium;
}
.content-block .form{
border-style: double;
padding: 5px;
}
/*** The dashboard wrapper ***/
.dashboard {
overflow: auto;
}
/** A widget within the dashboard **/
.dashboard .widget {
width: 70%;
vertical-align: top;
margin-bottom: 1em;
}
/***** Datalists ********/
.datalist_border {
border: solid #BABABA;
}
.datalist_data_cell {
background: #BBBBBB;
}
.datalist_data_cell_selected {
background: #BBBBBB;
}
.datalist_data_cell_odd {
background: #DDDDDD;
}
.datalist_data_cell_odd_selected {
background: #DDDDDD;
}
.displayobject {
background: #FFFFFF;
border: 1px solid;
}
.displayobject_title {
font-weight: bolder;
}
.displayobject .even {
background: #C0C0C0;
}
.displayobject .odd {
background: #D0D0D0;
}
/****** The left block *****/
/* The left block of the page */
.left-block {
width: 10%;
padding-right: 4px;
}
/**\**\* The footer **\****/
.footer {
margin-top: 2em;
width: 100%;
text-align: center;
margin-left: auto;
margin-right: auto;
}
/************** Widgets *****************/
/* Default Widget */
.widget {
border: 1px solid;
background: #DADADA;
}
/* The title for a widget */
.widget-title {
margin: 0px;
padding: 0px;
background: #B8B8B8;
font-weight: bolder;
}
/* one row in a widget */
.widget-item {
}
/*** Menu (a ul in disguise) ***/
#menu {
padding: 0;
margin: 0;
font-size: x-large;
border: 0px;
list-style-type: none;
}
#menu li:hover {
background-color: #A6A6A6;
}
#menu li a {
display: block;
color: #000000;
margin: 4px 2px;
}
/************** Action Menu *******************/
/*** ActionMenu (ul in disguise) ***/
#actionmenu {
float: left;
padding: 0;
margin: 0;
border: 0px;
list-style-type: none;
}
#actionmenu li {
float: left;
}
#actionmenu li a {
background-color: #ccc;
border: 1px solid #999;
display: block;
font-size: small;
margin: 0 2px;
padding: 0 1em;
color: #3333CC;
}
#actionmenu li a:hover {
border: 1px inset #999;
background: #A6A6A6;
}
/************** The Login Page ****************/
/* The login form itself */
.loginform {
background: #B8B8B8;
border: solid #666666;
width: 350px;
position: relative;
top: 70%;
left: 35%;
}
.loginform img {
padding: 5px 0px 0px 5px;
}
.header1{
color: #FFFFFF;
font-family: Verdana, Helvetica, sans-serif;
font-size: 16px;
font-weight: 600;
text-align: center;
}
After you save, and reload the page, you can see that 'myskin' is now a perfect image of the Maintain skin. This is good, because all we did was copy all of maintain's default skins resources and instructions into our own.
For a quick example, let's change one thing in our style.css to see how it works. Look at the very top of style.css for the following block.
a {
color: #3333CC;
font-size: small;
text-decoration: none;
font-family: sans-serif;
}
This block has all the instruction for everything on the page with an html 'a' tag. This means that every single link on the page will be affected by this block, unless we change it later on down the css sheet. (Hence, "cascading" style sheet) Because of this, we can make a single change in our css file, and it will globally affect all of Maintain. (Which is a huge time and headache saver) So lets change the 'a' block to look something like the code below.
a
{color: #CC0000;
font-size: large;
text-decoration: none;
font-family: sans-serif;
}
If you save this file and reload the page, you will see that all the links on the main page are bigger, and red. But notice that some links did not change colors. (Like on the sidebar) This is probably due to those links being defined with a different color later on down the css.
Anyway, here is a short description of some of the headers in our css file.
- Standard Maintain Settings
- Fairly self explanitory. For eample, changing the color in the 'a' block will change the color of everything with an 'a' tag in the HTML code, (i.e. the color of the links on the page)
- General Page Layout
- Although you cannot change the location of items on the page with css, you can 'float' things left or right. This is helpful because, like the main page is set up, there is a header, body, and footer. We can just add object to each part, and then in the css 'float' them to wherever we want them. Feel free to try and float things around and see what happens.
- Datalist
- Pretty much anything not on the main page, for example 'Show Hosts' is a datalist. This allows you to move stuff around and change the color of anything in Maintain.
- Widgets
- Widgets are the blocks of information you see on the main page. (For example, 'Manage Hosts')
In Review
In review...
- includes.php
- Gives the skin information about the layout of the page. It must include 'HTCLASS."/Skin.php"'. It also must have a class with the same name as the folder of the skin, and that class must extend 'Skin'.
- images.php
- Tells the skin where to look for its images. This file should contain the paths to each image in your images directory. Remember, do not change the name of the constants, only the paths.
- /images directory
- Contains all the images your skin will use, and each image should be pointed to by a constant in images.php.
- style.css
- Contains all the non-layout information of your skin. This will let you change colors, borders of objects, float things left or right, change fonts and linked-text colors, etc.
And remember, your skin will not work if you do not have all four items in your skin's folder, and if your skin's folder is not located in the 'htdocs/skins/'.
Advanced Skinning
Before breaking into this section, keep in mind the results you are trying to acheive. If your only need/desire is to change colors and icons, there is no reason to start overloading methods and editing widgets and other more advanced/difficult tasks. With that being said...
Overloading Overview
When creating a skin that is more than just images and/or a CSS file, the Skin class must be extended.The only methods that should be overloaded are 'getWidget()' and 'getLayout()'. Treat the other methods as if they are final. The 'getLayout()' method should return an instance of the desired Layout class, no doubt the custom one associated with this skin. The 'getWidget()' method should return an instance of the widget associated with this skin. If you do not overload these methods then Maintain's default classes will be used in their place.
Making a layout
There is a 'Layout' class already defined in Maintain that was designed to be extended. This class is an extension off of phpHtmlLibs's 'PageWidget' class. The fundamental difference is that 'Layout' has an extra method called'addContent()' (which Maintain expects to be there). This method is used to add the content from the various htdocs into the page that is made based off of the skin. This way, it is possible to make an instance of the layout without knowing the content ahead of time. If this method is not defined errors will arise!
In a class that extends 'Layout' it is necessary to overload the 'body_content()' method. It is highly recommended to review the phpHtmlLib examples, especially example number three. More specifically, review the class that extends 'PageWidget'. Remember, the 'Layout' class in Maintain is an extension of 'PageWidget', so the 'PageWidget' information is all still relevant here. The highlights from this example is that the method body content is used to add the various 'Container' objects to the page in a defined order.
When making the paths to images in the skin, it is highly recommended that the paths do not name the skin explicitly, but rather go off the current skin's name. This allows for people to easily swap out images and CSS as they may be located in a different skin folder.
Most important info from this section...
- Your skin must extend Skin.
- The only methods that should be overridden are getLayout() and getWidget()
Overloading Tutorial
If you reqire the actual position of objects be changed, then the best way to do that is overloading the proper methods our includes.php file. Overloading simply means writing your own functions that will take the place of the default functions.
So, hopefully you still have 'myskin' kicking around, because that will be the skin that we are going to change the layout of in this little tutorial.
So first things first, copy/paste or type the following code into your includes.php file.
<?php
require_once(HTCLASS."/Skin.php");
class myskin extends Skin
{
function getLayout()
{
return new mylayout("Maintain Test Skin", HTML);
}
}
class mylayout extends Layout
{
}
Now observe what it happening here. We know that myskin extends Skin, meaning that Skin contains all the information myskin needs to draw a skin. So by overloading the getLayout() in the Skin class with our own instructions, we can tell theskin now to do whatever we want. So getLayout is now no longer returning the default maintain skin, it now returns mylayout, which is the layout we defined. So, we have sucessfully overridden getLayout() in the Skin class with our own getLayout() function. Unfortunately, the layout that we told it to get is empty, and if you save this file and refresh the page, that will be confirmed, because nothing is visible.
So now lets add something to the page.
require_once(HTCLASS."/Skin.php");
class myskin extends Skin
{
function getLayout()
{
return new mylayout("Maintain Test Skin", HTML);
}
}
class mylayout extends Layout
{
function body_content()
{
$this->add(html_comment("MAIN BLOCK BEGIN"));
$this->add($this->main_block());
$this->add(html_comment("MAIN BLOCK END"));
}
function main_block()
{
$main = html_table();
$content = html_td("content-block");
$content->add($this->content);
$main->add_row($content);
return $main;
}
}
mylayout must extend the generic class Layout, and it must overload a function called body_content(). As far as how you want to add information to body_content() is entirely up to you. I would suggest breaking your page up into sections, and then adding information to each sections function, then just add the section funtions to body_content(). (Like the example above shows) But in the end, its your call, the only requrement is you must overload body_content().
That is the basic form for skinning Maintain. All that is left is adding more objects or information, and you can literally do anything to want to Maintain in respect to the limitations of PHP and phpHtmlLib. Your best friend in learning to skin will be to examine code that is already there to see how things were done in the default skin. (i.e. Skin.php in the class/htdocs/ directory)
The Big Picture
Now that we have the idea of what needs to be done to make a skin, it might be a good idea to review what is going on in a standard htdoc to see how this all plays nicely with eachother. Below is merely an example case, it is not a real htdoc, but is in the same format as a real htdoc.
<?php
require_once("includes.php");
session_start();
setup_user_environment();
$skin = Skin::Factory($user->skin);
$title = "This is a test title";
$content = container($title);
$table = html_table("100%",1);
$table->add_row("Test column1", "Test column2");
$content->add($table);
print $skin->display($content);
?>
The first line of the above code is just a place holder for some other include files. After that a sessions is started and the generic environment variables are initialized. After the formalities are taken care of, the meat of the page begins. This all starts with the instantiation of a 'Skin' object. This is achieved through a factory method, aptly named, 'Factory()'. This factory takes one argument, which is the current user's skin's name. The factory takes this name, and then sees if there is a subfolder in the skin's directory by that name. If there is, it checks for an 'includes.php' file. If that is found it instantiates a class based off that name and returns. Otherwise, it alters the name of the current skin and loads the images and/or css associated with the layoutless skin. If no such skin exists, it is assumed to load the Maintain built in default layout and skin. This way, Maintain is still usable.
In the next line of code we define a string. Here it was called '$title', but it very well might not be a title at all. To phpHtmlLib, it's all a string, how the layout decides to display it is its choice.
Now there is a string, but it needs to get to the layout somehow to be displayed. This is accomplished by wrapping it in a 'Container' object. This object is a fairly straight forward container object, it's essentially a wrapper for an array. The container function is just a shortcut for the constructor, it can take any number of arguements and they will be added to the container in order. In this case it is initialized with the '$title' string.
To add something else to the 'Container' object after the fact, there is a method named 'add()'. This, like the 'container()' function, takes any number of arguments which are then added sequentially. To put this to use a table is created, and then has a row added to it consisting of two columns. This table is then added to the 'Container' object.
After the 'Container' contains all the information that is desired, the next step is to make this page appear. The 'Skin' object has a method called 'display()'. This method takes one argument of any type, most preferably it is a 'Container' object however, or an extended class there of. This method returns a string which is the HTML code based off the skin that is being used. All that is left is to print that string in order to create the page.
To abstract the code a little more, the general structure of an htdoc looks like this:
<?php
session_start();
setup_user_environment();
$skin = Skin::Factory($user->skin);
$content = container();
print $skin->display($content);
?>
Though the layout itself will not be implementing any htdocs, just a layout to be used by the htdocs, it is sometimes helpful to see how it all ties in.
Further Help
If you need more help with skinning Maintain, it is reccomended that you
consult the following links.