<?php
// $ID:$
/**
* @file
* Style.module is a stylesheet override system for Drupal themes.
*/
/**
* See if styles exist in a directory
*
* Checks for directories
*
* @param string $dir
* Directory to view
* @param boolean $refresh
* Bypass cache
* @return
* TRUE if styles exist. FALSE if not.
*/
function style_styles_exist($style_dir, $refresh = FALSE) {
static $style_styles_exist;
// Caching, can be bypased with $refresh being TRUE
if ($refresh === FALSE && isset($style_styles_exist) && isset($style_styles_exist[$style_dir])) {
return $style_styles_exist[$style_dir];
}
// Open link with our dir, as $handle
if ($handle = opendir($style_dir)) {
// Recursive through the items in this directory
while (FALSE !== ($file = readdir($handle))) {
// Is this item a directory?
if (is_dir($file)) {
// We found a style, we can finish.
closedir($handle);
// Return TRUE, a style directory exists
return $style_styles_exist[$style_dir] = TRUE;
}
}
// No directory (styles) found. Let's close link and return FALSE.
closedir($handle);
return $style_styles_exist[$style_dir] = FALSE;
}
}
function style_scan_styles2($theme_name) {
$files_dir = style_get_files_skin_dir($theme_name);
$local_dir = style_get_local_skin_dir($theme_name);
$styles = array();
drupal_set_message("files {$files_dir} | local: {$local_dir}");
if (is_dir($files_dir) && style_styles_exist($files_dir)) {
$styles += style_file_tree($files_dir);
}
if (is_dir($local_dir) && style_styles_exist($local_dir)) {
$styles += style_file_tree($local_dir);
}
return $styles;
}
/**
* Scans for directories, not recursive.
*
* @param string $dir
* Directory of the theme or module styles
* @param boolean $refresh
Bypass cache
* @return array
* All styles in folder
*/
function style_scan_styles($dir, $refresh = FALSE) {
static $styles;
// Caching system. Can be bypassed with refresh
if ($refresh === FALSE && isset($styles) && isset($styles[$dir])) {
return $styles[$dir];
}
// Open up directory
if ($handle = opendir($dir)) {
// Make array
$styles[$dir] = array();
// Scans inside the /style dir of a theme.
// Each dir in /style is an individual style
while (FALSE !== ($file = readdir($handle))) {
// PHP will throw up '.' and '..'. No need to include.
if ($file != "." && $file != ".." && is_dir($dir . $file)) {
// Append this style dir to our array of styles
$styles[$dir][] = $file;
}
}
// Finish up
closedir($handle);
}
// Return list of styles as an array
return $styles[$dir];
}
/**
* Return files in an array tree. :D
*
* @param string $dir
* Directory of style
* @param boolean $refresh
* Bypass cache
* @return array
* Recursive tree of files.
*/
function style_file_tree($dir, $refresh = FALSE) {
static $style;
if (!is_dir($dir)) {
drupal_set_message("{$dir} is not a directory");
}
// Caching, can be bypassed with $refresh
if (isset($style[$dir]) && $refresh !== TRUE) {
return $style[$dir];
}
$style[$dir] = _style_file_tree($dir);
return $style[$dir];
}
/**
* Get the structure and files of a style
*
* @param string $dir
* Directory of style
* @param boolean $show_files
* Scan files
* @return array
* Recursive output of files.
*/
function _style_file_tree($dir, $show_files = TRUE) {
$d = dir($dir); $x = array();
while (($r = $d->read()) !== FALSE) {
if ($r != "." && $r != ".." && (($show_files == FALSE && is_dir($dir.$r)) || $show_files == TRUE)) {
$x[$r] = (is_dir($dir.$r) ? array() : (is_file($dir.$r) ? TRUE : FALSE));
}
}
foreach ($x as $key => $value) {
if (is_dir($dir.$key."/")) {
// Move recursively into directories
$x[$key] = _style_file_tree($dir.$key."/", $show_files);
}
}
ksort($x);
return $x;
}
/**
* Return list of styles as a keyed array.
*
* @param string $directory
* Directory to return formatted list from
* @return array
* Formatted as array[full_path + /styledir] = stylename
*/
function style_dirs($directory, $files = FALSE) {
// If styles exists in the directory
// First by seeing if styles dir itself exists
// Then by seeing if individual styles are present in dir
if (file_exists($directory) && style_styles_exist($directory)) {
// Scan for styles in style dir
$styles = style_scan_styles($directory);
// See if this list of styles in the files section
if (strpos($directory, file_directory_path()) !== FALSE) {
$file = TRUE;
}
// Add each style to our list array
foreach ($styles as $style) {
// output: array[full_path + dir name] = style name
$style_list[$directory . $style] = $style;
if ($file) {
// Add a * so users can identity generated/contributed styles
$style_list[$directory . $style] .= " *";
}
}
}
// Return an array of styles
return $style_list;
}
function style_menu() {
$items['admin/build/styles'] = array(
'page arguments' => array('style_settings_page'),
'page callback' => 'drupal_get_form',
'title' => 'Styles',
);
$items['style_test'] = array(
'page callback' => 'style_test_ahah',
'title' => 'color',
'type' => MENU_CALLBACK,
'access arguments' => array('access content'),
);
return $items;
}
/**
* Form for our settings page
*
*/
function style_settings_page() {
# Get a list of themes
$themes = array_keys(list_themes());
$form['diagnostic'] = array(
'#title' => t('Diagnostics'),
'#type' => 'fieldset',
'#description' => t('This module currently is set to detect themes stored
in /styles in your theme folders. It will also include
/files/themes/themename/styles.'),
);
foreach ($themes as $theme) {
$style_info = style_dirs('theme', $theme);
$style_list = $style_info['list'];
if (isset($style_list) && count($style_list) > 0) {
$form['diagnostic'][$theme] = array(
'#type' => 'fieldset',
'#title' => t('style_scan(\'theme\', \'' . $theme . '\')'),
'#collapsible' => TRUE,
'#attributes' => array('class' => 'style-select-fieldset'),
);
drupal_add_css(drupal_get_path('module', 'style') .'/style.form.css', 'module', 'all', TRUE);
$form['diagnostic'][$theme]['theme_'.$theme.'_style'] = array(
'#type' => 'select',
'#title' => t('Style select box'),
'#description' => t('* generated style'),
'#options' => $style_list,
'#default_value' => variable_get('theme_'.$theme.'_style', 0),
);
}
}
// Send our form to Drupal to make a settings page
return system_settings_form($form);
}
/**
* Inject pertinent style variables for current theme into Drupal.settings.style
* scope.
*
*/
function style_create_js_vars($theme_key) {
$styles = style_scan_styles2($theme_key);
drupal_add_js(array('style' => array('styles' => $styles)), 'setting');
}
function style_preprocess_page(&$vars) {
// Our variables from Drupal's global scope
global $theme_key, $theme_info, $base_url;
// Get our theme directory
$theme_dir = theme_get_setting('theme_'.$theme_key.'_style');
$local_dir = style_get_local_skin_dir($theme_key);
$files_dir = style_get_files_skin_dir($theme_key);
// Does the directory exist?
if (dir($theme_dir)) {
// Initiate array
$styles = array();
// Cycle through our page's stylesheet files, by basename.
foreach (array_keys($vars['css']['all']['theme']) as $css) {
$styles[] = basename($css);
}
// Grab the CSS files from our style.
$newcss = style_file_tree($theme_dir);
// Create an array for new styles
$new_styles = array();
foreach (array_keys($newcss) as $css) {
$new_styles[] = $theme_dir . '/' . $css; }
$color_paths = $new_styles;
}
///
/*********************************************************^^^^^^**| | *
* | D___| | *
* W A R N I N G W A R N I N G W A R N I N G | R_____| *
* /_ U| *
* <STYLESHEET>___O P|__<VOILA!>
* the machine | A| *
* CSS OVERRIDE IN PROGRESS BEEP BOOP BEEP BOOP | L| *
* |2008| *
*****************************************************************/
if (!empty($color_paths)) {
// Loop over theme CSS files and try to Rebuild CSS array with rewritten
// stylesheets. Keep the orginal order intact for CSS cascading.
$new_theme_css = array();
foreach ($vars['css']['all']['theme'] as $old_path => $old_preprocess) {
// Add the non-colored stylesheet first as we might not find a
// re-colored stylesheet for replacement later.
$new_theme_css[$old_path] = $old_preprocess;
// Loop over the path array with recolored CSS files to find matching
// paths which could replace the non-recolored paths.
foreach ($color_paths as $color_path) {
if (basename($old_path) == basename($color_path)) {
// Pull out the non-colored and add rewritten stylesheet.
unset($new_theme_css[$old_path]);
$new_theme_css[$color_path] = $old_preprocess;
// If the current language is RTL and the CSS file had an RTL variant,
// pull out the non-colored and add rewritten RTL stylesheet.
if (defined('LANGUAGE_RTL') && $language->direction == LANGUAGE_RTL) {
$rtl_old_path = str_replace('.css', '-rtl.css', $old_path);
$rtl_color_path = str_replace('.css', '-rtl.css', $color_path);
if (file_exists($rtl_color_path)) {
unset($new_theme_css[$rtl_old_path]);
$new_theme_css[$rtl_color_path] = $old_preprocess;
}
}
break;
}
}
}
$vars['css']['all']['theme'] = $new_theme_css;
$vars['styles'] = drupal_get_css($vars['css']);
}
// Some logo kung-fu. Replace logo.png at theme base.
if (file_exists(theme_get_setting('theme_'.$theme_key.'_style') . '/logo.png')) {
$vars['logo'] = $base_url . '/' . theme_get_setting('theme_'.$theme_key.'_style') . '/logo.png';
}
}
function style_get_local_skin_dir($theme_name) {
$local_dir = drupal_get_path('theme', $theme_name) . '/styles/';
return $local_dir;
}
function style_get_files_skin_dir($theme_name) {
$files_dir = file_directory_path() . '/' . drupal_get_path('theme', $theme_name) . '/styles/';
return $files_dir;
}
/**
* Implementation of hook_form_alter().
*/
function style_form_alter(&$form, $form_state, $form_id) {
global $base_url;
// Insert the color changer into the theme settings page.
if ($form_id == 'system_theme_settings' && arg(4)) {
if (variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC) != FILE_DOWNLOADS_PUBLIC) {
// Disables the color changer when the private download method is used.
// TODO: This should be solved in a different way. See issue #181003.
drupal_set_message(t('The color picker only works if the <a href="@url">download method</a> is set to public.', array('@url' => url('admin/settings/file-system'))), 'warning');
}
else {
// Via drupal argument (you'll see it in URL), we grab the theme name.
$theme_name = arg(4);
// Get the directories we want to scan for files in.
$local_dir = style_get_local_skin_dir($theme_name);
$files_dir = style_get_files_skin_dir($theme_name);
$style_list = array();
// Grab various information about styles.
if (count($style_list_local = style_dirs($local_dir)) > 0) {
$style_list += $style_list_local;
}
if (count($style_list_files = style_dirs($files_dir, TRUE)) > 0) {
$style_list += $style_list_files;
}
// If styles are available for theme OR extensions are present in
// $form['style']['extensions'], show our style fieldset.
if (isset($style_list) && count($style_list) > 0 || count($form['style']['extensions']) > 0) {
// Add our stylesheet to tighten things up
drupal_add_css(drupal_get_path('module', 'style') .'/style.form.css', 'module', 'all', TRUE);
drupal_add_js(drupal_get_path('module', 'style') . '/preloadCssImages.jQuery_v5.js');
drupal_add_js(drupal_get_path('module', 'style') . '/style.js');
// Add our theme's style roots to Drupal.Settings JS variable scope
drupal_add_js(array('style' => array('local_dir' => $local_dir, 'files_dir' => $files_dir, 'theme_name' => $theme_name), ), 'setting');
style_create_js_vars($theme_name);
// Does $form['style'] already exist?
if (!isset($form['style'])) {
$form['style'] = array();
}
// Take on the ['style'], in case a module is loaded before.
// This will assure order is not interfered with.
$form['style'] += array(
'#type' => 'fieldset',
'#title' => t('Style'),
'#description' => t('Change the look and feel your theme.'),
'#collapsible' => TRUE,
'#attributes' => array('class' => 'style-select-fieldset'),
'#weight' => -15,
);
// http://api.drupal.org/api/function/theme_get_settings/6
// We need to update theme_get_setting() in D7 to support per-theme
// caching in certain events.
$value = theme_get_settings($theme_name, 0);
$value = $value['theme_'.$theme_name.'_style'];
$form['style']['theme_name'] = array(
'#type' => 'hidden',
'#value' => $theme_name,
);
if (!isset($style_list) || count($style_list) == 0) {
$form['style']['theme_'.$theme_name.'_style'] = array(
'#type' => 'item',
'#value' => t('No styles available'),
'#default_value' => $value,
);
}
else {
// Select box of styles
$form['style']['theme_'.$theme_name.'_style'] = array(
'#type' => 'select',
'#title' => t('Default'),
// Generated styles have *
'#description' => t('* generated style'),
'#options' => $style_list,
'#default_value' => $value,
/*
'#ahah' => array(
'path' => 'style_test',
'wrapper' => 'style-postgen-note',
'method' => 'replace',
'effect' => 'fade',
),
*/
);
$form['style']['markup'] = array(
'#type' => 'markup',
'#value' => '<div id=\'style-postgen-note\'></div>',
);
}
// If style extensions have not yet been created, let's make it.
if (count($form['style']['extensions']) > 0) {
$form['style']['extensions'] += array(
'#type' => 'fieldset',
'#title' => t('Extensions'),
'#collapsible' => TRUE,
'#attributes' => array('class' => 'style-extensions-fieldset'),
'#weight' => 5,
);
}
}
}
}
}
function style_test_ahah() {
$code = "<pre>";
$code .= var_export($_REQUEST, 1);
$code .= "</pre>";
$theme_name = $_REQUEST['theme_name'];
$path = $_REQUEST['theme_' . $theme_name . '_style'];
if (strpos($path, file_directory_path()) !== FALSE) {
drupal_json(array('status' => TRUE, 'data' => $theme_name . ' is a files style'));
} else {
drupal_json(array('status' => TRUE, 'data' => $code));
}
}
?>