Theming forms: system_themes
Last modified: February 19, 2008 - 03:55
This function demonstrates how to make use of multiple checkboxes, theming of form elements in a table, and a seperate theming function which allows for rendering of the table and inline HTML elements.
Before
<?php
function system_themes() {
system_listing_save();
$form = system_theme_listing();
$form .= form_submit(t('Save configuration'));
print theme('page', form($form));
}
function system_theme_listing() {
$themes = system_theme_data();
ksort($themes);
foreach ($themes as $info) {
$info->screenshot = dirname($info->filename) . '/screenshot.png';
$row = array();
// Screenshot column.
$row[] = file_exists($info->screenshot) ? theme('image', $info->screenshot, t('Screenshot for %theme theme', array('%theme' => $info->name)), '', 'class="screenshot"', false) : t('no screenshot');
// Information field.
$row[] = "<strong>$info->name</strong><br /><em>" . dirname($info->filename) . '</em>';
// enabled, default, and operations columns
$row[] = array('data' => form_checkbox('', 'status]['. $info->name, 1, $info->status), 'align' => 'center');
$row[] = array('data' => form_radio('', 'theme_default', $info->name, (variable_get('theme_default', 'bluemarine') == $info->name) ? 1 : 0), 'align' => 'center');
if (function_exists($info->prefix . '_settings') || function_exists($info->prefix . '_features')) {
$row[] = array('data' => l(t('configure'), 'admin/themes/settings/' . $info->name), 'align' => 'center');
}
else {
$row[] = '';
}
$rows[] = $row;
}
$header = array(t('Screenshot'), t('Name'), t('Enabled'), t('Default'), t('Operations'));
$output = form_hidden('type', 'theme');
$output .= theme('table', $header, $rows);
return $output;
}
?>After
<?php
function system_themes() {
$themes = system_theme_data();
ksort($themes);
foreach ($themes as $info) {
$info->screenshot = dirname($info->filename) . '/screenshot.png';
$screenshot = file_exists($info->screenshot) ? theme('image', $info->screenshot, t('Screenshot for %theme theme', array('%theme' => $info->name)), '', array('class' => 'screenshot'), false) : t('no screenshot');
### Note both the use of '#markup' to drop the screenshot info into the form, and
### the grouping of the screenshot/description under the theme name in the form array
$form[$info->name]['screenshot'] = array('#type' => 'markup', '#value' => $screenshot);
### Use of a form item. Notice how '#value' is used, because the value is not editable
### by a user
$form[$info->name]['description'] = array(
'#type' => 'item',
'#title' => $info->name,
'#value' => dirname($info->filename),
);
### Here the options array for all checkboxes is built. Notice that the theme name is used
### for the key--this will be important later when the form is themed
$options[$info->name] = '';
### Here the status array is built conditionally--only checkboxes that are checked are
### added to this array. Notice in this array the theme name is put in the element's value,
### unlike the $options array
if ($info->status) {
$status[] = $info->name;
}
if ($info->status && (function_exists($info->prefix . '_settings') || function_exists($info->prefix . '_features'))) {
### Note that links can also be included in a markup element. Markup can hold any
### kind of markup that needs to get into the form
$form[$info->name]['operations'] = array(
'#type' => 'markup',
'#value' => l(t('configure'),
'admin/themes/settings/' . $info->name),
);
}
else {
// Dummy element for form_render. Cleaner than adding a check in the theme function.
$form[$info->name]['operations'] = array();
}
}
### Now that all checkbox options have been built, and all checked boxes are know, the
### checkboxes element can be declared. Notice that the $status array is dropped directly
### into '#default_value'
$form['status'] = array(
'#type' => 'checkboxes',
'#options' => $options,
'#default_value' => $status,
);
### Radio button groups are built the same basic way, using the same $options array in
### this case
$form['theme_default'] = array(
'#type' => 'radios',
'#options' => $options,
'#default_value' => variable_get('theme_default', 'bluemarine')
);
### Notice that the two submit buttons are grouped under a 'buttons' group in the form array
### This will be important when we examine the execute function
$form['buttons']['submit'] = array('#type' => 'submit', '#value' => t('Save configuration') );
$form['buttons']['reset'] = array('#type' => 'submit', '#value' => t('Reset to defaults') );
### Drop the form array into the master form function
return drupal_get_form('system_themes', $form);
}
?>In this case, the theming of the form is fairly complex, so a custom theming function is used. Custom theme functions are declared by prepending theme_ to the form_id of the form you wish to theme. The single arg is the constructed form array.
<?php
function theme_system_themes($form) {
### The constructed form array has a number of internal record-keeping elements, so directly
### looping through the array would result in errors. The element_children function
### extracts only those form elements that have a value.
foreach (element_children($form) as $key) {
### Here the table rows are constructed using the previous convention of a rows array.
### Notice that there is a check to make sure that the particular form array element
### is has valid theme info in it, otherwise an empty row is added
$row = array();
if (is_array($form[$key]['description'])) {
### In order to manually render a portion of the form, form_render is called. It's
### single argument is the section of the form array that is to be rendered. Note
### that form_render is recursive--it will render all form array elements in the portion
### of the array that you declare.
$row[] = form_render($form[$key]['screenshot']);
$row[] = form_render($form[$key]['description']);
### $form['status'] is the checkboxes element. Notice that by using $form['status'][$key],
### only the checkbox for the current theme $key gets rendered. If the rendered checkbox
### has a matching value in the above created $status array (which was passed to the
### checkboxes element), then it will be rendered as a checked box
$row[] = array('data' => form_render($form['status'][$key]), 'align' => 'center');
if ($form['theme_default']) {
### The radio buttons are rendered using the same logic as the checkboxes
$row[] = array('data' => form_render($form['theme_default'][$key]), 'align' => 'center');
$row[] = array('data' => form_render($form[$key]['operations']), 'align' => 'center');
}
}
$rows[] = $row;
}
### Now the table is created using the usual theme_table approach
$header = array(t('Screenshot'), t('Name'), t('Enabled'), t('Default'), t('Operations'));
$output = theme('table', $header, $rows);
### The rendering code remembers which form elements have already been rendered--therefore,
### to render any remaining elements (in this case the submit buttons), simply call form_render
### using the entire form array, and only unrendered elements will be rendered. It's good
### practice to always end with this, in case other modules may have used form_alter to
### include additional form elements
$output .= form_render($form);
### Finally, the constructed output is returned in the standard fashion.
return $output;
}
?>A custom submit function also exists for this form. Note that the form is executed before it is themed/displayed.
<?php
function system_themes_submit($form_id, $values) {
db_query("UPDATE {system} SET status = 0 WHERE type = 'theme'");
### $_POST['op'] can be examined just as before to determine which button was pressed
if ($_POST['op'] == t('Save configuration')) {
if (is_array($values['status'])) {
### Only those checkboxes that were checked are returned in the processed form array
foreach ($values['status'] as $key => $choice) {
if ($choice) {
// If theme status is being set to 1 from 0, initialize block data for this theme if necessary.
if (db_num_rows(db_query("SELECT status FROM {system} WHERE type = 'theme' AND name = '%s' AND status = 0", $key))) {
system_initialize_theme_blocks($key);
}
db_query("UPDATE {system} SET status = 1 WHERE type = 'theme' and name = '%s'", $key);
}
}
}
### Likewise, only the selected radio button's value is available in the processed form,
### so it can be used to set the default
variable_set('theme_default', $values['theme_default']);
}
else {
variable_del('theme_default');
}
drupal_set_message(t('The configuration options have been saved.'));
### Redirecting back to the page, which will reload the form with the updated data
drupal_goto('admin/themes');
}
?>