Jump to:
| Project: | Drupal core |
| Version: | 6.6 |
| Component: | theme system |
| Category: | support request |
| Priority: | normal |
| Assigned: | Unassigned |
| Status: | closed (fixed) |
Issue Summary
Ok... I thought It was gonna be easy but...
I will be as descriptive as possible, with hopes that someone understands what my problem REALLY is:
I wanted to create another right sidebar region in Drupal 6, in the Zen theme. So:
1) I added a new region in my zen theme's .info file, called right_2 so that it seems like this:
regions[left] = left sidebar
regions[right] = right sidebar
regions[right_2] = right sidebar 2 /* this is the region that I added */
regions[navbar] = navigation bar
regions[content_top] = content top
regions[content_bottom] = content bottom
regions[header] = header
regions[footer] = footer
regions[closure_region] = closureThen I added the code in page.tpl.php :
<?php if ($right_2): ?>
<div id="sidebar-right"><div id="sidebar-right-inner" class="region region-right">
<?php print $right_2; ?>
</div></div> <!-- /#sidebar-right-inner, /#sidebar-right -->
<?php endif; ?>So far, so good, since I was able to place a block into the newly added region, and it "showed" it in the page... but....... not the way I was expecting it to...
Just by creating a new region in Drupal, Drupal doesn't understand that this region is a "side bar", and thus, it doesn't automatically create the two-sidebars body class in the html markup, and therefore I cannot easily style it.
For example, when I use the regular left sidebar, and right sidebar regions, Drupal prints:
<body class="not-front logged-in page-admin two-sidebars page-admin-build-block section-admin">...from the page.tpl.php's $body_class line:
<body class="<?php print $body_classes; ?>">BUT, when I use (assign a block to) my newly created region right sidebar 2 , instead of the default right sidebar , for instance, AND along with the left sidebar , it only prints:
<body class="front logged-in page-home one-sidebar sidebar-left">It is clear that Drupal is not recognizing that my added region is another sidebar.
It doesn't matter where I put the new region code in page.tpl.php, it doesn't recognize it as a side bar.
This, of course, brings a lot of more troubles when trying to style my theme, since I cannot take advantage of a class like:
.two-sidebars #content
{
width: 560px;
margin-left: 200px; /* The width of #sidebar-left */
margin-right: -760px; /* Negative value of #content's width + left margin. */
}in my layout.css
This class, is highly important, as you may see: the #content id is a very important part of the layout.
Is this to be considered as a bug?
For example; I just noticed that under /admin/build/block page, it does print the class two-sidebars:
<body class="not-front logged-in page-admin two-sidebars page-admin-build-block section-admin tableHeader-processed">Is there any way I can tell drupal my right sidebar 2 is to be taken into account to print two-sidebars class in ?
Thank you.
Comments
#1
Those classes are set in http://api.drupal.org/api/function/template_preprocess_page/6. Look at the code that defines $variables['layout'] and $body_classes (which gets transformed into $variables['body_classes']).
In your theme's template.php you could use function mytheme_preprocess_page() to override these values. Simply adding a relevant class if you have 2 right sidebars might be the easiest thing to do and straightforward to pick up with an additional CSS selector in your stylesheet. Also look at how the contrib aquamarine theme does it - this outputs a body class something like region "layout-first-main-last" which would mean we have left sidebar, main content area, and right sidebar. Might give you some ideas).
#2
Ok... with your help, I am beggining to understand it (bear it in mind that I am not at all an experienced programmer).
1) $body_classes seems to be an array, that takes values based on the following preprocess function, found in the Zen Theme's template.php file:
/**
* Override or insert variables into the page templates.
*
* @param $vars
* An array of variables to pass to the theme template.
* @param $hook
* The name of the template being rendered ("page" in this case.)
*/
function zen_preprocess_page(&$vars, $hook) {
// Add an optional title to the end of the breadcrumb.
if (theme_get_setting('zen_breadcrumb_title') && $vars['breadcrumb']) {
$vars['breadcrumb'] = substr($vars['breadcrumb'], 0, -6) . $vars['title'] . '';
}
// Add conditional stylesheets.
if (!module_exists('conditional_styles')) {
$vars['styles'] .= $vars['conditional_styles'] = variable_get('conditional_styles_' . $GLOBALS['theme'], '');
}
// Classes for body element. Allows advanced theming based on context
// (home page, node of certain type, etc.)
$body_classes = array($vars['body_classes']);
if (!$vars['is_front']) {
// Add unique classes for each page and website section
$path = drupal_get_path_alias($_GET['q']);
list($section, ) = explode('/', $path, 2);
$body_classes[] = zen_id_safe('page-' . $path);
$body_classes[] = zen_id_safe('section-' . $section);
if (arg(0) == 'node') {
if (arg(1) == 'add') {
if ($section == 'node') {
array_pop($body_classes); // Remove 'section-node'
}
$body_classes[] = 'section-node-add'; // Add 'section-node-add'
}
elseif (is_numeric(arg(1)) && (arg(2) == 'edit' || arg(2) == 'delete')) {
if ($section == 'node') {
array_pop($body_classes); // Remove 'section-node'
}
$body_classes[] = 'section-node-' . arg(2); // Add 'section-node-edit' or 'section-node-delete'
}
}
}
if (theme_get_setting('zen_wireframes')) {
$body_classes[] = 'with-wireframes'; // Optionally add the wireframes style.
}
$vars['body_classes'] = implode(' ', $body_classes); // Concatenate with spaces
}
2) I have to copy and paste this function, into my subtheme folder's template.php file, and change its name to:
function subthemename_preprocess_page(&$vars, $hook) {
3) Now... looking at this code, I really don't see any reference, to the origination of the two-sidebars class, or other classes related to the number of sidebars in the page.
This, makes me think of the other thing you mention in your message: looking at the Core preprocess API.
4) The two aspects mentioned in includes/theme.inc, that you refered to, are:
// Set up layout variable.$variables['layout'] = 'none';
if (!empty($variables['left'])) {
$variables['layout'] = 'left';
}
if (!empty($variables['right'])) {
$variables['layout'] = ($variables['layout'] == 'left') ? 'both' : 'right';
}
and
// Add information about the number of sidebars.if ($variables['layout'] == 'both') {
$body_classes[] = 'two-sidebars';
}
elseif ($variables['layout'] == 'none') {
$body_classes[] = 'no-sidebars';
}
else {
$body_classes[] = 'one-sidebar sidebar-'. $variables['layout'];
}
// Implode with spaces.
$variables['body_classes'] = implode(' ', $body_classes);
5) With these elements of the recipe, what kind of information should I include in the overriding function :
function subthemename_preprocess_page(&$vars, $hook) {
etc...
??
6) How can I, for example, force $body_classes array, to have a value? What should the function look like?
Thank you.
#3
You will see that the Zen preprocessor gets the existing body classes
$body_classes = array($vars['body_classes']);and then adds some more. You could do something similar, by checking for non-empty 2nd right sidebar.
These pages http://drupal.org/node/223430 and http://drupal.org/node/225125 suggest that the zen preprocessor will be run for the subtheme. This would be worth checking (the documentation is slightly unclear to my eye), but if so you wouldn't need to replicate the code there already, just add your own custom bit.
#4
Ok... after a lot of trial and error, this overriding function I added to template.php in my subtheme folder....
function mysubtheme_preprocess_page(&$vars, $hook) {
// Zen Theme's template.php
// Classes for body element. Allows advanced theming based on context
// (home page, node of certain type, etc.)
$body_classes = array($vars['body_classes']); // Commented Out
if (!$vars['is_front']) {
// Add unique classes for each page and website section
$path = drupal_get_path_alias($_GET['q']);
list($section, ) = explode('/', $path, 2);
$body_classes[] = zen_id_safe('page-' . $path);
$body_classes[] = zen_id_safe('section-' . $section);
if (arg(0) == 'node') {
if (arg(1) == 'add') {
if ($section == 'node') {
array_pop($body_classes); // Remove 'section-node'
}
$body_classes[] = 'section-node-add'; // Add 'section-node-add'
}
elseif (is_numeric(arg(1)) && (arg(2) == 'edit' || arg(2) == 'delete')) {
if ($section == 'node') {
array_pop($body_classes); // Remove 'section-node'
}
$body_classes[] = 'section-node-' . arg(2); // Add 'section-node-edit' or 'section-node-delete'
}
}
}
if (theme_get_setting('zen_wireframes')) {
$body_classes[] = 'with-wireframes'; // Optionally add the wireframes style.
}
// Taken from Drupal API theme.inc; Changed "$variables" for "$vars"
// Set up layout variable.
$vars['layout'] = 'none';
if (!empty($vars['left'])) {
$vars['layout'] = 'left';
}
if (!empty($vars['right_2'])) {
$vars['layout'] = ($vars['layout'] == 'left') ? 'both' : 'right_2';
}
// Add information about the number of sidebars.
if ($vars['layout'] == 'both') {
$body_classes[] = 'two-sidebars';
}
elseif ($vars['layout'] == 'none') {
$body_classes[] = 'no-sidebars';
}
else {
$body_classes[] = 'one-sidebar sidebar-'. $vars['layout'];
}
// Implode with spaces.
$vars['body_classes'] = implode(' ', $body_classes);
... the following body classes:
<body class="front logged-in page-home one-sidebar sidebar-left two-sidebars tableHeader-processed">However, as it can be seen in the body classes, I haven't been able to get rid of the class "one-sidebar". Altought my css two-sidebars is overriding it when it comes to content styling, I still wonder how could I tell drupal not to include it in my body classes?
On thing I did notice is that the underlying (core and zen theme) preprocess functions don't scale as I would expect; so I had to add a big ammount of code.
Thank you for your help.
L
#5
Sounds like you are making good progress.
>On thing I did notice is that the underlying (core and zen theme) preprocess functions don't scale as I would expect
Are you saying that zen_preprocess_page doesn't get called? If it did then that would improve the "scaling" since a lot of the code in your preprocess is duplication (I presume this is what you were talking about). You can tell by checking the content of $vars[] in your own preprocess hook as passed from the theme system.
You should also find that $vars['body_classes'] includes 'one-sidebar' already - it will have been set by http://api.drupal.org/api/function/template_preprocess_page/6, so you just need to strip that string out.
It takes a while to grok the themeing system but at the end of the day it handles *most* things pretty well.
#6
Ok, here's where I stand at this point. I will say in detail what I have done.
Note 1) "plc_theme" is the name of my Zen's subtheme
Note 2) Some of my own insights are included in the code, with // comments
1) in plc_theme_template.php
function plc_theme_preprocess_page(&$vars, $hook) {
// Bear it in mind that when page.tpl.php prints <body class="<?php print $body_classes; etc..
// it is still printing clases preprocessed in function template_preprocess_page(&$variables) in includes/theme.inc
// So I have to make sure that in my .css, the new classes introduced in this function
// override the previous classes such as one-sidebar and sidebar-left
// This $body_class call is taken from zen's template.php,
// and it calls the body_classes that have been created in the Core API found in /includes/theme.inc
// which includes classes like "front" or "not-front"
// AND it also seems, that at this point of function overriding, it is also taking values from
// the Zen theme_preprocess_page function, now that it is adding classes like
// section-node-add, which are not found in theme.inc
$body_classes = array($vars['body_classes']);
// This is taken and adapted from Drupal API theme.inc
// Changed "$variables" for "$vars"
// After calling the $body_classes variable in the previous step, this code below adds another class
// when region_2 has been used by a block.
// Bear it in mind that when page.tpl.php prints <body class="<?php print $body_classes; etc..
// It is still printing clases preprocessed in function template_preprocess_page(&$variables) in includes/theme.inc
// So I have to make sure that in my .css, the new classes here, below, override the previous classes
// such as one-sidebar and sidebar-left
// Set up layout variable.
// When having two right side bars: right and right_2
$vars['layout'] = 'none';
if (!empty($vars['right'])) {
$vars['layout'] = 'right';
}
if (!empty($vars['right_2'])) {
$vars['layout'] = ($vars['layout'] == 'right') ? 'both' : 'right_2';
}
// Add information about the number of sidebars.
if ($vars['layout'] == 'both') {
$body_classes[] = 'two-right-sidebars'; // adds the class two-right-sidebars
}
elseif ($vars['layout'] == 'none') {
$body_classes[] = 'no-sidebars';
}
else {
$body_classes[] = 'one-sidebar sidebar-'. $vars['layout'];
}
// Implode with spaces.
$vars['body_classes'] = implode(' ', $body_classes);
}
2) in page.tpl.php
<?php if ($right_2): ?><div id="sidebar-right-2"><div id="sidebar-right-2-inner" class="region region-right-2">
<?php print $right_2; ?>
</div></div> <!-- /#sidebar-right-2-inner, /#sidebar-right-2 -->
<?php endif; ?>
Result so far:
The body class that I am getting at this point (example):
<body class="not-front logged-in page-admin one-sidebar sidebar-left page-admin section-admin <strong>two-right-sidebars</strong>">Notice that it still prints one-sidebar and sidebar-left. BUT it also prints my so desired .two-right-sidebars class.
So, I noticed that when working out the .css of this body class output, I can put the code of .two-right-sidebars #content below the code .one-sidebar #content in my layout.css file. So this means that in a .ccs file, the code that appears below, seems to override the code above. So...
3) in layout.css added, below the code .two-sidebars #content
.two-right-sidebars #content
{
width: 560px;
margin-left: 000px; /* The width of #sidebar-left */
margin-right: -560px; /* Negative value of #content's width + left margin. */
}
4) in layout.css changed
#sidebar-right{
float: left;
width: 200px;
margin-left: 760px; /* Width of content + sidebar-left. */
margin-right: -960px; /* Negative value of #sidebar-right's width + left margin. */
padding: 0; /* DO NOT CHANGE. Add padding or margin to #sidebar-right-inner. */
}
To
#sidebar-right{
float: left;
width: 200px;
margin-left: 560px; /* Width of content + sidebar-left. */
margin-right: -760px; /* Negative value of #sidebar-right's width + left margin. */
padding: 0; /* DO NOT CHANGE. Add padding or margin to #sidebar-right-inner. */
}
And added, below /** sidebar-right **/
/** sidebar-right-2 **/
#sidebar-right-2
{
float: left;
width: 200px;
margin-left: 760px; /* Width of content + sidebar-left. */
margin-right: -960px; /* Negative value of #sidebar-right's width + left margin. */
padding: 0; /* DO NOT CHANGE. Add padding or margin to #sidebar-right-inner. */
}
#sidebar-right-2-inner
{
margin: 0 0 0 20px;
padding: 0;
}
This is getting me the desired result: An region called right_2 to be displayed nicely along with the default region right
*****
Indeed. I was mistaken when I said that other template_preprocess stages were not being called... because I could in the end verify that they were being called.
Now, how I verified it was just by assumption: seeing what was printed in
<body class="... etc">, and comparing with all the preprocess functions, both of Drupal API, and the Zen theme.A) What I would like to know is *how* I could "check the content of $vars[] in my own preprocess hook as passed from the theme system". What could I exactly do to know this?
An idea that comes to my mind is by temporarily printing the array at the header or footer in my page.tpl.php.
B) You are right when you notice that "$vars['body_classes'] includes 'one-sidebar' already". This is exactly why I had to write, in my "self-tutorial", all that stuff about css overriding code from "above" with code form "below".
It would be much more convenient to "strip that [one-sidebar class] out". How could I do this?
Thank you again for following up on my issue.
#7
A) From within your preprocess hook you could use http://api.drupal.org/api/function/drupal_set_message/6 to output the content of $vars['body_classes']; but note that the body classes displayed on a given page view would refer to those applicable to the *previous* page view (this is a "timing issue" caused by the fact that the $messages variable has already been populated, in http://api.drupal.org/api/function/template_preprocess_page/6, by the time your hook gets invoked. To get round this you could also temporarliy add
$vars['messages'] .= theme('status_messages');to your preprocess hook).B) Easy, if crude -
$vars['body_classes'] = str_replace('one-sidebar ', '', $vars['body_classes']);#8
After some days of having written this support request, I found another interesting approach: the one found in the Newswire theme:
In template.php
function newswire_column_count_class($left, $content, $right, $right_2) {if ($content && $left && $right && $right_2) {
$class = 'four-column'; // all four columns active
}
else {
if (($content && $left && $right) or ($content && $left && $right_2) or ($content && $right && $right_2)) {
$class = 'three-column'; // three columns active
}
else {
if (($content && $left) or ($content && $right) or ($content && $right_2)) {
$class = 'two-column'; // two columns active
}
else {
if ($content != '') {
$class = 'one-column'; //one column (content itelf) active
}
}
}
}
if (isset($class)) {
print $class;
}
}
and in page.tpl.php
<body class="<?php print newswire_column_count_class($left, $content, $right, $right_2) . $body_class; ?>">Simple... and just fine!
Regards.
#9