Note:

If you want to create a new page for developers, you should create it on the Moodle Developer Resource site.

Creating a theme: Difference between revisions

From MoodleDocs
m (Text replacement - "<code (.*)>" to "<syntaxhighlight lang="$1">")
 
(91 intermediate revisions by 19 users not shown)
Line 1: Line 1:
Welcome to the exciting world of theme creation for Moodle 2.0.  
{{obsolete}}
{{Template:Themes}}{{Moodle 2.1}}{{Moodle 2.0}}This document describes how to create a theme for Moodle 2.0 and Moodle 2.1. ONLY. It assumes you have some understanding of how themes within Moodle work as well as a good understanding of HTML and CSS.


This document is a basic tutorial designed to instruct the reader as to how to go about creating their first theme. It is targeted at designers and assumes you have at least a little understanding of how themes within Moodle work as well as a good understanding of HTML and CSS.
For Themes in Moodle 2.2 onwards please read the tutorial about [https://docs.moodle.org/dev/Themes_2.2_how_to_clone_a_Moodle_2.2_theme How to clone a Moodle 2.2 theme].  


==Before your begin==
Your checklist before we begin:
# Make sure you have at least a little understanding about how themes work in Moodle 2.0
# Have a checked out or downloaded version of Moodle that is installed, operating and ready to go. You also of course need admin access to your Moodle site.
# Have some idea of what you would like your theme to look like... believe me it helps.
# Your favourite editor / IDE / development program ready to go.
# Make yourself comfortable, get a coffee, turn up the music, you will be here for a while!


For the purposes of this tutorial:
===Theme designer mode===
* The theme I will be creating is going to be called 'excitement'
* The software I will be working with is as follows:
** Firebox web browser ''plus'' Firebug extension
** Netbeans 6.8 (my preferred IDE)


I will be working with Firefox and more specifically the Firebug extension. If you don't know what either of these two are make sure you look into them before you start. I STRONGLY recommend either this combination or Google Chrome, it will make your life a lot easier.
Under normal operation Moodle does several things in the name of performance, one of these is to combine all of the CSS into one file, minimize it, cache it on the server, and then serve it. After the first request the cached version is served to greatly improve page performance.
 
What this means for you as a themer? When you make changes they will not be seen immediately. In fact you will need to tell Moodle to rebuild the cache that it is serving.
This isn't practical for designing themes of course so the '''theme designer mode''' was added. When enabled it tells Moodle not to combine or cache the CSS that gets delivered. This has the downside that page load times will take significantly longer, however you will see your changes immediately every time.
 
Theme designer mode may be enabled via ''Administration > Appearance > Themes > [[Theme settings]]''.
 
'''Warning''': Internet Explorer versions 6 and 7 have BIG problems when a site attempts to link to more than 31 stylesheets, in which case either a limited number or no styles get applied. Because Moodle sends up all of the CSS all of the time with theme designer mode turned on there is a very high chance you will get more than 31 stylesheets being included. This will, of course, cause major problems for Internet Explorer until theme designer mode is turned off.


==Getting started==
==Getting started==


The first thing you need to do is create the directories and files you will be using, the first thing to create is the actual directory for your theme. This should be the name of your theme, in my case its 'excitement'. The directory should be located within the theme directory of Moodle, ./moodle/theme/excitement/ will be the directory I create.
The first thing you need to do is create the directories and files you will be using, the first thing to create is the actual directory for your theme. This should be the name of your theme, in my case it's 'excitement'. The directory should be located within the theme directory of Moodle, ./moodle/theme/excitement/ will be the directory I create.


Now within that directory we can immediately create several files that we know we are going to need.  
Now within that directory we can immediately create several files that we know we are going to need.  
Why create them before I actually put content into them? because it's easier to explain this as one setup step, you can of course create them as you go if you prefer.


So the files that we want to create are:
So the files that we want to create are:
Line 34: Line 29:
; /layout/ : Our layout files will end up in this directory.
; /layout/ : Our layout files will end up in this directory.
; /layout/standard.php : This will be our one basic layout file.
; /layout/standard.php : This will be our one basic layout file.
; /lang/en/ : The file we put here will make our theme name show properly on the Theme Selector page. You need a few standard entries. Copy the one from the Standard theme and modify is easiest.


So after this setup step you should have a directory structure similar to what is shown below.
So after this setup step you should have a directory structure similar to what is shown below.
Line 41: Line 37:
==Configuring our theme==
==Configuring our theme==


The config.php is the single most important file in your theme and my no means the most complex. Without it the theme won't be seen, if it's wrong the theme won't work, we had better get it right.
Open config.php in your favourite editor and start by adding the opening PHP tags '''<nowiki><?php</nowiki>'''.


So open up your config.php in you favourite editor and start by adding the opening PHP tags '''<nowiki><?php</nowiki>'''. We don't need to add the closing tags but you can if it gives you piece of mind.
Now we need to add the settings:


Now we need to add the settings! The first setting we will add is the name,
<syntaxhighlight lang="php">
 
<code php>
$THEME->name = 'excitement';
$THEME->name = 'excitement';
</code>
</syntaxhighlight>
Very simply this tells the Moodle the name of your theme, and if you ever have several config.php files open this will be help you identify which one you are looking at.
Very simply this tells Moodle the name of your theme, and if you ever have several config.php files open this will help you identify which one you are looking at.


Next the parents for this theme.
Next, the parents for this theme.
<code php>
<syntaxhighlight lang="php">
$THEME->parents = array('base');
$THEME->parents = array('base');
</code>
</syntaxhighlight>
This tells Moodle that my new theme ''`excitement`' wants to extend the base theme.  
This tells Moodle that my new theme ''`excitement`' wants to extend the base theme.  


A theme can extend any number of themes which is fantastic for those who find a theme they like but want to make changes. Rather than creating an entirely new theme and coping all of the CSS or having to modify the theme you like you can simply create a new theme, extend the theme you like and just add the changes you want to your theme. This helps to keep everything clean and simple.
A theme can extend any number of themes. Rather than creating an entirely new theme and copying all of the CSS, you can simply create a new theme, extend the theme you like and just add the changes you want to your theme.  


Also worth noting is the purpose of the base theme, it provides us with a basic layout and just enough CSS to make everything fall into place. When you first start out creating themes I would recommend starting by extending the base theme.
Also worth noting is the purpose of the base theme: it provides us with a basic layout and just enough CSS to make everything fall into place.


Now we will tell Moodle about our stylesheets:
Now we will tell Moodle about our stylesheets:
<code php>
<syntaxhighlight lang="php">
$THEME->sheets = array('excitement');
$THEME->sheets = array('excitement');
</code>
</syntaxhighlight>
This line tells Moodle what stylesheets we would like to use for this theme. You'll notice that we don't need to add .css or tell Moodle that it is in the style directory, Moodle will assume those two things for you.


The final thing we need to add into our themes config.php file is the definition of the layouts for our theme:
The final thing we need to add into our theme's config.php file is the definition of the layouts for our theme:
<div style="height:400px;overflow:auto;">
(in the code below, replace 'general.php' with 'standard.php' because that is the file that is previously created)
<code php>
<syntaxhighlight lang="php">
$THEME->layouts = array(
$THEME->layouts = array(
    // Most backwards compatible layout without the blocks - this is the layout used by default
     'base' => array(
     'base' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'general.php',
         'regions' => array(),
         'regions' => array(),
     ),
     ),
    // Standard layout with blocks, this is recommended for most pages with general information
     'standard' => array(
     'standard' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'standard.php',
         'regions' => array('side-pre', 'side-post'),
         'regions' => array('side-pre', 'side-post'),
         'defaultregion' => 'side-post',
         'defaultregion' => 'side-pre',
     ),
     ),
    // Main course page
     'course' => array(
     'course' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'standard.php',
         'regions' => array('side-pre', 'side-post'),
         'regions' => array('side-pre', 'side-post'),
         'defaultregion' => 'side-post'
         'defaultregion' => 'side-pre',
        'options' => array('langmenu'=>true),
     ),
     ),
     'coursecategory' => array(
     'coursecategory' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'standard.php',
         'regions' => array('side-pre', 'side-post'),
         'regions' => array('side-pre', 'side-post'),
         'defaultregion' => 'side-post',
         'defaultregion' => 'side-pre',
     ),
     ),
    // part of course, typical for modules - default page layout if $cm specified in require_login()
     'incourse' => array(
     'incourse' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'standard.php',
         'regions' => array('side-pre', 'side-post'),
         'regions' => array('side-pre', 'side-post'),
         'defaultregion' => 'side-post',
         'defaultregion' => 'side-pre',
     ),
     ),
    // The site home page.
     'frontpage' => array(
     'frontpage' => array(
        'theme' => 'excitement',
         'file' => 'frontpage.php',
         'file' => 'standard.php',
         'regions' => array('side-pre', 'side-post'),
         'regions' => array('side-pre', 'side-post'),
         'defaultregion' => 'side-post',
         'defaultregion' => 'side-pre',
     ),
     ),
    // Server administration scripts.
     'admin' => array(
     'admin' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'standard.php',
         'regions' => array('side-pre'),
         'regions' => array('side-pre'),
         'defaultregion' => 'side-pre',
         'defaultregion' => 'side-pre',
     ),
     ),
    // My dashboard page
     'mydashboard' => array(
     'mydashboard' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'standard.php',
         'regions' => array('side-pre', 'side-post'),
         'regions' => array('side-pre', 'side-post'),
         'defaultregion' => 'side-post',
         'defaultregion' => 'side-pre',
         'options' => array('langmenu'=>true),
         'options' => array('langmenu'=>true),
     ),
     ),
    // My public page
     'mypublic' => array(
     'mypublic' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'standard.php',
         'regions' => array('side-pre', 'side-post'),
         'regions' => array('side-pre', 'side-post'),
         'defaultregion' => 'side-post',
         'defaultregion' => 'side-pre',
     ),
     ),
     'login' => array(
     'login' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'standard.php',
         'regions' => array(),
         'regions' => array(),
         'options' => array('langmenu'=>true),
         'options' => array('langmenu'=>true),
     ),
     ),
    // Pages that appear in pop-up windows - no navigation, no blocks, no header.
     'popup' => array(
     'popup' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'standard.php',
         'regions' => array(),
         'regions' => array(),
         'options' => array('nofooter'=>true),
         'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nologininfo'=>true, 'nocourseheaderfooter'=>true),
     ),
     ),
    // No blocks and minimal footer - used for legacy frame layouts only!
     'frametop' => array(
     'frametop' => array(
         'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'standard.php',
        'regions' => array(),
        'options' => array('nofooter'=>true, 'nocoursefooter'=>true),
    ),
    // Embeded pages, like iframe/object embeded in moodleform - it needs as much space as possible
    'embedded' => array(
         'file' => 'embedded.php',
         'regions' => array(),
         'regions' => array(),
         'options' => array('nofooter'=>true),
         'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nocourseheaderfooter'=>true),
     ),
     ),
    // Used during upgrade and install, and for the 'This site is undergoing maintenance' message.
    // This must not have any blocks, and it is good idea if it does not have links to
    // other places - for example there should not be a home link in the footer...
     'maintenance' => array(
     'maintenance' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'standard.php',
         'regions' => array(),
         'regions' => array(),
         'options' => array('nofooter'=>true, 'nonavbar'=>true),
         'options' => array('noblocks'=>true, 'nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nocourseheaderfooter'=>true),
     ),
     ),
);
    // Should display the content and basic headers only.
</code>
     'print' => array(
</div>
Now that looks pretty scary but if you look closely at it you'll see most of it is repetition.<br />
What you are looking at is the different layouts for our theme.<br />Why are there so many? because that is how many there are in Moodle, there is one for every general type of page. With my ''`excitement`'' theme I have chosen to use my own layout, unless there was specific reason to do so normally you would not need to create your own layouts, you could of course extend the base theme and use its layouts meaning you would only have to write CSS to achieve your desired look. However that is not helpful for a tutorial such as this where the aim is to introduce you to much of the theme making process as possible.
 
For each layout above you will notice the following five things are being set:
; theme : This is the name of the theme in which to look for the layout file.
; file : This is the name of the layout file we want to use, it should always be located in the above themes layout directory. For us this is of course standard.php as we only have one layout file.
; regions : This is an array of block regions that our theme has. Each entry in here can be used to put blocks in when that layout is being used.
; defaultregion : If a layout has regions it should have a default region, this is where blocks get put when first added.
; options : These are special settings, anything that gets put into the options array is available later on when we are writing our layout file.
 
So what does it look like all up:
<div style="height:400px;overflow:auto;">
<code php>
<?php
 
/**
* Excitement theme config
*
* This is the configuration file for my exciting theme
*/
 
$THEME->name = 'excitement';
$THEME->parents = array('base');
$THEME->sheets = array('excitement');
$THEME->layouts = array(
     'base' => array(
        'theme' => 'excitement',
         'file' => 'general.php',
         'file' => 'general.php',
         'regions' => array(),
         'regions' => array(),
        'options' => array('noblocks'=>true, 'nofooter'=>true, 'nonavbar'=>false, 'nocustommenu'=>true, 'nocourseheaderfooter'=>true),
     ),
     ),
     'standard' => array(
    // The pagelayout used when a redirection is occuring.
        'theme' => 'excitement',
     'redirect' => array(
         'file' => 'standard.php',
         'file' => 'embedded.php',
         'regions' => array('side-pre', 'side-post'),
         'regions' => array(),
         'defaultregion' => 'side-post',
         'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nocourseheaderfooter'=>true),
    ),
    'course' => array(
        'theme' => 'excitement',
        'file' => 'standard.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-post'
     ),
     ),
     'coursecategory' => array(
     // The pagelayout used for reports.
        'theme' => 'excitement',
     'report' => array(
        'file' => 'standard.php',
         'file' => 'report.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-post',
    ),
     'incourse' => array(
        'theme' => 'excitement',
        'file' => 'standard.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-post',
    ),
    'frontpage' => array(
        'theme' => 'excitement',
        'file' => 'standard.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-post',
    ),
    'admin' => array(
        'theme' => 'excitement',
         'file' => 'standard.php',
         'regions' => array('side-pre'),
         'regions' => array('side-pre'),
         'defaultregion' => 'side-pre',
         'defaultregion' => 'side-pre',
     ),
     ),
     'mydashboard' => array(
    // The pagelayout used for safebrowser and securewindow.
        'theme' => 'excitement',
     'secure' => array(
         'file' => 'standard.php',
         'file' => 'general.php',
         'regions' => array('side-pre', 'side-post'),
         'regions' => array('side-pre', 'side-post'),
         'defaultregion' => 'side-post',
         'defaultregion' => 'side-pre',
        'options' => array('langmenu'=>true),
         'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nologinlinks'=>true, 'nocourseheaderfooter'=>true),
    ),
    'mypublic' => array(
        'theme' => 'excitement',
        'file' => 'standard.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-post',
    ),
    'login' => array(
        'theme' => 'excitement',
        'file' => 'standard.php',
        'regions' => array(),
         'options' => array('langmenu'=>true),
    ),
    'popup' => array(
        'theme' => 'excitement',
        'file' => 'standard.php',
        'regions' => array(),
        'options' => array('nofooter'=>true),
    ),
    'frametop' => array(
        'theme' => 'excitement',
        'file' => 'standard.php',
        'regions' => array(),
        'options' => array('nofooter'=>true),
    ),
    'maintenance' => array(
        'theme' => 'excitement',
        'file' => 'standard.php',
        'regions' => array(),
        'options' => array('nofooter'=>true, 'nonavbar'=>true),
     ),
     ),
);
);
</code>
</div>


There are of course several more settings that can be defined in the config.php file however this is all that my theme requires. For a full list of settings and examples of how they are used please see [[Themes 2.0|Themes 2.0]] or have a look at the config.php files for existing themes.
 
/** List of javascript files that need to be included on each page */
$THEME->javascripts = array();
$THEME->javascripts_footer = array();
 
</syntaxhighlight>
 
What you are looking at is the different layouts for our theme. Why are there so many? Because, that is how many there are in Moodle. There is one for every general type of page. With my ''`excitement`'' theme I have chosen to use my own layout. Unless there was a specific reason to do so, normally you would not need to create your own layouts, you could extend the base theme, and use its layouts, meaning you would only have to write CSS to achieve your desired look.
 
For each layout above, you will notice the following four things are being set:
; file : This is the name of the layout file we want to use, it should always be located in the above themes layout directory. For us this is of course standard.php as we only have one layout file.
; regions : This is an array of block regions that our theme has. Each entry in here can be used to put blocks in when that layout is being used.
; defaultregion : If a layout has regions it should have a default region, this is where blocks get put when first added. It is also where the "add block" block is shown when editing is on.
; options : These are special settings, anything that gets put into the options array is available later on when we are writing our layout file.
 
There are additional settings that can be defined in the config.php file - see [[Themes|Themes]] for the full list.
 
==Configuring the language file==
Open theme_boost.php file from '''boost/lang/en/theme_boost.php'''
 
Save it as '''excitement/lang/en/theme_excitement.php'''.
 
Change
<syntaxhighlight lang="php">
$string['pluginname'] = 'Boost';
</syntaxhighlight>
to
<syntaxhighlight lang="php">
$string['pluginname'] = 'Excitement';
</syntaxhighlight>
 
After making these changes and perhaps clearing theme caches and/or purging all caches, the new theme name should now show properly in the Theme Selector: ''Site Administration > Appearance > Themes > Theme Selector''
 
You can also edit the theme description:
 
<syntaxhighlight lang="php">
$string['choosereadme'] = 'Write your theme description here.';
</syntaxhighlight>
 
You need to leave the following two lines in place (you can change the wording if you need to) to avoid notices when editing blocks etc.:
 
<syntaxhighlight lang="php">
$string['region-side-pre'] = 'Right';
</syntaxhighlight>


==Writing the layout file==
==Writing the layout file==
For my excitement theme I have chosen to use one layout file, now this has its advantages and disadvantages.


The downside to using just one layout file is that I have to make the layout file do everything I want which means I need to make use of some options (as defined in the layouts in config.php).  
The excitement theme has just one layout file.
 
The downside of this is that I have to make the layout file do everything I want which means I need to make use of some options (as defined in the layouts in config.php).
 
The upside is that I only need to maintain one file.
 
Other than maintenance, using multiple layout files provides many advantages to real world themes in that you can easily tweak and customise specific layouts to achieve the goals of the organisation using the theme.
 
Before learning more it is good to understand the two primary objects that will be used in your layout files: $OUTPUT and $PAGE.


The upside is that I only need to maintain one file and for the purposes of this tutorial it is superb in that you will get a glimpse at how to use options and work with one file.  
'''$OUTPUT''' is an instance of the <syntaxhighlight lang="php">core_renderer</syntaxhighlight> class which is defined in lib/outputrenderers.php. Each method is clearly documented there, along with which is appropriate for use within the layout files.


I believe that other than maintenance using multiple layout files provides many advantages to real world themes in that you can easily tweak and customise specific layouts to achieve the goals of the organisation or business using the theme.
'''$PAGE''' is an instance of the <syntaxhighlight lang="php">moodle_page</syntaxhighlight> class defined in lib/pagelib.php. Most of the things you will want to use are the properties that are all documented at the top of the file. If you are not familiar with PHP properties, you access them like $PAGE->activityname, just like fields of an ordinary PHP object. (However, behind the scenes the value you get is produced by calling a function. Also, you cannot change these values, they are '''read-only'''. However, you don't need to understand all that if you are just using these properties in your theme.)


So lets start writing standard.php, the layout file for my ''`excitement`'' theme.
So lets start writing standard.php, the layout file for my ''`excitement`'' theme.


Before we start I should point out that this is entirely based on the layout files for the base theme, if reality I should have just used the base theme's layout however then I wouldn't get to show you what goes into a layout file.
===The top of the layout file===


===The top of the layout file===
<syntaxhighlight lang="php">
The first bit of code that goes into the layout file is that head section of the HTML.
<code php>
<?php
<?php
$hassidepre = $PAGE->blocks->region_has_content('side-pre', $OUTPUT);
$hassidepost = $PAGE->blocks->region_has_content('side-post', $OUTPUT);
echo $OUTPUT->doctype(); ?>
echo $OUTPUT->doctype(); ?>
<html <?php echo $OUTPUT->htmlattributes() ?>>
<html <?php echo $OUTPUT->htmlattributes() ?>>
Line 285: Line 253:
     <?php echo $OUTPUT->standard_head_html() ?>
     <?php echo $OUTPUT->standard_head_html() ?>
</head>
</head>
</code>
</syntaxhighlight>
What you'll notice about this is that it is very short. Moodle takes care of most of the tags that are required in the head.


Lets look at the code that goes into this section piece by piece:
Lets look at the code that goes into this section:
<code php>
<syntaxhighlight lang="php">
<?php
<?php
echo $OUTPUT->doctype(); ?>
echo $OUTPUT->doctype(); ?>
</code>
</syntaxhighlight>
This is very important and is required to go at the very top of the page. This tells Moodle to print out the document type tag that is determined by the settings within Moodle.
This is very important and is required to go at the very top of the page. This tells Moodle to print out the document type tag that is determined by the settings within Moodle.


<code php>
<syntaxhighlight lang="php">
<html <?php echo $OUTPUT->htmlattributes() ?>>
<html <?php echo $OUTPUT->htmlattributes() ?>>
</code>
</syntaxhighlight>
Here we open the HTML tag and then ask Moodle to print the attributes that should go inside it.
Here we open the HTML tag and then ask Moodle to print the attributes that should go inside it.


<code php>
<syntaxhighlight lang="php">
     <title><?php echo $PAGE->title ?></title>
     <title><?php echo $PAGE->title ?></title>
</code>
</syntaxhighlight>
Simply creates the title tag and asks Moodle to fill it in.
Simply creates the title tag and asks Moodle to fill it in.


<code php>
<syntaxhighlight lang="php">
     <?php echo $OUTPUT->standard_head_html() ?>
     <?php echo $OUTPUT->standard_head_html() ?>
</code>
</syntaxhighlight>
This is also a very important line. Here we are asking Moodle to print all of the other tags and content that need to go into the head. This includes stylesheets, script tags, and inline JavaScript code.
Here we are asking Moodle to print all of the other tags and content that need to go into the head. This includes stylesheets, script tags, and inline JavaScript code.


===The page header===
===The page header===
With the head printed we can start printing our actual content and the first bit of content we want is the page header.
 
<code php>
<syntaxhighlight lang="php">
<body id="<?php echo $PAGE->bodyid; ?>" class="<?php echo $PAGE->bodyclasses; ?>">
<body id="<?php p($PAGE->bodyid); ?>" class="<?php p($PAGE->bodyclasses); ?>">
<?php echo $OUTPUT->standard_top_of_body_html() ?>
<?php echo $OUTPUT->standard_top_of_body_html() ?>
<div id="page">
<div id="page">
Line 321: Line 288:
             <h1 class="headermain"><?php echo $PAGE->heading ?></h1>
             <h1 class="headermain"><?php echo $PAGE->heading ?></h1>
             <div class="headermenu"><?php
             <div class="headermenu"><?php
                 echo $OUTPUT->login_info();
                 if (method_exists($OUTPUT, 'user_menu')) {
                  echo $OUTPUT->user_menu(); // user menu, for Moodle 2.8
                } else {
                  echo $OUTPUT->login_info(); // login_info, Moodle 2.7 and before
                }
                 if (!empty($PAGE->layout_options['langmenu'])) {
                 if (!empty($PAGE->layout_options['langmenu'])) {
                     echo $OUTPUT->lang_menu();
                     echo $OUTPUT->lang_menu();
Line 336: Line 307:
     </div>
     </div>
<?php } ?>
<?php } ?>
</code>
</syntaxhighlight>


So there is a bit more going on here obviously.
So there is a bit more going on here obviously.


<code php>
<syntaxhighlight lang="php">
<body id="<?php echo $PAGE->bodyid; ?>" class="<?php echo $PAGE->bodyclasses; ?>">
<body id="<?php p($PAGE->bodyid); ?>" class="<?php p($PAGE->bodyclasses); ?>">
</code>
</syntaxhighlight>
Again much like what we did for the opening HTML tag in the head we have started writing the opening body tag and are then asking Moodle to give us the ID we should use for the body tag as well as the classes we should use.
Again much like what we did for the opening HTML tag in the head we have started writing the opening body tag and are then asking Moodle to give us the ID we should use for the body tag as well as the classes we should use.


<code php>
<syntaxhighlight lang="php">
<?php echo $OUTPUT->standard_top_of_body_html() ?>
<?php echo $OUTPUT->standard_top_of_body_html() ?>
</code>
</syntaxhighlight>
This very important call writes some critical bits of JavaScript into the page. It should always be located after the body tag as soon as possible.
This very important call writes some critical bits of JavaScript into the page. It should always be located after the body tag as soon as possible.


<code php>
<syntaxhighlight lang="php">
<?php if ($PAGE->heading || (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar())) { ?>
<?php if ($PAGE->heading || (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar())) { ?>
......
......
<?php } ?>
<?php } ?>
</code>
</syntaxhighlight>
Here we are checking whether or not we need to print the header for the page. There are three checks we need to make here:
Here we are checking whether or not we need to print the header for the page. There are three checks we need to make here:
# '''$PAGE->heading''' : This checks to make sure that there is a heading for the page. This will have been set by the script and normally describes the page in a couple of words.
# '''$PAGE->heading''' : This checks to make sure that there is a heading for the page. This will have been set by the script and normally describes the page in a couple of words.
Line 362: Line 333:


Leading on from this lets assume that there is a header to print.
Leading on from this lets assume that there is a header to print.
<code php>
<syntaxhighlight lang="php">
<?php if ($PAGE->heading) { ?>
<?php if ($PAGE->heading) { ?>
     <h1 class="headermain"><?php echo $PAGE->heading ?></h1>
     <h1 class="headermain"><?php echo $PAGE->heading ?></h1>
     .....
     .....
<?php } ?>
<?php } ?>
</code>
</syntaxhighlight>
This line is simply saying if the page has a heading print it. In this case we run the first check above again just to make sute there is a heading, we then open a heading tag that we choose and ask the page to print the heading.
This line is simply saying if the page has a heading print it. In this case we run the first check above again just to make sure there is a heading, we then open a heading tag that we choose and ask the page to print the heading.


<code php>
<syntaxhighlight lang="php">
<div class="headermenu"><?php
<div class="headermenu"><?php
     echo $OUTPUT->login_info();
     if (method_exists($OUTPUT, 'user_menu')) {
      echo $OUTPUT->user_menu(); // user menu, for Moodle 2.8
    } else {
      echo $OUTPUT->login_info(); // login_info, Moodle 2.7 and before
    }
     if (!empty($PAGE->layout_options['langmenu'])) {
     if (!empty($PAGE->layout_options['langmenu'])) {
         echo $OUTPUT->lang_menu();
         echo $OUTPUT->lang_menu();
Line 378: Line 353:
     echo $PAGE->headingmenu
     echo $PAGE->headingmenu
?></div>
?></div>
</code>
</syntaxhighlight>
This block of code looks a bit more complex but it really isn't. Here we are looking to print the menu and content that you see at the top of the page (usually to the right). We start by getting Moodle to print the login information for the current user. If the user is logged in this will be their name and a link to their profile, if not then it will be a link to login.
Here we are looking to print the menu and content that you see at the top of the page (usually to the right). We start by getting Moodle to print the login information for the current user. If the user is logged in this will be their name and a link to their profile, if not then it will be a link to login. Note that on Moodle 2.8 and above, a new user menu renderable is available that visually shows the same information as the login info renderable, as well as a dropdown menu with user-centric actions.


Next we check our page options to see whether a language menu should be printed. If in the layout definition within config.php it sets '''langmenu => true''' then we will print the language menu. This is the drop down box that lets the user change the language that Moodle displays in.
Next we check our page options to see whether a language menu should be printed. If in the layout definition within config.php it sets '''langmenu => true''' then we will print the language menu, a drop down box that lets the user change the language that Moodle displays in.


Finally the page also has a heading menu that can be printed. This heading menu is special HTML that the page you are viewing wants to add. It can be anything from drop down boxes to buttons and any number of each.
Finally the page also has a heading menu that can be printed. This heading menu is special HTML that the page you are viewing wants to add. It can be anything from drop down boxes to buttons and any number of each.


<code php>
<syntaxhighlight lang="php">
<?php if (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar()) { ?>
<?php if (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar()) { ?>
     <div class="navbar clearfix">
     <div class="navbar clearfix">
Line 392: Line 367:
     </div>
     </div>
<?php } ?>
<?php } ?>
</code>
</syntaxhighlight>
The final part of the header.
The final part of the header.


Here we want to print the navigation bar for the page if there is one. To find out if there is one we run checks number 2 and 3 again and proceed if they pass.
Here we want to print the navigation bar for the page if there is one. To find out if there is one we run checks number 2 and 3 again and proceed if they pass.


Assuming there is a header then there are two things that we need to print. The first is the navigation bar. This is a component that the OUTPUT library knows about. The second is a button to shown on the page.
Assuming there is a header then there are two things that we need to print. The first is the navigation bar. This is a component that the OUTPUT library knows about. The second is a button to show on the page.


In both cases we can choose to wrap them in a div to make our life as a themer easier.
In both cases we choose to wrap them in a div tag with a class attribute to enable theming on those elements.


Well that is it for the header. You will notice that there is a lot of PHP in there compared to the other sections of the layout file however rest assured the PHP that goes into it does not change and can be copied and pasted between themes.
Well that is it for the header. There is a lot of PHP compared to the other sections of the layout file but it does not change and can be copied and pasted between themes.


===The page content===
===The page content===
With the page head and the header both printed it's time to print the main content for the page. In this case I am going with the default two block regions plus the main content.
 
I am going with the default two block regions plus the main content.


Because I have based this theme and layout file on the base theme the HTML looks a little intense. This is because it is a floating div layout where the content comes first and then we get the columns (even though one column will be to the left of the content.) Don't worry too much about it. When it comes to writing your own theme you can go about it as you choose.
Because I have based this theme and layout file on the base theme the HTML looks a little intense. This is because it is a floating div layout where the content comes first and then we get the columns (even though one column will be to the left of the content.) Don't worry too much about it. When it comes to writing your own theme you can go about it as you choose.


<code php>
<syntaxhighlight lang="php">
<div id="page-content">
<div id="page-content">
     <div id="regions">
     <div id="region-main-box">
         <div id="regions-mask">
         <div id="region-post-box">
             <div id="region-main">
             <div id="region-main-wrap">
                 <div id="region-main-mask">
                 <div id="region-main">
                     <div class="region-content">
                     <div class="region-content">
                         <?php echo core_renderer::MAIN_CONTENT_TOKEN ?>
                         <?php echo core_renderer::MAIN_CONTENT_TOKEN ?>
Line 419: Line 395:
                 </div>
                 </div>
             </div>
             </div>
             <div id="region-pre">
             <?php if ($hassidepre) { ?>
                <div class="region-content">
                <div id="region-pre">
                    <?php echo $OUTPUT->blocks_for_region('side-pre') ?>
                    <div class="region-content">
                        <?php echo $OUTPUT->blocks_for_region('side-pre') ?>
                    </div>
                 </div>
                 </div>
            </div>
                <?php } ?>
            <div id="region-post">
               
                <div class="region-content">
                <?php if ($hassidepost) { ?>
                    <?php echo $OUTPUT->blocks_for_region('side-post') ?>
                <div id="region-post">
                    <div class="region-content">
                        <?php echo $OUTPUT->blocks_for_region('side-post') ?>
                    </div>
                 </div>
                 </div>
             </div>
             <?php } ?>
         </div>
         </div>
     </div>
     </div>
</div>
</div>
</code>
</syntaxhighlight>


In regards to PHP this section is very easy. There are only three lines for the whole section one to get the main content and one for each block region.
In regards to PHP this section is very easy. There are only three lines for the whole section one to get the main content and one for each block region.
<code php>
<syntaxhighlight lang="php">
<?php echo core_renderer::MAIN_CONTENT_TOKEN ?>
<?php echo core_renderer::MAIN_CONTENT_TOKEN ?>
</code>
</syntaxhighlight>
This line prints the main content for the page, its as simple as that!
This line prints the main content for the page.
PLEASE NOTE: In Moodle 2.2 onwards "core_renderer::MAIN_CONTENT_TOKEN" changed to "$OUTPUT->main_content()".


<code php>
<syntaxhighlight lang="php">
<?php if ($hassidepre) { ?>
....
<?php } ?>
</syntaxhighlight>
These lines of code check the variables we created earlier on to decide whether we should show the pre block region. If you try to display a block region that isn't there or has no content then Moodle will give you an error message so these lines are very important.
 
For those who get an error message if it is "unknown block region side-pre" or "unknown block region side-post" then this is the issue you are experiencing. Simply add the following lines and all will be fine.
 
<syntaxhighlight lang="php">
<?php echo $OUTPUT->blocks_for_region('side-pre') ?>
<?php echo $OUTPUT->blocks_for_region('side-pre') ?>
</code>
</syntaxhighlight>
This line gets all of the blocks and more particularly there content for the block region '''side-pre'''. This block region will be displayed to the left of the content.
This line gets all of the blocks and more particularly the content for the block region '''side-pre'''. This block region will be displayed to the left of the content.


<code php>
<syntaxhighlight lang="php">
<?php if ($hassidepost) { ?>
....
<?php } ?>
</syntaxhighlight>
Again we should make this check for every block region as there are some pages that have no blocks what-so-ever.
 
<syntaxhighlight lang="php">
<?php echo $OUTPUT->blocks_for_region('side-post') ?>
<?php echo $OUTPUT->blocks_for_region('side-post') ?>
</code>
</syntaxhighlight>
Here we are getting the other block region '''side-post''' which will be displayed to the right of the content.
Here we are getting the other block region '''side-post''' which will be displayed to the right of the content.


===The page footer===
===The page footer===
The final section is the page footer. Here we want to print the footer for the page, any content required by Moodle, and then close the last tags.
Here we want to print the footer for the page, any content required by Moodle, and then close the last tags.
<code php>
<syntaxhighlight lang="php">
     <?php if (empty($PAGE->layout_options['nofooter'])) { ?>
     <?php if (empty($PAGE->layout_options['nofooter'])) { ?>
     <div id="page-footer" class="clearfix">
     <div id="page-footer" class="clearfix">
Line 467: Line 465:
</body>
</body>
</html>
</html>
</code>
</syntaxhighlight>


The section of code is responsible for printing the footer for the page.
The section of code is responsible for printing the footer for the page.
<code php>
<syntaxhighlight lang="php">
     <?php if (empty($PAGE->layout_options['nofooter'])) { ?>
     <?php if (empty($PAGE->layout_options['nofooter'])) { ?>
     <div id="page-footer" class="clearfix">
     <div id="page-footer" class="clearfix">
Line 481: Line 479:
     </div>
     </div>
     <?php } ?>
     <?php } ?>
</code>
</syntaxhighlight>
The first thing we do before printing the footer is check that we actually want to print it. This is done by checking the options for the layout as defined in the config.php file. If '''nofooter => true''' is set the we don't want to print the footer and should skip over this body of code.
The first thing we do before printing the footer is check that we actually want to print it. This is done by checking the options for the layout as defined in the config.php file. If '''nofooter => true''' is set the we don't want to print the footer and should skip over this body of code.


Line 492: Line 490:


And the final line of code for our layout file is:
And the final line of code for our layout file is:
<code php>
<syntaxhighlight lang="php">
<?php echo $OUTPUT->standard_end_of_body_html(); ?>
<?php echo $OUTPUT->standard_end_of_body_html(); ?>
</code>
</syntaxhighlight>
This is one of the most important lines of code in the layout file. It asks Moodle to print any required content into the page, and there will likely be a lot although most of it will not be visual.
This is one of the most important lines of code in the layout file. It asks Moodle to print any required content into the page, and there will likely be a lot although most of it will not be visual.


It will instead be things such as inline scripts and JavaScript files required to go at the bottom of the page. If you forget this line its likely no JavaScript will work!
It will instead be things such as inline scripts and JavaScript files required to go at the bottom of the page. If you forget this line its likely no JavaScript will work!


That's it! we've now written our layout file and it is all set to go. The complete source is below for reference and remember if you want more practical examples simply look at the layout files located within the layout directory of other themes.
We've now written our layout file and it is all set to go. The complete source is below for reference. Remember if you want more practical examples simply look at the layout files located within the layout directory of other themes.


<div style="height:400px;overflow:auto;">
<syntaxhighlight lang="php">
<code php>
<?php
<?php
$hassidepre = $PAGE->blocks->region_has_content('side-pre', $OUTPUT);
$hassidepost = $PAGE->blocks->region_has_content('side-post', $OUTPUT);
echo $OUTPUT->doctype() ?>
echo $OUTPUT->doctype() ?>
<html <?php echo $OUTPUT->htmlattributes() ?>>
<html <?php echo $OUTPUT->htmlattributes() ?>>
Line 512: Line 511:
</head>
</head>


<body id="<?php echo $PAGE->bodyid; ?>" class="<?php echo $PAGE->bodyclasses; ?>">
<body id="<?php p($PAGE->bodyid); ?>" class="<?php p($PAGE->bodyclasses); ?>">
<?php echo $OUTPUT->standard_top_of_body_html() ?>
<?php echo $OUTPUT->standard_top_of_body_html() ?>
<div id="page">
<div id="page">
Line 536: Line 535:


     <div id="page-content">
     <div id="page-content">
         <div id="regions">
         <div id="region-main-box">
             <div id="regions-mask">
             <div id="region-post-box">
                 <div id="region-main">
                 <div id="region-main-wrap">
                     <div id="region-main-mask">
                     <div id="region-main">
                         <div class="region-content">
                         <div class="region-content">
                             <?php echo core_renderer::MAIN_CONTENT_TOKEN ?>
                             <?php echo core_renderer::MAIN_CONTENT_TOKEN ?>
Line 545: Line 544:
                     </div>
                     </div>
                 </div>
                 </div>
                <?php if ($hassidepre) { ?>
                 <div id="region-pre">
                 <div id="region-pre">
                     <div class="region-content">
                     <div class="region-content">
Line 550: Line 550:
                     </div>
                     </div>
                 </div>
                 </div>
                <?php } ?>
               
                <?php if ($hassidepost) { ?>
                 <div id="region-post">
                 <div id="region-post">
                     <div class="region-content">
                     <div class="region-content">
Line 555: Line 558:
                     </div>
                     </div>
                 </div>
                 </div>
                <?php } ?>
             </div>
             </div>
         </div>
         </div>
     </div>
     </div>


     <?php if ($hasfooter) { ?>
     <?php if (empty($PAGE->layout_options['nofooter'])) { ?>
     <div id="page-footer" class="clearfix">
     <div id="page-footer" class="clearfix">
         <p class="helplink"><?php echo page_doc_link(get_string('moodledocslink')) ?></p>
         <p class="helplink"><?php echo page_doc_link(get_string('moodledocslink')) ?></p>
Line 573: Line 577:
</body>
</body>
</html>
</html>
</code>
</syntaxhighlight>
</div>


==Adding some CSS==
==Adding some CSS==
With config.php and standard.php both complete the theme is now usable and starting to look like a real theme, however if you change to it using the theme selector you will notice that it still lacks any style.
With config.php and standard.php both complete the theme is now usable and starting to look like a real theme, however if you change to it using the theme selector you will notice that it still lacks any style.


This of course is where CSS comes in. When writing Moodle developers are strongly encouraged to not use inline styles anywhere. This is fantastic us as themers because there is nothing (or at least very little) in Moodle that cannot be styled using CSS.
This of course is where CSS comes in. When writing code Moodle developers are strongly encouraged to not use inline styles anywhere. This is fantastic for us as themers because there is nothing (or at least very little) in Moodle that cannot be styled using CSS.
 
If you haven't read [[ Themes 2.0|Themes 2.0]] at this point now would be a good time to go and read up about writing good CSS which is what we will touch on lightly here.


===Moodle CSS basics===
===Moodle CSS basics===
Before we begin writing CSS there are a couple of things you should be aware of.


In Moodle 2.0 all of the CSS for the whole of Moodle is delivered all of the time! This was done for performance reasons. Moodle now reads in all of the CSS, combines it into one file, shrinks it removing any white space, caches it, and then delivers it.
In Moodle 2.0 all of the CSS for the whole of Moodle is delivered all of the time! This was done for performance reasons. Moodle now reads in all of the CSS, combines it into one file, shrinks it removing any white space, caches it, and then delivers it.
Line 592: Line 592:
You will need to write good CSS that won't clash with any other CSS within Moodle.
You will need to write good CSS that won't clash with any other CSS within Moodle.


Unfortunately Moodle is so big and complex, and there is so much contributed code available that there is no way to ensure that classes don't get reused. What we can control however is the classes and id that get added to the body tag for every page. When writing CSS it is highly encouraged to make full use of these body classes, using them will help ensure the CSS you write has the least chance of causing conflicts.
Moodle is so big and complex,there is no way to ensure that classes don't get reused. What we can control however is the classes and id that get added to the body tag for every page. When writing CSS it is highly encouraged to make full use of these body classes, using them will help ensure the CSS you write has the least chance of causing conflicts.


You should also take the time to look at how the Moodle themes use CSS. Look at their use of the body classes and how they separate the CSS for the theme into separate files based on the region of Moodle it applies to.
You should also take the time to look at how the Moodle themes use CSS. Look at their use of the body classes and how they separate the CSS for the theme into separate files based on the region of Moodle it applies to.


Check out the [[Themes 2.0|Themes 2.0]] page for more information about writing good CSS.
Check out [[Themes 2.0|Themes 2.0]] and [[CSS coding style]] for more information about writing good CSS.


===Starting to write excitement.css===
===Starting to write excitement.css===
Alright to it is time to start writing a bit of CSS for my excitement theme.
<code css>
a {text-decoration: none;}
.addcoursebutton .singlebutton {text-align: center;}


h1.headermain {color: #fff;}
<syntaxhighlight lang="css">
h2.main {border-bottom: 3px solid #013D6A;color: #013D6A;text-align: center;}
a {
h2.headingblock {font-size: 18pt;margin-top: 0;background-color: #013D6A;color: #FFF;text-align: center;}
    text-decoration: none;
#page-header {background-color: #013D6A;}
}
#page-header .headermenu {color: #FFF;}
 
#page-header .headermenu a {color: #FDFF2A;}
.addcoursebutton .singlebutton {
    text-align: center;
}
 
h1.headermain {
    color: #fff;
}
 
h2.main {
    border-bottom: 3px solid #013D6A;
    color: #013D6A;
    text-align: center;
}
 
h2.headingblock {
    font-size: 18pt;
    margin-top: 0;
    background-color: #013D6A;
    color: #FFF;
    text-align: center;
}
 
#page-header {
    background-color: #013D6A;
}
 
#page-header .headermenu {
    color: #FFF;
}
 
#page-header .headermenu a {
    color: #FDFF2A;
}
 
.navbar {
    padding-left: 1em;
}
 
.breadcrumb li {
    color: #FFF;
}
 
.breadcrumb li a {
    color: #FFF;
}
 
.block {
    background-color: #013D6A;
}
 
.block .header .title {
    color: #FFF;
}
 
.block .header .title .block_action input {
    background-color: #FFF;
}
 
.block .content {
    border: 1px solid #000;
    padding: 5px;
    background-color: #FFF;
}
 
.block .content .block_tree p {
    font-size: 80%;
}
 
.block_settings_navigation_tree .content .footer {
    text-align: center;
}
 
.block_settings_navigation_tree .content .footer .adminsearchform {
    margin-left: 5%;
    width: 90%;
    font-size: 9pt;
}
 
.block_settings_navigation_tree .content .footer .adminsearchform #adminsearchquery {
    width: 95%;
}
 
.block_calendar_month .content .calendar-controls a {
    color: #013D6A;
    font-weight: bold;
}
 
.block_calendar_month .content .minicalendar td {
    border-color: #FFF;
}
 
.block_calendar_month .content .minicalendar .day {
    color: #FFF;
    background-color: #013D6A;
}


.sideblock {background-color: #013D6A;}
.block_calendar_month .content .minicalendar .day a {
.sideblock .header .title {color: #FFF;}
    color: #FFF000;
.sideblock .header .title .block_action input {background-color: #FFF;}
}
.sideblock .content {border: 1px solid #000;padding: 5px;background-color: #FFF;}
.sideblock .content .block_tree p {font-size: 80%;}


.block_settings_navigation_tree .content .footer {text-align: center;}
.block_calendar_month .content .minicalendar .weekdays th {
.block_settings_navigation_tree .content .footer .adminsearchform {margin-left: 5%;width: 90%;font-size: 9pt;}
    border-width: 0;
.block_settings_navigation_tree .content .footer .adminsearchform #adminsearchquery {width: 95%;}
    font-weight: bold;
    color: #013D6A;
}


.block_calendar_month .content .calendar-controls a {color: #013D6A;font-weight: bold;}
.block_calendar_month .content .minicalendar .weekdays abbr {
.block_calendar_month .content .minicalendar td {border-color: #FFF;}
    border-width: 0;
.block_calendar_month .content .minicalendar .day {color: #FFF;background-color: #013D6A;}
    text-decoration: none;
.block_calendar_month .content .minicalendar .day a {color: #FFF000;}
}
.block_calendar_month .content .minicalendar .weekdays th {border-width: 0;font-weight: bold;color: #013D6A;}
</syntaxhighlight>
.block_calendar_month .content .minicalendar .weekdays abbr {border-width: 0;text-decoration: none;}
</code>
[[image:Learn_to_theme_02.jpg|400px|thumb|Excitement theme screenshot]]
[[image:Learn_to_theme_02.jpg|400px|thumb|Excitement theme screenshot]]
Now of course this isn't all of the CSS for the theme, this is just enough to style the front page as I would like it to look on a fresh install when the user is not logged in.
This isn't all of the CSS for the theme, but just enough to style the front page when the user is not logged in.
Remember this theme extends the base theme so there is already CSS for layout as well.
Remember this theme extends the base theme so there is already CSS for layout as well.


What you should notice when looking at the CSS:
Note:
* The CSS is laid out in a single line format. You will notice this is done within the core themes for Moodle this makes it quicker to read the selectors and see what is being styled. When writing your own CSS however it is entirely up to you how you lay it out.
* The CSS is laid out in a single line format. This is done within the core themes for Moodle. It makes it quicker to read the selectors and see what is being styled.
* I have written my selectors to take into account the structure of the HTML (more than just the one tag I want to style). This helps further to reduce the conflicts that I may encounter.
* I have written my selectors to take into account the structure of the HTML (more than just the one tag I want to style). This helps further to reduce the conflicts that I may encounter.
* I use generic classes like ''.sideblock'' only where I want to be generic, as soon as I want to be specific I use the unique classes such as ''.block_calendar_month''
* I use generic classes like ''.sideblock'' only where I want to be generic, as soon as I want to be specific I use the unique classes such as ''.block_calendar_month''


<br style="clear:right;" />
<br style="clear:right;" />
===Using images within CSS===
===Using images within CSS===
This is of course something you are more than likely to want to do when creating a theme and its very easy thanks to the new theme engine.


At this point I will add two image files to the pix directory of my theme:
I will add two image files to the pix directory of my theme:
; /theme/excitement/pix/background.png : This will be the background image for my theme.
; /theme/excitement/pix/background.png : This will be the background image for my theme.
* /theme/excitement/pix/gradient.jpg : This will be a gradient I use for the header and headings.
; /theme/excitement/pix/gradient.jpg : This will be a gradient I use for the header and headings.
I quickly created both of these images using gimp and simply saved them to the pix directory.
I quickly created both of these images using gimp and simply saved them to the pix directory.
<code css>
<syntaxhighlight lang="css">
html {background-image:url([[pix:theme|background]]);}
html {
    background-image: url([[pix:theme|background]]);
}


h2.headingblock,
h2.headingblock,
#page-header,
#page-header,
.sideblock,
.sideblock,
.block_calendar_month .content .minicalendar .day {background-image:url([[pix:theme|gradient]]);background-repeat:repeat-x;background-color: #0273C8;}
.block_calendar_month .content .minicalendar .day {
</code>
    background-image: url([[pix:theme|gradient]]);
    background-repeat: repeat-x;
    background-color: #0273C8;
}
</syntaxhighlight>
[[image:Learn_to_theme_03.jpg‎|400px|thumb|Excitement theme screenshot]]
[[image:Learn_to_theme_03.jpg‎|400px|thumb|Excitement theme screenshot]]
The CSS above is the two new rules that I had to write to use my images within CSS.
The CSS above is the two new rules that I had to write to use my images within CSS.
Line 663: Line 757:
The advantage of using this syntax over writing the path in is that the path may change depending on where you are or what theme is being used.
The advantage of using this syntax over writing the path in is that the path may change depending on where you are or what theme is being used.


Remember that other themers may choose to extend your theme with their own, if you use this syntax then all they need to do to override the image is to create one with the same name in their themes directory.
Other themers may choose to extend your theme with their own; if you use this syntax then all they need to do to override the image is to create one with the same name in their themes directory.


You will also notice that I don't need to add the image files extension. This is because Moodle is smart enough to work out what extension the file uses. It also allows themers to override images with different formats.
You will also notice that I don't need to add the image files extension. This is because Moodle is smart enough to work out what extension the file uses. It also allows themers to override images with different formats.
Line 669: Line 763:
<br style="clear:right" />
<br style="clear:right" />
The following is the complete CSS for my theme:
The following is the complete CSS for my theme:
<div style="overflow:auto;height:400px;">
<div style="overflow:auto;height:860px;">
<code css>
<syntaxhighlight lang="css">
a {text-decoration: none;}
a {text-decoration: none;}
.addcoursebutton .singlebutton {text-align: center;}
.addcoursebutton .singlebutton {text-align: center;}
Line 703: Line 797:
#page-header,
#page-header,
.sideblock,
.sideblock,
.block_calendar_month .content .minicalendar .day {background-image:url([[pix:theme|gradient]]);background-repeat:repeat-x;background-color: #0273C8;}
.block_calendar_month .content .minicalendar .day {background-image:url([[pix:theme|gradient]]);
</code>
  background-repeat:repeat-x;background-color: #0273C8;}
</syntaxhighlight>
</div>
</div>


==Adding a screenshot and favicon==
==Adding a screenshot and favicon==
The final thing to do at this point is add both a screenshot for this theme as well as fav icon for it.
The final thing to do at this point is add both a screenshot for this theme as well as a favicon for it.
The screenshot will be shown in the theme selector screen and should be named screenshot.jpg.
The screenshot will be shown in the theme selector screen and should be named ''screenshot.jpg''.
The favicon will of course be used when someone bookmarks this page.
The favicon will be used when someone bookmarks this page.
Both images should be located in your themes pix directory as follows:
Both images should be located in your themes pix directory as follows:
* /theme/excitement/pix/screenshot.jpg
* /theme/excitement/pix/screenshot.jpg
Line 716: Line 811:


In the case of my theme I have taken a screenshot and added it to that directory, and because I don't really want to do anything special with the favicon I have copied it from /theme/base/pix/favicon.ico so that at least it will be recognisable as Moodle.
In the case of my theme I have taken a screenshot and added it to that directory, and because I don't really want to do anything special with the favicon I have copied it from /theme/base/pix/favicon.ico so that at least it will be recognisable as Moodle.
[[es:Desarollo:Temas 2.0 creando tu primer tema]]

Latest revision as of 08:09, 15 July 2021

Warning: This page is no longer in use. The information contained on the page should NOT be seen as relevant or reliable.

Moodle 2.1 Moodle 2.0

This document describes how to create a theme for Moodle 2.0 and Moodle 2.1. ONLY. It assumes you have some understanding of how themes within Moodle work as well as a good understanding of HTML and CSS.

For Themes in Moodle 2.2 onwards please read the tutorial about How to clone a Moodle 2.2 theme.


Theme designer mode

Under normal operation Moodle does several things in the name of performance, one of these is to combine all of the CSS into one file, minimize it, cache it on the server, and then serve it. After the first request the cached version is served to greatly improve page performance.

What this means for you as a themer? When you make changes they will not be seen immediately. In fact you will need to tell Moodle to rebuild the cache that it is serving. This isn't practical for designing themes of course so the theme designer mode was added. When enabled it tells Moodle not to combine or cache the CSS that gets delivered. This has the downside that page load times will take significantly longer, however you will see your changes immediately every time.

Theme designer mode may be enabled via Administration > Appearance > Themes > Theme settings.

Warning: Internet Explorer versions 6 and 7 have BIG problems when a site attempts to link to more than 31 stylesheets, in which case either a limited number or no styles get applied. Because Moodle sends up all of the CSS all of the time with theme designer mode turned on there is a very high chance you will get more than 31 stylesheets being included. This will, of course, cause major problems for Internet Explorer until theme designer mode is turned off.

Getting started

The first thing you need to do is create the directories and files you will be using, the first thing to create is the actual directory for your theme. This should be the name of your theme, in my case it's 'excitement'. The directory should be located within the theme directory of Moodle, ./moodle/theme/excitement/ will be the directory I create.

Now within that directory we can immediately create several files that we know we are going to need.

So the files that we want to create are:

config.php
All of our settings will go here.
/style/
This directory will contain all of our stylesheets.
/style/excitement.css
All of our css will go in here.
/pix/
Into this directory we'll put a screen shot of our theme as well as our favicon and any images we use in CSS.
/layout/
Our layout files will end up in this directory.
/layout/standard.php
This will be our one basic layout file.
/lang/en/
The file we put here will make our theme name show properly on the Theme Selector page. You need a few standard entries. Copy the one from the Standard theme and modify is easiest.

So after this setup step you should have a directory structure similar to what is shown below.

Learn to theme 01.jpg

Configuring our theme

Open config.php in your favourite editor and start by adding the opening PHP tags <?php.

Now we need to add the settings:

$THEME->name = 'excitement';

Very simply this tells Moodle the name of your theme, and if you ever have several config.php files open this will help you identify which one you are looking at.

Next, the parents for this theme.

$THEME->parents = array('base');

This tells Moodle that my new theme `excitement`' wants to extend the base theme.

A theme can extend any number of themes. Rather than creating an entirely new theme and copying all of the CSS, you can simply create a new theme, extend the theme you like and just add the changes you want to your theme.

Also worth noting is the purpose of the base theme: it provides us with a basic layout and just enough CSS to make everything fall into place.

Now we will tell Moodle about our stylesheets:

$THEME->sheets = array('excitement');

The final thing we need to add into our theme's config.php file is the definition of the layouts for our theme: (in the code below, replace 'general.php' with 'standard.php' because that is the file that is previously created)

$THEME->layouts = array(
    // Most backwards compatible layout without the blocks - this is the layout used by default
    'base' => array(
        'file' => 'general.php',
        'regions' => array(),
    ),
    // Standard layout with blocks, this is recommended for most pages with general information
    'standard' => array(
        'file' => 'general.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
    ),
    // Main course page
    'course' => array(
        'file' => 'general.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
        'options' => array('langmenu'=>true),
    ),
    'coursecategory' => array(
        'file' => 'general.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
    ),
    // part of course, typical for modules - default page layout if $cm specified in require_login()
    'incourse' => array(
        'file' => 'general.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
    ),
    // The site home page.
    'frontpage' => array(
        'file' => 'frontpage.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
    ),
    // Server administration scripts.
    'admin' => array(
        'file' => 'general.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    // My dashboard page
    'mydashboard' => array(
        'file' => 'general.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
        'options' => array('langmenu'=>true),
    ),
    // My public page
    'mypublic' => array(
        'file' => 'general.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
    ),
    'login' => array(
        'file' => 'general.php',
        'regions' => array(),
        'options' => array('langmenu'=>true),
    ),

    // Pages that appear in pop-up windows - no navigation, no blocks, no header.
    'popup' => array(
        'file' => 'general.php',
        'regions' => array(),
        'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nologininfo'=>true, 'nocourseheaderfooter'=>true),
    ),
    // No blocks and minimal footer - used for legacy frame layouts only!
    'frametop' => array(
        'file' => 'general.php',
        'regions' => array(),
        'options' => array('nofooter'=>true, 'nocoursefooter'=>true),
    ),
    // Embeded pages, like iframe/object embeded in moodleform - it needs as much space as possible
    'embedded' => array(
        'file' => 'embedded.php',
        'regions' => array(),
        'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nocourseheaderfooter'=>true),
    ),
    // Used during upgrade and install, and for the 'This site is undergoing maintenance' message.
    // This must not have any blocks, and it is good idea if it does not have links to
    // other places - for example there should not be a home link in the footer...
    'maintenance' => array(
        'file' => 'general.php',
        'regions' => array(),
        'options' => array('noblocks'=>true, 'nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nocourseheaderfooter'=>true),
    ),
    // Should display the content and basic headers only.
    'print' => array(
        'file' => 'general.php',
        'regions' => array(),
        'options' => array('noblocks'=>true, 'nofooter'=>true, 'nonavbar'=>false, 'nocustommenu'=>true, 'nocourseheaderfooter'=>true),
    ),
    // The pagelayout used when a redirection is occuring.
    'redirect' => array(
        'file' => 'embedded.php',
        'regions' => array(),
        'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nocourseheaderfooter'=>true),
    ),
    // The pagelayout used for reports.
    'report' => array(
        'file' => 'report.php',
        'regions' => array('side-pre'),
        'defaultregion' => 'side-pre',
    ),
    // The pagelayout used for safebrowser and securewindow.
    'secure' => array(
        'file' => 'general.php',
        'regions' => array('side-pre', 'side-post'),
        'defaultregion' => 'side-pre',
        'options' => array('nofooter'=>true, 'nonavbar'=>true, 'nocustommenu'=>true, 'nologinlinks'=>true, 'nocourseheaderfooter'=>true),
    ),
);


/** List of javascript files that need to be included on each page */
$THEME->javascripts = array();
$THEME->javascripts_footer = array();

What you are looking at is the different layouts for our theme. Why are there so many? Because, that is how many there are in Moodle. There is one for every general type of page. With my `excitement` theme I have chosen to use my own layout. Unless there was a specific reason to do so, normally you would not need to create your own layouts, you could extend the base theme, and use its layouts, meaning you would only have to write CSS to achieve your desired look.

For each layout above, you will notice the following four things are being set:

file
This is the name of the layout file we want to use, it should always be located in the above themes layout directory. For us this is of course standard.php as we only have one layout file.
regions
This is an array of block regions that our theme has. Each entry in here can be used to put blocks in when that layout is being used.
defaultregion
If a layout has regions it should have a default region, this is where blocks get put when first added. It is also where the "add block" block is shown when editing is on.
options
These are special settings, anything that gets put into the options array is available later on when we are writing our layout file.

There are additional settings that can be defined in the config.php file - see Themes for the full list.

Configuring the language file

Open theme_boost.php file from boost/lang/en/theme_boost.php

Save it as excitement/lang/en/theme_excitement.php.

Change

$string['pluginname'] = 'Boost';

to

$string['pluginname'] = 'Excitement';

After making these changes and perhaps clearing theme caches and/or purging all caches, the new theme name should now show properly in the Theme Selector: Site Administration > Appearance > Themes > Theme Selector

You can also edit the theme description:

$string['choosereadme'] = 'Write your theme description here.';

You need to leave the following two lines in place (you can change the wording if you need to) to avoid notices when editing blocks etc.:

$string['region-side-pre'] = 'Right';

Writing the layout file

The excitement theme has just one layout file.

The downside of this is that I have to make the layout file do everything I want which means I need to make use of some options (as defined in the layouts in config.php).

The upside is that I only need to maintain one file.

Other than maintenance, using multiple layout files provides many advantages to real world themes in that you can easily tweak and customise specific layouts to achieve the goals of the organisation using the theme.

Before learning more it is good to understand the two primary objects that will be used in your layout files: $OUTPUT and $PAGE.

$OUTPUT is an instance of the

core_renderer

class which is defined in lib/outputrenderers.php. Each method is clearly documented there, along with which is appropriate for use within the layout files. $PAGE is an instance of the

moodle_page

class defined in lib/pagelib.php. Most of the things you will want to use are the properties that are all documented at the top of the file. If you are not familiar with PHP properties, you access them like $PAGE->activityname, just like fields of an ordinary PHP object. (However, behind the scenes the value you get is produced by calling a function. Also, you cannot change these values, they are read-only. However, you don't need to understand all that if you are just using these properties in your theme.)

So lets start writing standard.php, the layout file for my `excitement` theme.

The top of the layout file

<?php
$hassidepre = $PAGE->blocks->region_has_content('side-pre', $OUTPUT);
$hassidepost = $PAGE->blocks->region_has_content('side-post', $OUTPUT);
echo $OUTPUT->doctype(); ?>
<html <?php echo $OUTPUT->htmlattributes() ?>>
<head>
    <title><?php echo $PAGE->title ?></title>
    <?php echo $OUTPUT->standard_head_html() ?>
</head>

Lets look at the code that goes into this section:

<?php
echo $OUTPUT->doctype(); ?>

This is very important and is required to go at the very top of the page. This tells Moodle to print out the document type tag that is determined by the settings within Moodle.

<html <?php echo $OUTPUT->htmlattributes() ?>>

Here we open the HTML tag and then ask Moodle to print the attributes that should go inside it.

    <title><?php echo $PAGE->title ?></title>

Simply creates the title tag and asks Moodle to fill it in.

    <?php echo $OUTPUT->standard_head_html() ?>

Here we are asking Moodle to print all of the other tags and content that need to go into the head. This includes stylesheets, script tags, and inline JavaScript code.

The page header

<body id="<?php p($PAGE->bodyid); ?>" class="<?php p($PAGE->bodyclasses); ?>">
<?php echo $OUTPUT->standard_top_of_body_html() ?>
<div id="page">
<?php if ($PAGE->heading || (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar())) { ?>
    <div id="page-header">
        <?php if ($PAGE->heading) { ?>
            <h1 class="headermain"><?php echo $PAGE->heading ?></h1>
            <div class="headermenu"><?php
                if (method_exists($OUTPUT, 'user_menu')) {
                  echo $OUTPUT->user_menu(); // user menu, for Moodle 2.8
                } else {
                  echo $OUTPUT->login_info(); // login_info, Moodle 2.7 and before
                }
                if (!empty($PAGE->layout_options['langmenu'])) {
                    echo $OUTPUT->lang_menu();
                }
                echo $PAGE->headingmenu
            ?></div>
        <?php } ?>
        <?php if (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar()) { ?>
            <div class="navbar clearfix">
                <div class="breadcrumb"><?php echo $OUTPUT->navbar(); ?></div>
                <div class="navbutton"> <?php echo $PAGE->button; ?></div>
            </div>
        <?php } ?>
    </div>
<?php } ?>

So there is a bit more going on here obviously.

<body id="<?php p($PAGE->bodyid); ?>" class="<?php p($PAGE->bodyclasses); ?>">

Again much like what we did for the opening HTML tag in the head we have started writing the opening body tag and are then asking Moodle to give us the ID we should use for the body tag as well as the classes we should use.

<?php echo $OUTPUT->standard_top_of_body_html() ?>

This very important call writes some critical bits of JavaScript into the page. It should always be located after the body tag as soon as possible.

<?php if ($PAGE->heading || (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar())) { ?>
......
<?php } ?>

Here we are checking whether or not we need to print the header for the page. There are three checks we need to make here:

  1. $PAGE->heading : This checks to make sure that there is a heading for the page. This will have been set by the script and normally describes the page in a couple of words.
  2. empty($PAGE->layout_options['nonavbar']) : Now this check is looking at the layout options that we set in our config.php file. It is looking to see if the layout set 'nonavbar' => true.
  3. $PAGE->has_navbar() The third check is to check with the page whether it has a navigation bar to display.

If either there is a heading for this page or there is a navigation bar to display then we will display a heading.

Leading on from this lets assume that there is a header to print.

<?php if ($PAGE->heading) { ?>
    <h1 class="headermain"><?php echo $PAGE->heading ?></h1>
    .....
<?php } ?>

This line is simply saying if the page has a heading print it. In this case we run the first check above again just to make sure there is a heading, we then open a heading tag that we choose and ask the page to print the heading.

<div class="headermenu"><?php
    if (method_exists($OUTPUT, 'user_menu')) {
      echo $OUTPUT->user_menu(); // user menu, for Moodle 2.8
    } else {
      echo $OUTPUT->login_info(); // login_info, Moodle 2.7 and before
    }
    if (!empty($PAGE->layout_options['langmenu'])) {
        echo $OUTPUT->lang_menu();
    }
    echo $PAGE->headingmenu
?></div>

Here we are looking to print the menu and content that you see at the top of the page (usually to the right). We start by getting Moodle to print the login information for the current user. If the user is logged in this will be their name and a link to their profile, if not then it will be a link to login. Note that on Moodle 2.8 and above, a new user menu renderable is available that visually shows the same information as the login info renderable, as well as a dropdown menu with user-centric actions.

Next we check our page options to see whether a language menu should be printed. If in the layout definition within config.php it sets langmenu => true then we will print the language menu, a drop down box that lets the user change the language that Moodle displays in.

Finally the page also has a heading menu that can be printed. This heading menu is special HTML that the page you are viewing wants to add. It can be anything from drop down boxes to buttons and any number of each.

<?php if (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar()) { ?>
    <div class="navbar clearfix">
        <div class="breadcrumb"><?php echo $OUTPUT->navbar(); ?></div>
        <div class="navbutton"> <?php echo $PAGE->button; ?></div>
    </div>
<?php } ?>

The final part of the header.

Here we want to print the navigation bar for the page if there is one. To find out if there is one we run checks number 2 and 3 again and proceed if they pass.

Assuming there is a header then there are two things that we need to print. The first is the navigation bar. This is a component that the OUTPUT library knows about. The second is a button to show on the page.

In both cases we choose to wrap them in a div tag with a class attribute to enable theming on those elements.

Well that is it for the header. There is a lot of PHP compared to the other sections of the layout file but it does not change and can be copied and pasted between themes.

The page content

I am going with the default two block regions plus the main content.

Because I have based this theme and layout file on the base theme the HTML looks a little intense. This is because it is a floating div layout where the content comes first and then we get the columns (even though one column will be to the left of the content.) Don't worry too much about it. When it comes to writing your own theme you can go about it as you choose.

<div id="page-content">
    <div id="region-main-box">
        <div id="region-post-box">
            <div id="region-main-wrap">
                <div id="region-main">
                    <div class="region-content">
                        <?php echo core_renderer::MAIN_CONTENT_TOKEN ?>
                    </div>
                </div>
            </div>
            <?php if ($hassidepre) { ?>
                <div id="region-pre">
                    <div class="region-content">
                        <?php echo $OUTPUT->blocks_for_region('side-pre') ?>
                    </div>
                </div>
                <?php } ?>
                
                <?php if ($hassidepost) { ?>
                <div id="region-post">
                    <div class="region-content">
                        <?php echo $OUTPUT->blocks_for_region('side-post') ?>
                    </div>
                </div>
            <?php } ?>
        </div>
    </div>
</div>

In regards to PHP this section is very easy. There are only three lines for the whole section one to get the main content and one for each block region.

<?php echo core_renderer::MAIN_CONTENT_TOKEN ?>

This line prints the main content for the page. PLEASE NOTE: In Moodle 2.2 onwards "core_renderer::MAIN_CONTENT_TOKEN" changed to "$OUTPUT->main_content()".

<?php if ($hassidepre) { ?>
....
<?php } ?>

These lines of code check the variables we created earlier on to decide whether we should show the pre block region. If you try to display a block region that isn't there or has no content then Moodle will give you an error message so these lines are very important.

For those who get an error message if it is "unknown block region side-pre" or "unknown block region side-post" then this is the issue you are experiencing. Simply add the following lines and all will be fine.

<?php echo $OUTPUT->blocks_for_region('side-pre') ?>

This line gets all of the blocks and more particularly the content for the block region side-pre. This block region will be displayed to the left of the content.

<?php if ($hassidepost) { ?>
....
<?php } ?>

Again we should make this check for every block region as there are some pages that have no blocks what-so-ever.

<?php echo $OUTPUT->blocks_for_region('side-post') ?>

Here we are getting the other block region side-post which will be displayed to the right of the content.

The page footer

Here we want to print the footer for the page, any content required by Moodle, and then close the last tags.

    <?php if (empty($PAGE->layout_options['nofooter'])) { ?>
    <div id="page-footer" class="clearfix">
        <p class="helplink"><?php echo page_doc_link(get_string('moodledocslink')) ?></p>
        <?php
        echo $OUTPUT->login_info();
        echo $OUTPUT->home_link();
        echo $OUTPUT->standard_footer_html();
        ?>
    </div>
    <?php } ?>
</div>
<?php echo $OUTPUT->standard_end_of_body_html() ?>
</body>
</html>

The section of code is responsible for printing the footer for the page.

    <?php if (empty($PAGE->layout_options['nofooter'])) { ?>
    <div id="page-footer" class="clearfix">
        <p class="helplink"><?php echo page_doc_link(get_string('moodledocslink')) ?></p>
        <?php
        echo $OUTPUT->login_info();
        echo $OUTPUT->home_link();
        echo $OUTPUT->standard_footer_html();
        ?>
    </div>
    <?php } ?>

The first thing we do before printing the footer is check that we actually want to print it. This is done by checking the options for the layout as defined in the config.php file. If nofooter => true is set the we don't want to print the footer and should skip over this body of code.

Assuming we want to print a footer we proceed to create a div to house its content and then print the bits of the content that will make it up. There are four things that the typical page footer will want to print:

echo page_doc_link(get_string('moodledocslink'))
This will print a link to the Moodle.org help page for this particular page.
echo $OUTPUT->login_info();
This is the same as at the top of the page and will print the login information for the current user.
echo $OUTPUT->home_link();
This prints a link back to the Moodle home page for this site.
echo $OUTPUT->standard_footer_html();
This prints special HTML that is determined by the settings for the site. Things such as performance information or debugging will be printed by this line if they are turned on.

And the final line of code for our layout file is:

<?php echo $OUTPUT->standard_end_of_body_html(); ?>

This is one of the most important lines of code in the layout file. It asks Moodle to print any required content into the page, and there will likely be a lot although most of it will not be visual.

It will instead be things such as inline scripts and JavaScript files required to go at the bottom of the page. If you forget this line its likely no JavaScript will work!

We've now written our layout file and it is all set to go. The complete source is below for reference. Remember if you want more practical examples simply look at the layout files located within the layout directory of other themes.

<?php
$hassidepre = $PAGE->blocks->region_has_content('side-pre', $OUTPUT);
$hassidepost = $PAGE->blocks->region_has_content('side-post', $OUTPUT);
echo $OUTPUT->doctype() ?>
<html <?php echo $OUTPUT->htmlattributes() ?>>
<head>
    <title><?php echo $PAGE->title; ?></title>
    <link rel="shortcut icon" href="<?php echo $OUTPUT->pix_url('favicon', 'theme')?>" />
    <?php echo $OUTPUT->standard_head_html() ?>
</head>

<body id="<?php p($PAGE->bodyid); ?>" class="<?php p($PAGE->bodyclasses); ?>">
<?php echo $OUTPUT->standard_top_of_body_html() ?>
<div id="page">
<?php if ($PAGE->heading || (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar())) { ?>
    <div id="page-header">
        <?php if ($PAGE->heading) { ?>
        <h1 class="headermain"><?php echo $PAGE->heading ?></h1>
        <div class="headermenu"><?php
            echo $OUTPUT->login_info();
            if (!empty($PAGE->layout_options['langmenu'])) {
                echo $OUTPUT->lang_menu();
            }
            echo $PAGE->headingmenu
        ?></div><?php } ?>
        <?php if (empty($PAGE->layout_options['nonavbar']) && $PAGE->has_navbar()) { ?>
            <div class="navbar clearfix">
                <div class="breadcrumb"><?php echo $OUTPUT->navbar(); ?></div>
                <div class="navbutton"> <?php echo $PAGE->button; ?></div>
            </div>
        <?php } ?>
    </div>
<?php } ?>

    <div id="page-content">
        <div id="region-main-box">
            <div id="region-post-box">
                <div id="region-main-wrap">
                    <div id="region-main">
                        <div class="region-content">
                            <?php echo core_renderer::MAIN_CONTENT_TOKEN ?>
                        </div>
                    </div>
                </div>
                <?php if ($hassidepre) { ?>
                <div id="region-pre">
                    <div class="region-content">
                        <?php echo $OUTPUT->blocks_for_region('side-pre') ?>
                    </div>
                </div>
                <?php } ?>
                
                <?php if ($hassidepost) { ?>
                <div id="region-post">
                    <div class="region-content">
                        <?php echo $OUTPUT->blocks_for_region('side-post') ?>
                    </div>
                </div>
                <?php } ?>
            </div>
        </div>
    </div>

    <?php if (empty($PAGE->layout_options['nofooter'])) { ?>
    <div id="page-footer" class="clearfix">
        <p class="helplink"><?php echo page_doc_link(get_string('moodledocslink')) ?></p>
        <?php
        echo $OUTPUT->login_info();
        echo $OUTPUT->home_link();
        echo $OUTPUT->standard_footer_html();
        ?>
    </div>
    <?php } ?>
</div>
<?php echo $OUTPUT->standard_end_of_body_html() ?>
</body>
</html>

Adding some CSS

With config.php and standard.php both complete the theme is now usable and starting to look like a real theme, however if you change to it using the theme selector you will notice that it still lacks any style.

This of course is where CSS comes in. When writing code Moodle developers are strongly encouraged to not use inline styles anywhere. This is fantastic for us as themers because there is nothing (or at least very little) in Moodle that cannot be styled using CSS.

Moodle CSS basics

In Moodle 2.0 all of the CSS for the whole of Moodle is delivered all of the time! This was done for performance reasons. Moodle now reads in all of the CSS, combines it into one file, shrinks it removing any white space, caches it, and then delivers it.

What this means for you as a themer?

You will need to write good CSS that won't clash with any other CSS within Moodle.

Moodle is so big and complex,there is no way to ensure that classes don't get reused. What we can control however is the classes and id that get added to the body tag for every page. When writing CSS it is highly encouraged to make full use of these body classes, using them will help ensure the CSS you write has the least chance of causing conflicts.

You should also take the time to look at how the Moodle themes use CSS. Look at their use of the body classes and how they separate the CSS for the theme into separate files based on the region of Moodle it applies to.

Check out Themes 2.0 and CSS coding style for more information about writing good CSS.

Starting to write excitement.css

a {
    text-decoration: none;
}

.addcoursebutton .singlebutton {
    text-align: center;
}

h1.headermain {
    color: #fff;
}

h2.main {
    border-bottom: 3px solid #013D6A;
    color: #013D6A;
    text-align: center;
}

h2.headingblock {
    font-size: 18pt;
    margin-top: 0;
    background-color: #013D6A;
    color: #FFF;
    text-align: center;
}

#page-header {
    background-color: #013D6A;
}

#page-header .headermenu {
    color: #FFF;
}

#page-header .headermenu a {
    color: #FDFF2A;
}

.navbar {
    padding-left: 1em;
}

.breadcrumb li {
    color: #FFF;
}

.breadcrumb li a {
    color: #FFF;
}

.block {
    background-color: #013D6A;
}

.block .header .title {
    color: #FFF;
}

.block .header .title .block_action input {
    background-color: #FFF;
}

.block .content {
    border: 1px solid #000;
    padding: 5px;
    background-color: #FFF;
}

.block .content .block_tree p {
    font-size: 80%;
}

.block_settings_navigation_tree .content .footer {
    text-align: center;
}

.block_settings_navigation_tree .content .footer .adminsearchform {
    margin-left: 5%;
    width: 90%;
    font-size: 9pt;
}

.block_settings_navigation_tree .content .footer .adminsearchform #adminsearchquery {
    width: 95%;
}

.block_calendar_month .content .calendar-controls a {
    color: #013D6A;
    font-weight: bold;
}

.block_calendar_month .content .minicalendar td {
    border-color: #FFF;
}

.block_calendar_month .content .minicalendar .day {
    color: #FFF;
    background-color: #013D6A;
}

.block_calendar_month .content .minicalendar .day a {
    color: #FFF000;
}

.block_calendar_month .content .minicalendar .weekdays th {
    border-width: 0;
    font-weight: bold;
    color: #013D6A;
}

.block_calendar_month .content .minicalendar .weekdays abbr {
    border-width: 0;
    text-decoration: none;
}
Excitement theme screenshot

This isn't all of the CSS for the theme, but just enough to style the front page when the user is not logged in. Remember this theme extends the base theme so there is already CSS for layout as well.

Note:

  • The CSS is laid out in a single line format. This is done within the core themes for Moodle. It makes it quicker to read the selectors and see what is being styled.
  • I have written my selectors to take into account the structure of the HTML (more than just the one tag I want to style). This helps further to reduce the conflicts that I may encounter.
  • I use generic classes like .sideblock only where I want to be generic, as soon as I want to be specific I use the unique classes such as .block_calendar_month


Using images within CSS

I will add two image files to the pix directory of my theme:

/theme/excitement/pix/background.png
This will be the background image for my theme.
/theme/excitement/pix/gradient.jpg
This will be a gradient I use for the header and headings.

I quickly created both of these images using gimp and simply saved them to the pix directory.

html {
    background-image: url([[pix:theme|background]]);
}

h2.headingblock,
#page-header,
.sideblock,
.block_calendar_month .content .minicalendar .day {
    background-image: url([[pix:theme|gradient]]);
    background-repeat: repeat-x;
    background-color: #0273C8;
}
Excitement theme screenshot

The CSS above is the two new rules that I had to write to use my images within CSS.

The first rule sets the background image for the page to background.png

The second rule sets the background for headings, and the sideblocks to use gradient.jpg

You will notice that I did not need to write a path to the image. This is because Moodle has this special syntax that can be used and will be replaced when the CSS is parsed before delivery. The advantage of using this syntax over writing the path in is that the path may change depending on where you are or what theme is being used.

Other themers may choose to extend your theme with their own; if you use this syntax then all they need to do to override the image is to create one with the same name in their themes directory.

You will also notice that I don't need to add the image files extension. This is because Moodle is smart enough to work out what extension the file uses. It also allows themers to override images with different formats.


The following is the complete CSS for my theme:

a {text-decoration: none;}
.addcoursebutton .singlebutton {text-align: center;}

h1.headermain {color: #fff;}
h2.main {border-bottom: 3px solid #013D6A;color: #013D6A;text-align: center;}
h2.headingblock {font-size: 18pt;margin-top: 0;background-color: #013D6A;color: #FFF;text-align: center;}
#page-header {background-color: #013D6A;border-bottom:5px solid #013D6A;}
#page-header .headermenu  {color: #FFF;}
#page-header .headermenu a {color: #FDFF2A;}

.sideblock {background-color: #013D6A;}
.sideblock .header .title {color: #FFF;}
.sideblock .header .title .block_action input {background-color: #FFF;}
.sideblock .content {border: 1px solid #000;padding: 5px;background-color: #FFF;}
.sideblock .content .block_tree p {font-size: 80%;}

.block_settings_navigation_tree .content .footer {text-align: center;}
.block_settings_navigation_tree .content .footer .adminsearchform {margin-left: 5%;width: 90%;font-size: 9pt;}
.block_settings_navigation_tree .content .footer .adminsearchform #adminsearchquery {width: 95%;}

.block_calendar_month .content .calendar-controls a {color: #013D6A;font-weight: bold;}
.block_calendar_month .content .minicalendar td {border-color: #FFF;}
.block_calendar_month .content .minicalendar .day {color: #FFF;background-color: #013D6A;}
.block_calendar_month .content .minicalendar .day a {color: #FFF000;}
.block_calendar_month .content .minicalendar .weekdays th {border-width: 0;font-weight: bold;color: #013D6A;}
.block_calendar_month .content .minicalendar .weekdays abbr {border-width: 0;text-decoration: none;}

html {background-image:url([[pix:theme|background]]);}

h2.headingblock,
#page-header,
.sideblock,
.block_calendar_month .content .minicalendar .day {background-image:url([[pix:theme|gradient]]);
   background-repeat:repeat-x;background-color: #0273C8;}

Adding a screenshot and favicon

The final thing to do at this point is add both a screenshot for this theme as well as a favicon for it. The screenshot will be shown in the theme selector screen and should be named screenshot.jpg. The favicon will be used when someone bookmarks this page. Both images should be located in your themes pix directory as follows:

  • /theme/excitement/pix/screenshot.jpg
  • /theme/excitement/pix/favicon.ico

In the case of my theme I have taken a screenshot and added it to that directory, and because I don't really want to do anything special with the favicon I have copied it from /theme/base/pix/favicon.ico so that at least it will be recognisable as Moodle.