How to do taxonomy based css styling using drupal template.php? - css

I'm looking forward to implement taxonomy terms based css styling; especially for the body tag which i want to add the current terms.
For now, here's what i have so far :
function _phptemplate_variables($hook, $vars = array()) {
global $node;
switch ($hook) {
case 'page': die ('test');
$vars['body_class'] = '';
if (arg(0) == 'taxonomy' && arg(1) == 'term' && is_numeric(arg(2))) {
$vars['body_class'] = 'theme'.arg(2);
}
if (arg(0) == 'node' && is_numeric(arg(1))) {
$node = node_load(arg(1));
if (is_array($node->taxonomy)) {
foreach ($node->taxonomy as $term) {
$vars['body_class'] .= 'theme'.$term->tid;
}
}
}
if (drupal_is_front_page()) {
$vars['body_class'] .= ' front';
}
break;
}
return $vars;
}
I think the code is OK, but it never get called (see my 'die' function); using simple phptemplate engine and minimal drupal6 install.
What obviousity am I missing here ?

/**
* Override or insert PHPTemplate variables into the templates.
*/
function phptemplate_preprocess_page(&$vars) {
//
}
/**
* Override or insert PHPTemplate variables into the templates.
*/
function phptemplate_preprocess_node(&$vars) {
//
}

Well, years ago we wrote this up http://openconcept.ca/blog/jmlane/taxonomy_specific_css
It would need to be updated I expect for Drupal 6 or 7 but the principals still apply.

For Drupal 7 a more elegant solution might be to use the Context module. See the following posting for detailed information and instructions.
Add a body class based on a node's vocabulary term
The Context modules allows for creating arbitrary contexts for specific Drupal pages, e.g. based on taxonomy terms. These contexts can trigger certain reactions, e.g. add a CSS class with the chosen taxonomy term.

Related

different tag using different template file according to the content type?

i want to using the taxonomy page template according to the content type? namely, the tag under content type "story" using a tpl.php, and the tag under the content page, using another tpl.php file. how should i do?thank you.
In template.php of your theme:
/**
* Override or insert variables into the page templates.
*
* #param $vars
* An array of variables to pass to the theme template.
*/
function YOURTHEMENAME_preprocess_page(&$vars) {
// ...
$node = $vars['node'];
if ($node && ($node->type=='story')) {
foreach ($node->taxonomy as $term) {
if ($term->name == 'SOMETAG1') {
$vars['template_files'][] = 'page-sometag1';
break;
}
else if ($term->name == 'SOMETAG2') {
$vars['template_files'][] = 'page-sometag2';
break;
}
}
}
// ...
}
Replace uppercase with your values. page-sometag1.tpl.php and page-sometag2.tpl.php files should be in your theme folder.
The ThemeKey module might do this.
(Not sure it can work with both content-type and taxonomy term in the same condition ; but I've seen it used a couple of times to change templates / themes bases on some conditions)

Drupal hook_form_alter for Taxonomy admin

I created a module to do all my form altering called "form_mods". It's working for most situations but not for the Taxonomy page.
I'm targeting the form id of "taxonomy_overview_vocabularies". I'm trying to hide the link "edit vocabulary" for roles of "webmaster" and "dj".
My code is unsetting the $form array correctly, but Drupal is still displaying the "edit vocabulary" link.
function form_mods_form_alter($form, $form_state, $form_id) {
if($form_id == 'taxonomy_overview_vocabularies'){
global $user;
$hide=0;
$hideArray = array('webmaster', 'dj');
foreach($user->roles AS $key => $value){
if(in_array($value, $hideArray)){
$hide++;
}
}
if($hide){
foreach($form AS $vocab){
//print_r($vocab);
if(isset($vocab['edit']['#value'])){
unset($vocab['edit']['#value']);
}
}
}
}
}
Very small PHP mistake,
when you want to change array members in a for each statement you have to pass them by reference & foreach($form AS &$vocab) otherwise the $vocab would be just a copy of the array
foreach($form AS &$vocab){
//print_r($vocab);
if(isset($vocab['edit']['#value'])){
unset($vocab['edit']['#value']);
}
}
In addition to Amjad's answer, if you don't like using references, I would suggest another alternative:
foreach ($form as $key => $vocab) {
unset($form[$key]['edit']['#value']);
}
This way you avoid using references, and potential issues they may lead to.
Also note I removed the if statement, which is not useful (PHP can figure it out).
An array_map could also be considered.

How to add a body class for the current node's taxonomy to a Drupal 7 theme

Does anyone know how or can guide me in the right direction on how to add a body css class for the current node's taxonomy term? i.e. <body class="term-dogs"> where "dogs" is the taxonomy term name. It could also be just the term ID. Either way is fine I just need a solution. This will be for a Drupal 7 zen sub-theme
This answer took longer than I expected to figure out. The hard part was collecting the terms on the node, since All taxonomy functions relating to nodes have been removed or refactored. Ultimately, page 355 of Pro Drupal 7 Development saved the day with a snippet that does the job previously handled by taxonomy_node_get_terms.
Below is the code that worked for me (look for the part that says "MAGIC BEGINS HERE"). Assuming you're creating a sub-theme of Zen, you'll want to move this to your sub-theme's template.php file and rename it to YOURSUBTHEMENAME_preprocess_html:
/**
* Override or insert variables into the html template.
*
* #param $vars
* An array of variables to pass to the theme template.
* #param $hook
* The name of the template being rendered ("html" in this case.)
*/
function zen_preprocess_html(&$vars, $hook) {
// If the user is silly and enables Zen as the theme, add some styles.
if ($GLOBALS['theme'] == 'zen') {
include_once './' . drupal_get_path('theme', 'zen') . '/zen-internals/template.zen.inc';
_zen_preprocess_html($vars, $hook);
}
// Classes for body element. Allows advanced theming based on context
// (home page, node of certain type, etc.)
if (!$vars['is_front']) {
// Add unique class for each page.
$path = drupal_get_path_alias($_GET['q']);
// Add unique class for each website section.
list($section, ) = explode('/', $path, 2);
if (arg(0) == 'node') {
if (arg(1) == 'add') {
$section = 'node-add';
}
elseif (is_numeric(arg(1)) && (arg(2) == 'edit' || arg(2) == 'delete')) {
$section = 'node-' . arg(2);
}
// MAGIC BEGINS HERE
$node = node_load(arg(1));
$results = field_view_field('node', $node, 'field_tags', array('default'));
foreach ($results as $key => $result) {
if (is_numeric($key)) {
$vars['classes_array'][] = strtolower($result['#title']);
}
}
// MAGIC ENDS HERE
}
$vars['classes_array'][] = drupal_html_class('section-' . $section);
}
if (theme_get_setting('zen_wireframes')) {
$vars['classes_array'][] = 'with-wireframes'; // Optionally add the wireframes style.
}
// Store the menu item since it has some useful information.
$vars['menu_item'] = menu_get_item();
switch ($vars['menu_item']['page_callback']) {
case 'views_page':
// Is this a Views page?
$vars['classes_array'][] = 'page-views';
break;
case 'page_manager_page_execute':
case 'page_manager_node_view':
case 'page_manager_contact_site':
// Is this a Panels page?
$vars['classes_array'][] = 'page-panels';
break;
}
}
I needed to know how to do this and Matt V's solution worked perfectly. I made a couple of additions to his work. I called drupal_html_class which replaces spaces and invalid characters. And I added in the term ID to allow you to target a term even if the name of the term changes.
// MAGIC BEGINS HERE
$node = node_load(arg(1));
$results = field_view_field('node', $node, 'field_tags', array('default'));
foreach ($results as $key => $result) {
if (is_numeric($key)) {
// Call drupal_html_class to make safe for a css class (remove spaces, invalid characters)
$vars['classes_array'][] = "taxonomy-" . strtolower(drupal_html_class( $result['#title']) );
// Add taxonomy ID. This will allow targeting of the taxonomy class even if the title changes
$vars['classes_array'][] = "taxonomy-id-" . $result['#options']['entity']->tid ;
}
}
// MAGIC ENDS HERE
Not sure what you mean with that body tag, but the classes on the node are generated here:
http://api.drupal.org/api/drupal/modules--node--node.module/function/template_preprocess_node/7
You can add more by implementing yourmodule_preprocess_node($vars) and then add whatever you want to $vars['classes_array']

Wordpress custom post type hierarchy and menu highlighting (current_page_parent)

I have created a custom post type of 'portfolio' and page with a template that retrieves all posts matching that custom post type.
The problem is when I drill down into the actual post, the post seems to sit under 'blog' in the main menu highlighting (displays current_page_parent as a class)
The permalink url is correct: www.site.com/portfolio/post-slug
But the menu thinks the parent is 'blog'.
This is obviously a hierarchical issue but I don't know what to do to fix it.
It appears this is an issue with the core Wordpress code; the code that generates the menu classes adds current_page_parent to your Blog page everywhere except when viewing static page templates.
(This has been discussed in passing at http://core.trac.wordpress.org/ticket/13543).
You can however get around this with some custom code using the page_css_class filter. For example, add something along these lines to functions.php (not 100% tested):
function my_page_css_class($css_class, $page) {
if (get_post_type()=='portfolio' || is_page(57)) {
if ($page->ID == get_option('page_for_posts')) {
foreach ($css_class as $k=>$v) {
if ($v=='current_page_parent') unset($css_class[$k]);
}
}
if ($page->ID==57) {
$css_class[]='current_page_parent';
}
}
return $css_class;
}
add_filter('page_css_class','my_page_css_class',10,2);
Replacing 57 with the ID of your portfolios page, of course. That removes current_page_parent when printing the blog page and adds current_page_parent to your portfolios page, when either viewing a single portfolio or viewing the portfolios page itself.
Here is my optimized/extended version of previously suggested solutions, which is pretty much fully automated. No more extra CSS or menu attributes needed.
This version dynamically gets a list of custom post types and if the current post type is a custom post type, then it removes the 'current_page_parent' class from all menu items.
Furthermore it checks each menu item to see if it's for a page with a page template like "page-{custom_post_type_slug}.php", and if so, it'll add the 'current_page_parent' class.
The filter priority is 1, as some themes, replace the current_page_parent/etc. classes with a class like 'active' (eg. 'roots' does this), so this filter needs to execute first.
Lastly, it makes use of 3 static variables since this function is repeatedly called and these (obviously) remain the same through all calls.
function theme_current_type_nav_class($css_class, $item) {
static $custom_post_types, $post_type, $filter_func;
if (empty($custom_post_types))
$custom_post_types = get_post_types(array('_builtin' => false));
if (empty($post_type))
$post_type = get_post_type();
if ('page' == $item->object && in_array($post_type, $custom_post_types)) {
$css_class = array_filter($css_class, function($el) {
return $el !== "current_page_parent";
});
$template = get_page_template_slug($item->object_id);
if (!empty($template) && preg_match("/^page(-[^-]+)*-$post_type/", $template) === 1)
array_push($css_class, 'current_page_parent');
}
return $css_class;
}
add_filter('nav_menu_css_class', 'theme_current_type_nav_class', 1, 2);
PS. Just to point out one shortcoming in all non-CSS solutions I've seen so far, including my own:
Something not taken into account is highlighting the menu item parent/ancestor of an item linking to a page which displays posts of the current custom post type. Consider a custom post type "product" and a menu like:
Home Company News Contact
|
\--About Us
\--Products
"Products" is a page with a template "page-product.php" and shows an overview of posts of type 'product'. It is highlighted due to posted solution. However 'Company' as its parent/ancestor should also be highlighted, but isn't. Something to keep in mind.
WP ticket: http://core.trac.wordpress.org/ticket/16382
function fix_blog_menu_css_class( $classes, $item ) {
if ( is_tax( 'my-cat-tax' ) || is_singular( 'my-post-type' ) || is_post_type_archive( 'my-post-type' ) ) {
if ( $item->object_id == get_option('page_for_posts') ) {
$key = array_search( 'current_page_parent', $classes );
if ( false !== $key )
unset( $classes[ $key ] );
}
}
return $classes;
}
add_filter( 'nav_menu_css_class', 'fix_blog_menu_css_class', 10, 2 );
I did some more looking around on this and found another way of doing this.
add_filter('nav_menu_css_class', 'current_type_nav_class', 10, 2);
function current_type_nav_class($css_class, $item)
{
if (get_post_type() === 'portfolio') {
$current_value = 'current_page_parent';
$css_class = array_filter($css_class, function ($element) use ($current_value) {
return ($element != $current_value);
});
}
$post_type = get_query_var('post_type');
if ($item->attr_title !== '' && $item->attr_title === $post_type) {
array_push($css_class, 'current_page_parent');
};
return $css_class;
}
I got some help form this post and then modified it to also remove the "current_page_parent" class from the blog page.
https://wordpress.stackexchange.com/questions/3014/highlighting-wp-nav-menu-ancestor-class-w-o-children-in-nav-structure/3034#3034
Cordially
Vayu
As explained at https://core.trac.wordpress.org/ticket/16382, .current_page_parent matches "anything that isn't a page" for the sake of backwards compatibility (bear in mind that this was considered backwards 10 years ago...) so themes nowadays really shouldn't still be using it.
So the simplest solution, and the most efficient (since unlike previous answers it doesn't require running extra code on every page load), is to modify your theme's CSS to replace use of the .current_page_parent class selector with .current-menu-parent, which does the right thing. (NB underscores vs hyphens.)
If you are using a third-party theme and don't want to modify it directly, then you can overwrite its properties in your own stylesheet. For example, if your theme has:
.current_page_parent > a {
border-bottom: 4px solid blue;
}
then in your child theme's stylesheet you would do this to cancel out its effects, and apply them to the correct class:
.current_page_parent > a {
border-bottom: transparent !important; /* Cancel out incorrect styling */
}
.current-menu-parent > a {
border-bottom: 4px solid blue; /* Add styling correctly */
}
This is just an example - the correct way will depend on how your theme is styling these links.
Here is a solution that worked for me, without having to define my custom post type or menu id or page id in the code:
http://dtbaker.net/web-development/how-to-stop-wordpress-automatically-highlighting-the-blog-page-in-the-menu/
function dtbaker_wp_nav_menu_objects($sorted_menu_items, $args){
// this is the code from nav-menu-template.php that we want to stop running
// so we try our best to "reverse" this code wp code in this filter.
/* if ( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && empty( $wp_query->is_page ) && $home_page_id == $menu_item->object_id )
$classes[] = 'current_page_parent'; */
// check if the current page is really a blog post.
//print_r($wp_query);exit;
global $wp_query;
if(!empty($wp_query->queried_object_id)){
$current_page = get_post($wp_query->queried_object_id);
if($current_page && $current_page->post_type=='post'){
//yes!
}else{
$current_page = false;
}
}else{
$current_page = false;
}
$home_page_id = (int) get_option( 'page_for_posts' );
foreach($sorted_menu_items as $id => $menu_item){
if ( ! empty( $home_page_id ) && 'post_type' == $menu_item->type && empty( $wp_query->is_page ) && $home_page_id == $menu_item->object_id ){
if(!$current_page){
foreach($sorted_menu_items[$id]->classes as $classid=>$classname){
if($classname=='current_page_parent'){
unset($sorted_menu_items[$id]->classes[$classid]);
}
}
}
}
}
return $sorted_menu_items;
}
add_filter('wp_nav_menu_objects','dtbaker_wp_nav_menu_objects',10,2);

Drupal main theme template file for any node

How can I switch to a different theme template file for any node that I want?
I understand how to create sub-themes like node-recipes.tpl.php for a node that has a path of "recipes". But what I want to have control of the entire base template like page.tpl.php.
Can I use some preprocess function in template.php for this?
Right now I have this in my template.php file:
function mythemename_preprocess_node(&$vars) {
// template name for current node id
$suggestions = array('node-'. $vars['nid']);
// additional node template names based on path alias
if (module_exists('path')) {
// we already can have a path alias
if (isset($vars['path'])) {
$alias = $vars['path'];
}else{
// otherwise do standard check
$alias = drupal_get_path_alias('node/'. $vars['nid']);
}
if ($alias != 'node/'. $vars['nid']) {
$add_path = '';
foreach (explode('/', $alias) as $path_part) {
$add_path .= !empty($path_part) ? $path_part.'_' : '';
$suggestions[] = 'node-'. $add_path;
}
// adding the last one (higher priority) for this path only
// node-some-long-path-nofollow.tpl.php (not for anchestors)
$suggestions[] = end($suggestions) .'-nofollow';
}
$suggestions=array_map(stripTag, $suggestions);
//print_r($suggestions);
}
$vars['template_files'] = isset($vars['template_files']) ? array_merge($vars['template_files'], $suggestions) : $suggestions;
}
thanks
Yes,
You can fully control the $vars['template_files'] array. I always suggest adding onto the array rather then overwriting it completely.
I have a module that I maintain that adds a few small suggestions that I use often.
http://github.com/electblake/template_suggestions/blob/master/template_suggestions.module
You can manipulate the $vars['template_files'] array in preprocess_node, preprocess_page, etc.
If you want to switch you page.tpl.php to another theme file do it in the preprocess_page hook...
I'm using this function to create template suggestions that works for me now. Thanks for the suggestions everyone.
/**
* Override or insert PHPTemplate variables into the templates.
* These are the main outer templates such as page.tpl.php
*/
function phptemplate_preprocess_page(&$vars) {
$alias = drupal_get_path_alias($_GET['q']);
if ($alias != $_GET['q']) {
$template_filename = 'page';
foreach (explode('/', $alias) as $path_part) {
$template_filename = $template_filename . '-' . $path_part;
$vars['template_files'][] = $template_filename;
}
}
//----
//print_r(arg());
/* print '<pre>';
print_r($vars);
print '</pre>';*/
//dpm($vars);
//print_r($vars['template_files']);
}

Resources