Main theme for SuiteDesk

This commit is contained in:
Manuel Cillero 2017-07-25 13:57:02 +02:00
parent 8cf3c56044
commit 42540d1310
185 changed files with 17597 additions and 0 deletions

View file

@ -0,0 +1,722 @@
<?php
/**
* @file
* Contains theme override functions and preprocess functions for the theme.
*
* ABOUT THE TEMPLATE.PHP FILE
*
* The template.php file is one of the most useful files when creating or
* modifying Drupal themes. You can add new regions for block content, modify
* or override Drupal's theme functions, intercept or make additional
* variables available to your theme, and create custom PHP logic. For more
* information, please visit the Theme Developer's Guide on Drupal.org:
* http://drupal.org/theme-guide
*
* OVERRIDING THEME FUNCTIONS
*
* The Drupal theme system uses special theme functions to generate HTML
* output automatically. Often we wish to customize this HTML output. To do
* this, we have to override the theme function. You have to first find the
* theme function that generates the output, and then "catch" it and modify it
* here. The easiest way to do it is to copy the original function in its
* entirety and paste it here, changing the prefix from theme_ to STARTERKIT_.
* For example:
*
* original: theme_breadcrumb()
* theme override: STARTERKIT_breadcrumb()
*
* where STARTERKIT is the name of your sub-theme. For example, the
* zen_classic theme would define a zen_classic_breadcrumb() function.
*
* If you would like to override any of the theme functions used in Zen core,
* you should first look at how Zen core implements those functions:
* theme_breadcrumbs() in zen/template.php
* theme_menu_item_link() in zen/template.php
* theme_menu_local_tasks() in zen/template.php
*
* For more information, please visit the Theme Developer's Guide on
* Drupal.org: http://drupal.org/node/173880
*
* CREATE OR MODIFY VARIABLES FOR YOUR THEME
*
* Each tpl.php template file has several variables which hold various pieces
* of content. You can modify those variables (or add new ones) before they
* are used in the template files by using preprocess functions.
*
* This makes THEME_preprocess_HOOK() functions the most powerful functions
* available to themers.
*
* It works by having one preprocess function for each template file or its
* derivatives (called template suggestions). For example:
* THEME_preprocess_page alters the variables for page.tpl.php
* THEME_preprocess_node alters the variables for node.tpl.php or
* for node-forum.tpl.php
* THEME_preprocess_comment alters the variables for comment.tpl.php
* THEME_preprocess_block alters the variables for block.tpl.php
*
* For more information on preprocess functions and template suggestions,
* please visit the Theme Developer's Guide on Drupal.org:
* http://drupal.org/node/223440
* and http://drupal.org/node/190815#template-suggestions
*/
/**
* Implementation of HOOK_theme().
*/
function zuitedesk_theme(&$existing, $type, $theme, $path) {
$hooks = zen_theme($existing, $type, $theme, $path);
// Add your theme hooks like this:
/*
$hooks['hook_name_here'] = array( // Details go here );
*/
// @TODO: Needs detailed comments. Patches welcome!
return $hooks;
}
/**
* Duplicate of zen_menu_local_tasks() hightlighting the main tab.
*/
function zuitedesk_menu_local_tasks() {
$output = '';
// CTools requires a different set of local task functions.
if (module_exists('ctools')) {
ctools_include('menu');
$primary = ctools_menu_primary_local_tasks();
$secondary = ctools_menu_secondary_local_tasks();
}
else {
$primary = menu_primary_local_tasks();
$secondary = menu_secondary_local_tasks();
}
if ($primary && $secondary) {
$output .= '<ul class="tabs tabs-main clearfix">' . $primary . '</ul>';
} elseif ($primary) {
$output .= '<ul class="tabs tabs-menu clearfix">' . $primary . '</ul>';
}
if ($secondary) {
$output .= '<ul class="tabs tabs-menu clearfix">' . $secondary . '</ul>';
}
return $output;
}
/**
* Override or insert variables into all templates.
*
* @param $vars
* An array of variables to pass to the theme template.
* @param $hook
* The name of the template being rendered (name of the .tpl.php file.)
*/
/* -- Delete this line if you want to use this function
function zuitedesk_preprocess(&$vars, $hook) {
$vars['sample_variable'] = t('Lorem ipsum.');
}
// */
/**
* 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 zuitedesk_preprocess_page(&$vars, $hook) {
global $user;
$vars['html_attributes'] = 'lang="' . $vars['language']->language . '" dir="' . $vars['language']->dir . '"';
// Remove duplicate utf-8 meta tag.
$vars['head'] = preg_replace('/<meta http-equiv=\"Content-Type\"[^>]*>\n/', '', $vars['head']);
// Remove pre-navigation links in header.
$vars['head'] = preg_replace('/<link rel=\"(prev|up|next)\"[^>]*>\n/', '', $vars['head']);
// We need to re-do the $layout and body classes because
// template_preprocess_page() assumes sidebars are named 'left' and 'right'.
if (!empty($vars['sidebar_main'])) {
$vars['layout'] = 'main';
$vars['classes_array'] = array_diff($vars['classes_array'], array('no-sidebars'));
$vars['classes_array'][] = 'sidebar-main';
}
// Main menu: primary links.
$lang = $vars['language']->language != 'es' ? '/' . $vars['language']->language : '';
$deskmenu = $_SESSION['deskmenu'];
$pl = '';
if ($vars['logged_in'] && !empty($deskmenu)) {
// Home option:
$pl .= _zuitedesk_option(TRUE, "$lang/user", t('Desktop'), NULL, t('Workspace'));
// Mission submenu:
$submenu = '';
$submenu .= _zuitedesk_option($deskmenu[4], "$lang/tasks/kanban", t('Tasks Kanban'), 'kanban', t('Kanban board view for your tasks'));
$submenu .= _zuitedesk_option($deskmenu[2], "$lang/projects", t('Projects'), 'project');
$submenu .= _zuitedesk_option($deskmenu[3], "$lang/tasks", t('Tasks'), 'task');
$submenu .= _zuitedesk_option($deskmenu[5], "$lang/calendar", t('Calendar'), 'calendar');
$partial = '';
$partial .= _zuitedesk_option($deskmenu[6], "$lang/doks", t('Documentation'), 'doc');
$partial .= _zuitedesk_option($deskmenu[6], "$lang/lexicon", t('Glossary'), 'glossary');
$submenu .= _zuitedesk_divider($submenu, $partial);
$partial = '';
$partial .= _zuitedesk_option(!empty($_SESSION['SuiteCRM_Session_ID']), "$lang/suitecrm", 'SuiteCRM', 'crm', t('Core sales, customer service and marketing processes with SuiteCRM'));
$partial .= _zuitedesk_option($deskmenu[7], "$lang/organizations", t('Organizations'), 'org');
$partial .= _zuitedesk_option($deskmenu[1], "$lang/admin/user/user/local", t('User list'), 'login', t('Allows to view list users and register new users'));
$partial .= _zuitedesk_option($deskmenu[8], "$lang/people", t('People'), 'people');
$partial .= _zuitedesk_option($deskmenu[9], "$lang/teams", t('Teams'), 'team');
$submenu .= _zuitedesk_divider($submenu, $partial);
$pl .= _zuitedesk_submenu($submenu, t('Mission'));
// Assistance submenu:
$submenu = '';
$submenu .= _zuitedesk_option($deskmenu[10], "$lang/tickets", t('Tickets'), 'ticket');
$submenu .= _zuitedesk_option($deskmenu[5], "$lang/events", t('Events'), 'event');
$partial = '';
$partial .= _zuitedesk_option($deskmenu[11], "$lang/timetrackings", t('Timetrackings'), 'timetracking');
$partial .= _zuitedesk_option($deskmenu[12], "$lang/expenses", t('Expenses'), 'expense');
$partial .= _zuitedesk_option($deskmenu[13], "$lang/invoices", t('Invoices'), 'invoice');
$submenu .= _zuitedesk_divider($submenu, $partial);
$partial = '';
$partial .= _zuitedesk_option($deskmenu[16], "$lang/trash", t('Trash'), 'trash');
$partial .= _zuitedesk_option(FALSE, "$lang/activity", t('Activity'), 'eye');
$submenu .= _zuitedesk_divider($submenu, $partial);
$pl .= _zuitedesk_submenu($submenu, t('Assistance'));
// User submenu:
$submenu = '';
$submenu .= _zuitedesk_option($deskmenu[14], "$lang/notes", t('My notes'), 'note');
$submenu .= _zuitedesk_option($deskmenu[15], "$lang/ideas", t('My ideas'), 'idea');
$submenu .= _zuitedesk_option(TRUE, "$lang/?tour=suitedesk", 'SuiteDesk Tour', 'help', t('A beginner\'s guide to SuiteDesk'));
$partial = '';
$partial .= _zuitedesk_option($deskmenu[17], "$lang/user/watcher", t('Watcher'), 'apply');
$submenu .= _zuitedesk_divider($submenu, $partial);
$partial = '';
$partial .= _zuitedesk_option(TRUE, "$lang/" . drupal_get_path_alias('user/' . $user->uid) . '/edit', t('My SuiteDesk profile'), 'user', t('Edit my personal account options'));
$partial .= _zuitedesk_option(TRUE, "$lang/logout", t('Logout'), 'logout', t('Close your session'));
$submenu .= _zuitedesk_divider($submenu, $partial);
$user_name = !empty($user->picture) ? '<div id="picture-menu"><img src="/' . preg_replace('/\/files\/suitedesk\//', '/system/files/', $user->picture) . '" alt="' . $user->name . '" /></div>' : '';
$firstname = strpos($user->name, ' ');
$user_name .= $firstname === FALSE ? $user->name : substr($user->name, 0, $firstname);
$pl .= _zuitedesk_submenu($submenu, $user_name);
}
else {
$pl = _zuitedesk_option(TRUE, "$lang/user", t('Login') . ' <span class="icon-login"></span>', NULL, t('New session for authenticated users'));
}
$vars['primary_links'] = '<h2 class="element-invisible">' . t('Main menu') . '</h2>';
$vars['primary_links'] .= '<ul id="main-menu" class="nav navbar-nav navbar-right clearfix">' . $pl . '</ul>';
// Special class and bootstrap tour for SuiteDesk pages:
$vars['classes_array'][] = 'suitedesk';
$closure = FALSE;
// Node vars.
if (!empty($vars['node'])) {
$node = $vars['node'];
// Remove the class for node type.
$vars['classes_array'] = array_diff($vars['classes_array'], array(drupal_html_class('node-type-' . $node->type)));
// Javascript to confirm projects cloning.
if ($vars['node']->type == 'stormproject' && user_access('Storm project: clone projects')) {
drupal_add_js('(function($) {$("li.stormproject_clone_run a").on("click", function() {return confirm("' . t('WARNING! This operation will duplicate all the project, tasks and documents in book. Are you sure?') . '");});})($jq);', 'inline', 'footer', FALSE);
$closure = TRUE;
}
}
// For first time users login or on demand:
if ($_SESSION['deskmenu'][0] || !empty($_GET['tour'])) {
drupal_add_js(drupal_get_path('theme', 'zuitedesk') . '/js/bootstrap-tour.min.js', 'theme', 'footer');
drupal_add_js(drupal_get_path('theme', 'zuitedesk') . '/js/tourpro.js', 'theme', 'footer');
$closure = TRUE;
}
if ($closure) {
$vars['closure'] = drupal_get_js('footer');
}
/*
$vars['sample_variable'] = t('Lorem ipsum.');
// To remove a class from $classes_array, use array_diff().
//$vars['classes_array'] = array_diff($vars['classes_array'], array('class-to-remove'));
*/
}
function _zuitedesk_option($access, $url, $text, $icon = NULL, $message = NULL) {
$option = '';
if ($access) {
$option .= '<li><a href="' . $url . '"';
$option .= empty($message) ? '' : ' title="' . $message . '"';
$option .= empty($icon) ? '>' : ' class="icon-' . $icon . '"> ';
$option .= $text . '</a></li>';
}
return $option;
}
function _zuitedesk_divider(&$submenu, &$partial) {
return !empty($submenu) && !empty($partial) ? '<li class="divider"></li>' . $partial : $partial;
}
function _zuitedesk_submenu(&$submenu, $text) {
return !empty($submenu) ? '<li class="dropdown"><a href="#" class="dropdown-toggle" data-toggle="dropdown">' . $text . ' <span class="caret"></span></a><ul class="dropdown-menu">' . $submenu . '</ul></li>' : '';
}
/**
* Override or insert variables into the node templates.
*
* @param $vars
* An array of variables to pass to the theme template.
* @param $hook
* The name of the template being rendered ("node" in this case.)
*/
function zuitedesk_preprocess_node(&$vars, $hook) {
if (in_array($vars['type'], array('blog', 'note', 'photo')) && $vars['page']) {
$vars['display_submitted'] = FALSE;
} else {
$node = $vars['node'];
$published = $node->created;
if ($node->published > $node->created && substr($vars['type'], 0, 5) != 'storm') {
$published = $node->published;
}
$vars['submitted'] = t('Submitted by !username on @datetime', array(
'!username' => theme('username', $node),
'@datetime' => format_date($published, 'small'),
));
}
/*
$vars['sample_variable'] = t('Lorem ipsum.');
// Optionally, run node-type-specific preprocess functions, like
// zuitedesk_preprocess_node_page() or zuitedesk_preprocess_node_story().
$function = __FUNCTION__ . '_' . $vars['node']->type;
if (function_exists($function)) {
$function($vars, $hook);
}
*/
}
/**
* Override or insert variables into the comment templates.
*
* @param $vars
* An array of variables to pass to the theme template.
* @param $hook
* The name of the template being rendered ("comment" in this case.)
*/
/* -- Delete this line if you want to use this function
function zuitedesk_preprocess_comment(&$vars, $hook) {
$vars['sample_variable'] = t('Lorem ipsum.');
}
// */
/**
* Override or insert variables into the block templates.
*
* @param $vars
* An array of variables to pass to the theme template.
* @param $hook
* The name of the template being rendered ("block" in this case.)
*/
/* -- Delete this line if you want to use this function
function zuitedesk_preprocess_block(&$vars, $hook) {
$vars['sample_variable'] = t('Lorem ipsum.');
}
// */
/**
* Override theme_breadcrumb().
*/
function zuitedesk_breadcrumb($breadcrumb) {
if (count($breadcrumb) > 2) {
array_shift($breadcrumb);
return '<div class="breadcrumb">'. implode(' / ', $breadcrumb) .'</div>';
}
}
/**
* Override theme_more_help_link().
*/
function zuitedesk_more_help_link($url) {
global $user;
return $user->uid == 1 ? '<div class="more-help-link">' . t('[<a href="@link">more help...</a>]', array('@link' => check_url($url))) . '</div>' : '';
}
/**
* Override theme_imagecache() function of imagecache module.
*/
function zuitedesk_imagecache($presetname, $path, $alt = '', $title = '', $attributes = NULL, $getsize = TRUE, $absolute = TRUE) {
if ($presetname == 'attached' || $presetname == 'maxiphoto') {
$attributes['class'] .= ' img-responsive';
}
return theme_imagecache($presetname, $path, $alt, $title, $attributes, $getsize, $absolute);
}
/**
* Override theme_button().
*
* See http://designguru.org/blog/040612/replacing-drupal-form-submission-inputs-allow-cufon-styling
*/
function zuitedesk_button($element) {
// Make sure not to overwrite classes.
if (isset($element['#attributes']['class'])) {
$element['#attributes']['class'] = 'form-' . $element['#button_type'] . ' ' . $element['#attributes']['class'];
}
else {
$element['#attributes']['class'] = 'form-' . $element['#button_type'];
}
$value = check_plain($element['#value']);
$element['#attributes']['class'] .= _zuitedesk_button_classes($value);
return '<button type="submit" ' . (empty($element['#name']) ? '' : 'name="' . $element['#name'] . '" ') . 'id="' . $element['#id'] . '" value="' . $value . '" ' . drupal_attributes($element['#attributes']) . ' >' . _zuitedesk_button_icontext($value) . '</button>';
}
/**
* Override theme_select().
*/
function zuitedesk_select($element) {
_form_set_class($element, array('form-control'));
return theme_select($element);
}
/**
* Override theme_textfield().
*/
function zuitedesk_textfield($element) {
_form_set_class($element, array('form-control'));
return theme_textfield($element);
}
/**
* Override theme_password().
*/
function zuitedesk_password($element) {
_form_set_class($element, array('form-control'));
return theme_password($element);
}
/**
* Override theme_textarea().
*/
function zuitedesk_textarea($element) {
_form_set_class($element, array('form-control'));
return theme_textarea($element);
}
/**
* Override theme_webform_email().
*/
function zuitedesk_webform_email($element) {
_form_set_class($element, array('form-control'));
return theme_webform_email($element);
}
/**
* Override theme_checkbox().
*/
function zuitedesk_checkbox($element) {
if (isset($element['#title'])) {
$element['#title'] = t($element['#title']);
}
return theme_checkbox($element);
}
/**
* Override theme_radio().
*/
function zuitedesk_radio($element) {
if (isset($element['#title'])) {
$element['#title'] = t($element['#title']);
}
return theme_radio($element);
}
/**
* Override theme_table().
*/
function zuitedesk_table($header, $rows, $attributes = array(), $caption = NULL) {
if (!in_array($attributes['id'], array('tasks'))) {
$attributes['class'] = 'table table-condensed' . (!empty($attributes['class']) ? ' ' . $attributes['class'] : '');
}
if (arg(0) == 'admin' || in_array($attributes['id'], array('stormorganizations', 'stormpeople', 'stormteams', 'stormprojects', 'tasks', 'stormtasks', 'stormtickets', 'stormnotes', 'stormideas', 'stormdoks', 'stormtimetrackings', 'stormevents', 'stormtrash', 'twatcher'))) {
$attributes['class'] .= ' table-hover';
return '<div class="table-responsive">' . theme_table($header, $rows, $attributes, $caption) . '</div>';
}
return theme_table($header, $rows, $attributes, $caption);
}
/**
* Override theme_pager().
*/
function zuitedesk_pager($tags = array(), $limit = 10, $element = 0, $parameters = array(), $quantity = 9) {
global $pager_page_array, $pager_total;
// Calculate various markers within this pager piece:
// Middle is used to "center" pages around the current page.
$pager_middle = ceil($quantity / 2);
// current is the page we are currently paged to
$pager_current = $pager_page_array[$element] + 1;
// first is the first page listed by this pager piece (re quantity)
$pager_first = $pager_current - $pager_middle + 1;
// last is the last page listed by this pager piece (re quantity)
$pager_last = $pager_current + $quantity - $pager_middle;
// max is the maximum page number
$pager_max = $pager_total[$element];
// End of marker calculations.
// Prepare for generation loop.
$i = $pager_first;
if ($pager_last > $pager_max) {
// Adjust "center" if at end of query.
$i = $i + ($pager_max - $pager_last);
$pager_last = $pager_max;
}
if ($i <= 0) {
// Adjust "center" if at start of query.
$pager_last = $pager_last + (1 - $i);
$i = 1;
}
// End of generation loop preparation.
// Special use for kanban tasks page (optimized for performace):
if ($_GET['q'] == 'tasks/kanban') {
$view = views_get_current_view();
if (!empty($view) && $view->name == 'storm_projects_kanban' && $view->current_display == 'page_2') {
if (empty($_REQUEST['go'])) {
$parameters['go'] = 'backlog';
} else {
drupal_add_js('$jq(\'html\').delay(500).animate({scrollTop:$("#views-exposed-form-storm-projects-kanban-page-2").offset().top-60},1600);', 'inline', 'footer');
}
}
}
$li_first = theme('pager_first', (isset($tags[0]) ? $tags[0] : '«'), $limit, $element, $parameters);
$li_previous = theme('pager_previous', (isset($tags[1]) ? $tags[1] : ''), $limit, $element, 1, $parameters);
$li_next = theme('pager_next', (isset($tags[3]) ? $tags[3] : ''), $limit, $element, 1, $parameters);
$li_last = theme('pager_last', (isset($tags[4]) ? $tags[4] : '»'), $limit, $element, $parameters);
if ($pager_total[$element] > 1) {
if ($li_first) {
$items[] = array(
// 'class' => 'pager-first',
'data' => $li_first,
);
}
if ($li_previous) {
$items[] = array(
// 'class' => 'pager-previous',
'data' => $li_previous,
);
}
// When there is more than one page, create the pager list.
if ($i != $pager_max) {
# if ($i > 1) {
# $items[] = array(
# 'class' => 'disabled',
# 'data' => '<span>…</span>',
# );
# }
// Now generate the actual pager piece.
for (; $i <= $pager_last && $i <= $pager_max; $i++) {
if ($i < $pager_current) {
$items[] = array(
// 'class' => 'pager-item',
'data' => theme('pager_previous', $i, $limit, $element, ($pager_current - $i), $parameters),
);
}
if ($i == $pager_current) {
$items[] = array(
'class' => 'active',
'data' => "<span>$i</span>",
);
}
if ($i > $pager_current) {
$items[] = array(
// 'class' => 'pager-item',
'data' => theme('pager_next', $i, $limit, $element, ($i - $pager_current), $parameters),
);
}
}
# if ($i < $pager_max) {
# $items[] = array(
# 'class' => 'disabled',
# 'data' => '<span>…</span>',
# );
# }
}
// End generation.
if ($li_next) {
$items[] = array(
// 'class' => 'pager-next',
'data' => $li_next,
);
}
if ($li_last) {
$items[] = array(
// 'class' => 'pager-last',
'data' => $li_last,
);
}
return theme('item_list', $items, NULL, 'ul', array('class' => 'pagination'));
}
}
function _zuitedesk_button_classes($value) {
// Text values containing these strings.
$button_strings = array(
'btn-danger' => array(
t('Delete'),
t('Remove'),
t('Move to trash'),
),
'btn-primary' => array(
t('Login'),
t('Log in'),
t('Save'),
t('Confirm'),
t('Submit'),
t('Search'),
t('Advanced search'),
t('Upload'),
t('Download feature'),
),
'btn-success' => array(
t('Add effect'),
t('Add and configure'),
t('Add'),
t('Create'),
t('Write'),
t('Add more exceptions'),
t('Add more additions'),
),
'btn-warning' => array(
t('Export'),
t('Import'),
t('Restore'),
t('Rebuild'),
t('Apply'),
t('Filter'),
t('Change'),
t('Go'),
),
'btn-info' => array(
t('Preview'),
t('Save and add'),
t('Add another item'),
t('Update style'),
t('Apply'),
t('Update'),
t('Reset'),
),
);
// String matching first.
foreach ($button_strings as $class => $strings) {
foreach ($strings as $string) {
if (strpos(drupal_strtolower($value), drupal_strtolower($string)) !== FALSE) {
return ' btn ' . $class;
}
}
}
return ' btn btn-default';
}
function _zuitedesk_button_icontext($value) {
// Text values containing these strings.
$icon_strings = array(
'login' => array(
t('Login'),
t('Log in'),
),
'cog' => array(
t('Manage'),
t('Configure'),
),
'search' => array(
t('Search'),
t('Advanced search'),
),
'save' => array(
t('Save'),
),
'trash' => array(
t('Delete'),
t('Remove'),
t('Move to trash'),
),
'upload' => array(
t('Submit'),
t('Upload'),
),
'eye' => array(
t('Preview'),
),
'download' => array(
t('Download'),
),
'export' => array(
t('Export'),
),
'import' => array(
t('Import'),
),
'pencil' => array(
t('Edit'),
),
'plus' => array(
t('Add'),
t('Write'),
t('Add more exceptions'),
t('Add more additions'),
),
'apply' => array(
t('Apply'),
t('Go'),
),
'reset' => array(
t('Reset'),
t('Update'),
t('Change'),
),
'filter' => array(
t('Filter'),
),
);
// String matching first.
foreach ($icon_strings as $icon => $strings) {
foreach ($strings as $string) {
if (strpos(drupal_strtolower($value), drupal_strtolower($string)) !== FALSE) {
return '<span class="icon-'. $icon .'"></span> ' . $value;
}
}
}
return $value;
}
/**
* Theming function for "Recently read" module.
*
* By default, Recently read block displays a list of links to node titles. This
* function customize the output by overriding theme_recently_read_item().
*/
function zuitedesk_recently_read_item($item) {
global $base_url, $theme_path;
$list_types = node_get_types();
$type = $item['type'];
$type_name = t($list_types[$type]->name);
$node_icon = $base_url . '/' . $theme_path . '/images/storm/' . substr($type, 5) . '.png';
$node_url = $base_url . '/' . drupal_get_path_alias('node/' . $item['nid']);
$title = mb_strlen($item['title']) > 18 ? mb_substr($item['title'], 0, 18) .'&hellip;' : $item['title'];
return '<div class="recent-read-item">' .
'<img src="' . $node_icon . '" width="20" height="20" />' .
'<a href="' . $node_url . '" title="' . $type_name . ' &laquo;' . check_plain($item['title']) . '&raquo;">' . $title . '</a></div>';
}