From bd25042191e404bf8f3ffa1fe255b56268132d81 Mon Sep 17 00:00:00 2001
From: JohnAlbin <virtually.johnalbin@gmail.com>
Date: Sat, 3 Dec 2011 02:52:06 +0800
Subject: [PATCH 1/5] Rename system_find_base_themes() to
 drupal_find_base_themes()

---
 includes/theme.inc           |   41 +++++++++++++++++++++++++++++++++++++++++
 modules/system/system.module |   38 +++-----------------------------------
 2 files changed, 44 insertions(+), 35 deletions(-)

diff --git a/includes/theme.inc b/includes/theme.inc
index 9a921de..1262b97 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -778,6 +778,47 @@ function list_themes($refresh = FALSE) {
 }
 
 /**
+ * Find all the base themes for the specified theme.
+ *
+ * Themes can inherit templates and function implementations from earlier themes.
+ *
+ * @param $themes
+ *   An array of available themes.
+ * @param $key
+ *   The name of the theme whose base we are looking for.
+ * @param $used_keys
+ *   A recursion parameter preventing endless loops.
+ * @return
+ *   Returns an array of all of the theme's ancestors; the first element's value
+ *   will be NULL if an error occurred.
+ */
+function drupal_find_base_themes($themes, $key, $used_keys = array()) {
+  $base_key = $themes[$key]->info['base theme'];
+  // Does the base theme exist?
+  if (!isset($themes[$base_key])) {
+    return array($base_key => NULL);
+  }
+
+  $current_base_theme = array($base_key => $themes[$base_key]->info['name']);
+
+  // Is the base theme itself a child of another theme?
+  if (isset($themes[$base_key]->info['base theme'])) {
+    // Do we already know the base themes of this theme?
+    if (isset($themes[$base_key]->base_themes)) {
+      return $themes[$base_key]->base_themes + $current_base_theme;
+    }
+    // Prevent loops.
+    if (!empty($used_keys[$base_key])) {
+      return array($base_key => NULL);
+    }
+    $used_keys[$base_key] = TRUE;
+    return drupal_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme;
+  }
+  // If we get here, then this is our parent theme.
+  return $current_base_theme;
+}
+
+/**
  * Generates themed output.
  *
  * All requests for themed output must go through this function. It examines
diff --git a/modules/system/system.module b/modules/system/system.module
index 010608d..ab313c0 100644
--- a/modules/system/system.module
+++ b/modules/system/system.module
@@ -2553,7 +2553,7 @@ function _system_rebuild_theme_data() {
   // Now that we've established all our master themes, go back and fill in data
   // for subthemes.
   foreach ($sub_themes as $key) {
-    $themes[$key]->base_themes = system_find_base_themes($themes, $key);
+    $themes[$key]->base_themes = drupal_find_base_themes($themes, $key);
     // Don't proceed if there was a problem with the root base theme.
     if (!current($themes[$key]->base_themes)) {
       continue;
@@ -2648,42 +2648,10 @@ function _system_default_theme_features() {
 /**
  * Find all the base themes for the specified theme.
  *
- * Themes can inherit templates and function implementations from earlier themes.
- *
- * @param $themes
- *   An array of available themes.
- * @param $key
- *   The name of the theme whose base we are looking for.
- * @param $used_keys
- *   A recursion parameter preventing endless loops.
- * @return
- *   Returns an array of all of the theme's ancestors; the first element's value
- *   will be NULL if an error occurred.
+ * This function has been deprecated in favor of drupal_find_base_themes().
  */
 function system_find_base_themes($themes, $key, $used_keys = array()) {
-  $base_key = $themes[$key]->info['base theme'];
-  // Does the base theme exist?
-  if (!isset($themes[$base_key])) {
-    return array($base_key => NULL);
-  }
-
-  $current_base_theme = array($base_key => $themes[$base_key]->info['name']);
-
-  // Is the base theme itself a child of another theme?
-  if (isset($themes[$base_key]->info['base theme'])) {
-    // Do we already know the base themes of this theme?
-    if (isset($themes[$base_key]->base_themes)) {
-      return $themes[$base_key]->base_themes + $current_base_theme;
-    }
-    // Prevent loops.
-    if (!empty($used_keys[$base_key])) {
-      return array($base_key => NULL);
-    }
-    $used_keys[$base_key] = TRUE;
-    return system_find_base_themes($themes, $base_key, $used_keys) + $current_base_theme;
-  }
-  // If we get here, then this is our parent theme.
-  return $current_base_theme;
+  return drupal_find_base_themes($themes, $key, $used_keys);
 }
 
 /**
-- 
1.7.8.3


From fc2b5e8c0e43b876316fc7508c1a5df74aa124c8 Mon Sep 17 00:00:00 2001
From: JohnAlbin <virtually.johnalbin@gmail.com>
Date: Sat, 3 Dec 2011 02:52:50 +0800
Subject: [PATCH 2/5] Make system_list() cache base_themes and sub_themes
 arrays for themes

---
 includes/module.inc |   24 ++++++++++++++++++++++++
 1 files changed, 24 insertions(+), 0 deletions(-)

diff --git a/includes/module.inc b/includes/module.inc
index 633b317..468d341 100644
--- a/includes/module.inc
+++ b/includes/module.inc
@@ -178,6 +178,30 @@ function system_list($type) {
           $lists['filepaths'][] = array('type' => $record->type, 'name' => $record->name, 'filepath' => $record->filename);
         }
       }
+      foreach ($lists['theme'] as $key => $theme) {
+        if (!empty($theme->info['base theme'])) {
+          // Make a list of the theme's base themes.
+          $lists['theme'][$key]->base_themes = drupal_find_base_themes($lists['theme'], $key);
+          // Don't proceed if there was a problem with the root base theme.
+          if (!current($lists['theme'][$key]->base_themes)) {
+            continue;
+          }
+          // Determine the root base theme.
+          $base_key = key($lists['theme'][$key]->base_themes);
+          // Add to the list of sub-themes for each of the theme's base themes.
+          foreach (array_keys($lists['theme'][$key]->base_themes) as $base_theme) {
+            $lists['theme'][$base_theme]->sub_themes[$key] = $lists['theme'][$key]->info['name'];
+          }
+          // Add the base theme's theme engine info.
+          $lists['theme'][$key]->info['engine'] = $lists['theme'][$base_key]->info['engine'];
+        }
+        else {
+          // A plain theme is its own base theme.
+          $base_key = $key;
+        }
+        // Set the theme engine prefix.
+        $lists['theme'][$key]->prefix = ($lists['theme'][$key]->info['engine'] == 'theme') ? $base_key : $lists['theme'][$key]->info['engine'];
+      }
       cache_set('system_list', $lists, 'cache_bootstrap');
     }
     // To avoid a separate database lookup for the filepath, prime the
-- 
1.7.8.3


From 7590e3ef06782d09c38e639436c3f0673280aa46 Mon Sep 17 00:00:00 2001
From: JohnAlbin <virtually.johnalbin@gmail.com>
Date: Sat, 3 Dec 2011 02:53:08 +0800
Subject: [PATCH 3/5] Add tests

---
 modules/simpletest/tests/theme.test                |   31 ++++++++++++++++++++
 modules/simpletest/tests/theme_test.module         |    2 +
 .../tests/themes/test_theme/test_theme.info        |    2 +
 .../themes/test_basetheme/test_basetheme.info      |    7 ++++
 .../tests/themes/test_subtheme/test_subtheme.info  |    7 ++++
 5 files changed, 49 insertions(+), 0 deletions(-)
 create mode 100644 modules/update/tests/themes/test_basetheme/test_basetheme.info
 create mode 100644 modules/update/tests/themes/test_subtheme/test_subtheme.info

diff --git a/modules/simpletest/tests/theme.test b/modules/simpletest/tests/theme.test
index d548850..a198a6a 100644
--- a/modules/simpletest/tests/theme.test
+++ b/modules/simpletest/tests/theme.test
@@ -103,6 +103,37 @@ class ThemeUnitTest extends DrupalWebTestCase {
     $this->drupalGet('theme-test/suggestion');
     variable_set('preprocess_css', 0);
   }
+
+  /**
+   * Test the list_themes() function.
+   */
+  function testListThemes() {
+    $themes = list_themes();
+    // Check if drupal_theme_access() retrieves enabled themes properly from list_themes().
+    $this->assertTrue(drupal_theme_access('test_theme'), t('Enabled theme detected'));
+    // Check if list_themes() returns disabled themes.
+    $this->assertTrue(array_key_exists('test_basetheme', $themes), t('Disabled theme detected'));
+    // Check for base theme and subtheme lists.
+    $base_theme_list = array('test_basetheme' => 'Theme test base theme');
+    $sub_theme_list = array('test_subtheme' => 'Theme test subtheme');
+    $this->assertIdentical($themes['test_basetheme']->sub_themes, $sub_theme_list, t('Base theme\'s object includes list of subthemes.'));
+    $this->assertIdentical($themes['test_subtheme']->base_themes, $base_theme_list, t('Subtheme\'s object includes list of base themes.'));
+    // Check for theme engine in subtheme.
+    $this->assertIdentical($themes['test_subtheme']->engine, 'phptemplate', t('Subtheme\'s object includes the theme engine.'));
+    // Check for theme engine prefix.
+    $this->assertIdentical($themes['test_basetheme']->prefix, 'phptemplate', t('Base theme\'s object includes the theme engine prefix.'));
+    $this->assertIdentical($themes['test_subtheme']->prefix, 'phptemplate', t('Subtheme\'s object includes the theme engine prefix.'));
+  }
+
+  /**
+   * Test the theme_get_setting() function.
+   */
+  function testThemeGetSetting() {
+    $GLOBALS['theme_key'] = 'test_theme';
+    $this->assertIdentical(theme_get_setting('theme_test_setting'), 'default value', t('theme_get_setting() uses the default theme automatically.'));
+    $this->assertNotEqual(theme_get_setting('subtheme_override', 'test_basetheme'), theme_get_setting('subtheme_override', 'test_subtheme'), t('Base theme\'s default settings values can be overridden by subtheme.'));
+    $this->assertIdentical(theme_get_setting('basetheme_only', 'test_subtheme'), 'base theme value', t('Base theme\'s default settings values are inherited by subtheme.'));
+  }
 }
 
 /**
diff --git a/modules/simpletest/tests/theme_test.module b/modules/simpletest/tests/theme_test.module
index 9cec538..8457482 100644
--- a/modules/simpletest/tests/theme_test.module
+++ b/modules/simpletest/tests/theme_test.module
@@ -5,6 +5,8 @@
  */
 function theme_test_system_theme_info() {
   $themes['test_theme'] = drupal_get_path('module', 'theme_test') . '/themes/test_theme/test_theme.info';
+  $themes['test_basetheme'] = drupal_get_path('module', 'theme_test') . '/themes/test_basetheme/test_basetheme.info';
+  $themes['test_subtheme'] = drupal_get_path('module', 'theme_test') . '/themes/test_subtheme/test_subtheme.info';
   return $themes;
 }
 
diff --git a/modules/simpletest/tests/themes/test_theme/test_theme.info b/modules/simpletest/tests/themes/test_theme/test_theme.info
index dd5584b..4bbbe7a 100644
--- a/modules/simpletest/tests/themes/test_theme/test_theme.info
+++ b/modules/simpletest/tests/themes/test_theme/test_theme.info
@@ -14,3 +14,5 @@ hidden = TRUE
 ; version from being loaded, and that errors aren't caused by the lack of this
 ; file within the theme folder.
 stylesheets[all][] = system.base.css
+
+settings[theme_test_setting] = default value
diff --git a/modules/update/tests/themes/test_basetheme/test_basetheme.info b/modules/update/tests/themes/test_basetheme/test_basetheme.info
new file mode 100644
index 0000000..c39e8a2
--- /dev/null
+++ b/modules/update/tests/themes/test_basetheme/test_basetheme.info
@@ -0,0 +1,7 @@
+name = Theme test base theme
+description = Test theme which acts as a base theme for other test subthemes.
+core = 7.x
+hidden = TRUE
+
+settings[basetheme_only] = base theme value
+settings[subtheme_override] = base theme value
diff --git a/modules/update/tests/themes/test_subtheme/test_subtheme.info b/modules/update/tests/themes/test_subtheme/test_subtheme.info
new file mode 100644
index 0000000..7da71e0
--- /dev/null
+++ b/modules/update/tests/themes/test_subtheme/test_subtheme.info
@@ -0,0 +1,7 @@
+name = Theme test subtheme
+description = Test theme which uses test_basetheme as the base theme.
+core = 7.x
+base theme = test_basetheme
+hidden = TRUE
+
+settings[subtheme_override] = subtheme value
-- 
1.7.8.3


From f03538354ddcbaef48fcd035ff6711f6b107f9b0 Mon Sep 17 00:00:00 2001
From: JohnAlbin <virtually.johnalbin@gmail.com>
Date: Fri, 27 Jan 2012 23:03:38 +0800
Subject: [PATCH 4/5] Theme settings forms do not need to rebuild the theme
 .info cache.

---
 modules/system/system.admin.inc |    2 +-
 1 files changed, 1 insertions(+), 1 deletions(-)

diff --git a/modules/system/system.admin.inc b/modules/system/system.admin.inc
index 8817118..56a5b41 100644
--- a/modules/system/system.admin.inc
+++ b/modules/system/system.admin.inc
@@ -392,7 +392,7 @@ function system_theme_settings($form, &$form_state, $key = '') {
   // Default settings are defined in theme_get_setting() in includes/theme.inc
   if ($key) {
     $var = 'theme_' . $key . '_settings';
-    $themes = system_rebuild_theme_data();
+    $themes = list_themes();
     $features = $themes[$key]->info['features'];
   }
   else {
-- 
1.7.8.3


From 0edb17b0a4a51b2b026c46b1375a6b22059960af Mon Sep 17 00:00:00 2001
From: JohnAlbin <virtually.johnalbin@gmail.com>
Date: Fri, 27 Jan 2012 23:54:14 +0800
Subject: [PATCH 5/5] Fixed documentation for list_themes().

---
 includes/theme.inc |    7 +++++--
 1 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/includes/theme.inc b/includes/theme.inc
index 1262b97..fca5b41 100644
--- a/includes/theme.inc
+++ b/includes/theme.inc
@@ -711,7 +711,7 @@ function _theme_build_registry($theme, $base_theme, $theme_engine) {
  *   names of the themes and the values are objects having the following
  *   properties:
  *   - 'filename': The name of the .info file.
- *   - 'name': The name of the theme.
+ *   - 'name': The machine name of the theme.
  *   - 'status': 1 for enabled, 0 for disabled themes.
  *   - 'info': The contents of the .info file.
  *   - 'stylesheets': A two dimensional array, using the first key for the
@@ -721,7 +721,10 @@ function _theme_build_registry($theme, $base_theme, $theme_engine) {
  *   - 'scripts': An associative array of JavaScripts, using the filename as key
  *     and the complete filepath as value.
  *   - 'engine': The name of the theme engine.
- *   - 'base theme': The name of the base theme.
+ *   - 'base_theme': The name of the base theme.
+ *   - 'base_themes': An ordered array of all the base themes. If the first item
+ *     is NULL, a base theme is missing for this theme.
+ *   - 'sub_themes': An unordered array of sub-themes of this theme.
  */
 function list_themes($refresh = FALSE) {
   $list = &drupal_static(__FUNCTION__, array());
-- 
1.7.8.3

