2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM Community Edition is a customer relationship management program developed by
5 * SugarCRM, Inc. Copyright (C) 2004-2011 SugarCRM Inc.
7 * This program is free software; you can redistribute it and/or modify it under
8 * the terms of the GNU Affero General Public License version 3 as published by the
9 * Free Software Foundation with the addition of the following permission added
10 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
11 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
12 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 * FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
19 * You should have received a copy of the GNU Affero General Public License along with
20 * this program; if not, see http://www.gnu.org/licenses or write to the Free
21 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
24 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
25 * SW2-130, Cupertino, CA 95014, USA. or at email address contact@sugarcrm.com.
27 * The interactive user interfaces in modified source and object code versions
28 * of this program must display Appropriate Legal Notices, as required under
29 * Section 5 of the GNU Affero General Public License version 3.
31 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
32 * these Appropriate Legal Notices must retain the display of the "Powered by
33 * SugarCRM" logo. If the display of the logo is not reasonably feasible for
34 * technical reasons, the Appropriate Legal Notices must display the words
35 * "Powered by SugarCRM".
36 ********************************************************************************/
39 /*********************************************************************************
41 * Description: Contains a variety of utility functions used to display UI
42 * components such as form headers and footers. Intended to be modified on a per
44 ********************************************************************************/
46 if(!defined('JSMIN_AS_LIB'))
47 define('JSMIN_AS_LIB', true);
49 require_once("include/SugarTheme/cssmin.php");
50 require_once("jssource/jsmin.php");
51 require_once('include/utils/sugar_file_utils.php');
54 * Class that provides tools for working with a theme.
70 protected $description;
73 * Defines which parent files to not include
77 protected $ignoreParentFiles = array();
80 * Defines which parent files to not include
84 protected $directionality = 'ltr';
86 * Theme directory name
97 protected $parentTheme;
100 * Colors sets provided by the theme
102 * @deprecated only here for BC during upgrades
105 protected $colors = array();
108 * Font sets provided by the theme
110 * @deprecated only here for BC during upgrades
113 protected $fonts = array();
116 * Maximum sugar version this theme is for; defaults to 5.5.1 as all the themes without this
117 * parameter as assumed to work thru 5.5.1
121 protected $version = '5.5.1';
124 * Colors used in bar charts
128 protected $barChartColors = array(
129 "docBorder" => "0xffffff",
130 "docBg1" => "0xffffff",
131 "docBg2" => "0xffffff",
132 "xText" => "0x33485c",
133 "yText" => "0x33485c",
134 "title" => "0x333333",
135 "misc" => "0x999999",
136 "altBorder" => "0xffffff",
137 "altBg" => "0xffffff",
138 "altText" => "0x666666",
139 "graphBorder" => "0xcccccc",
140 "graphBg1" => "0xf6f6f6",
141 "graphBg2" => "0xf6f6f6",
142 "graphLines" => "0xcccccc",
143 "graphText" => "0x333333",
144 "graphTextShadow" => "0xf9f9f9",
145 "barBorder" => "0xeeeeee",
146 "barBorderHilite" => "0x333333",
147 "legendBorder" => "0xffffff",
148 "legendBg1" => "0xffffff",
149 "legendBg2" => "0xffffff",
150 "legendText" => "0x444444",
151 "legendColorKeyBorder" => "0x777777",
152 "scrollBar" => "0xcccccc",
153 "scrollBarBorder" => "0xeeeeee",
154 "scrollBarTrack" => "0xeeeeee",
155 "scrollBarTrackBorder" => "0xcccccc",
159 * Colors used in pie charts
163 protected $pieChartColors = array(
164 "docBorder" => "0xffffff",
165 "docBg1" => "0xffffff",
166 "docBg2" => "0xffffff",
167 "title" => "0x333333",
168 "subtitle" => "0x666666",
169 "misc" => "0x999999",
170 "altBorder" => "0xffffff",
171 "altBg" => "0xffffff",
172 "altText" => "0x666666",
173 "graphText" => "0x33485c",
174 "graphTextShadow" => "0xf9f9f9",
175 "pieBorder" => "0xffffff",
176 "pieBorderHilite" => "0x333333",
177 "legendBorder" => "0xffffff",
178 "legendBg1" => "0xffffff",
179 "legendBg2" => "0xffffff",
180 "legendText" => "0x444444",
181 "legendColorKeyBorder" => "0x777777",
182 "scrollBar" => "0xdfdfdf",
183 "scrollBarBorder" => "0xfafafa",
184 "scrollBarTrack" => "0xeeeeee",
185 "scrollBarTrackBorder" => "0xcccccc",
189 * Does this theme support group tabs
193 protected $group_tabs;
197 * Cache built of all css files locations
201 private $_cssCache = array();
204 * Cache built of all image files locations
208 private $_imageCache = array();
211 * Cache built of all javascript files locations
215 private $_jsCache = array();
218 * Cache built of all template files locations
222 private $_templateCache = array();
225 * Size of the caches after the are initialized in the constructor
229 private $_initialCacheSize = array(
233 'templateCache' => 0,
237 * Controls whether or not to clear the cache on destroy; defaults to false
239 private $_clearCacheOnDestroy = false;
241 private $imageExtensions = array(
252 * Sets the theme properties from the defaults passed to it, and loads the file path cache from an external cache
254 * @param $defaults string defaults for the current theme
256 public function __construct(
260 // apply parent theme's properties first
261 if ( isset($defaults['parentTheme']) ) {
263 include("themes/{$defaults['parentTheme']}/themedef.php");
264 foreach ( $themedef as $key => $value ) {
265 if ( property_exists(__CLASS__,$key) ) {
266 // For all arrays ( except colors and fonts ) you can just specify the items
267 // to change instead of all of the values
268 if ( is_array($this->$key) && !in_array($key,array('colors','fonts')) )
269 $this->$key = array_merge($this->$key,$value);
271 $this->$key = $value;
275 foreach ( $defaults as $key => $value ) {
276 if ( property_exists(__CLASS__,$key) ) {
277 // For all arrays ( except colors and fonts ) you can just specify the items
278 // to change instead of all of the values
279 if ( is_array($this->$key) && !in_array($key,array('colors','fonts')) )
280 $this->$key = array_merge($this->$key,$value);
282 $this->$key = $value;
285 if ( !inDeveloperMode() ) {
286 if ( sugar_is_file($GLOBALS['sugar_config']['cache_dir'].$this->getFilePath().'/pathCache.php') ) {
287 $caches = unserialize(sugar_file_get_contents($GLOBALS['sugar_config']['cache_dir'].$this->getFilePath().'/pathCache.php'));
288 if ( isset($caches['jsCache']) )
289 $this->_jsCache = $caches['jsCache'];
290 if ( isset($caches['cssCache']) )
291 $this->_cssCache = $caches['cssCache'];
292 if ( isset($caches['imageCache']) )
293 $this->_imageCache = $caches['imageCache'];
294 if ( isset($caches['templateCache']) )
295 $this->_templateCache = $caches['templateCache'];
298 $this->_initialCacheSize = array(
299 'jsCache' => count($this->_jsCache),
300 'cssCache' => count($this->_cssCache),
301 'imageCache' => count($this->_imageCache),
302 'templateCache' => count($this->_templateCache),
308 * Here we'll write out the internal file path caches to an external cache of some sort.
310 public function __destruct()
312 // Bug 28309 - Set the current directory to one which we expect it to be (i.e. the root directory of the install
313 set_include_path(realpath(dirname(__FILE__) . '/../..') . PATH_SEPARATOR . get_include_path());
314 chdir(realpath(dirname(__FILE__) . '/../..'));
316 // clear out the cache on destroy if we are asked to
317 if ( $this->_clearCacheOnDestroy ) {
318 if (is_file($GLOBALS['sugar_config']['cache_dir'].$this->getFilePath().'/pathCache.php'))
319 unlink($GLOBALS['sugar_config']['cache_dir'].$this->getFilePath().'/pathCache.php');
321 elseif ( !inDeveloperMode() ) {
322 // only update the caches if they have been changed in this request
323 if ( count($this->_jsCache) != $this->_initialCacheSize['jsCache']
324 || count($this->_cssCache) != $this->_initialCacheSize['cssCache']
325 || count($this->_imageCache) != $this->_initialCacheSize['imageCache']
326 || count($this->_templateCache) != $this->_initialCacheSize['templateCache']
328 sugar_file_put_contents(
329 create_cache_directory($this->getFilePath().'/pathCache.php'),
332 'jsCache' => $this->_jsCache,
333 'cssCache' => $this->_cssCache,
334 'imageCache' => $this->_imageCache,
335 'templateCache' => $this->_templateCache,
345 * Specifies what is returned when the object is cast to a string, in this case it will be the
346 * theme directory name.
348 * @return string theme directory name
350 public function __toString()
352 return $this->dirName;
356 * Generic public accessor method for all the properties of the theme ( which are kept protected )
360 public function __get(
364 if ( isset($this->$key) )
368 public function __isset($key){
369 return isset($this->$key);
373 public function clearJSCache()
375 $this->_jsCache = array();
379 * Clears out the caches used for this themes
381 public function clearCache()
383 $this->_clearCacheOnDestroy = true;
387 * Return array of all valid fields that can be specified in the themedef.php file
391 public static function getThemeDefFields()
410 * Returns the file path of the current theme
414 public function getFilePath()
416 return 'themes/'.$this->dirName;
420 * Returns the image path of the current theme
424 public function getImagePath()
426 return $this->getFilePath().'/images';
430 * Returns the css path of the current theme
434 public function getCSSPath()
436 return $this->getFilePath().'/css';
440 * Returns the javascript path of the current theme
444 public function getJSPath()
446 return $this->getFilePath().'/js';
450 * Returns the tpl path of the current theme
454 public function getTemplatePath()
456 return $this->getFilePath().'/tpls';
460 * Returns the file path of the theme defaults
464 public final function getDefaultFilePath()
466 return 'themes/default';
470 * Returns the image path of the theme defaults
474 public final function getDefaultImagePath()
476 return $this->getDefaultFilePath().'/images';
480 * Returns the css path of the theme defaults
484 public final function getDefaultCSSPath()
486 return $this->getDefaultFilePath().'/css';
490 * Returns the template path of the theme defaults
494 public final function getDefaultTemplatePath()
496 return $this->getDefaultFilePath().'/tpls';
500 * Returns the javascript path of the theme defaults
504 public final function getDefaultJSPath()
506 return $this->getDefaultFilePath().'/js';
510 * Returns CSS for the current theme.
512 * @param $color string optional, specifies the css color file to use if the theme supports it; defaults to cookie value or theme default
513 * @param $font string optional, specifies the css font file to use if the theme supports it; defaults to cookie value or theme default
514 * @return string HTML code
516 public function getCSS(
521 // include style.css file
522 $html = '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('yui.css').'" />';
523 $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('deprecated.css').'" />';
524 $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('style.css').'" />';
526 // for BC during upgrade
527 if ( !empty($this->colors) ) {
528 if ( isset($_SESSION['authenticated_user_theme_color']) && in_array($_SESSION['authenticated_user_theme_color'], $this->colors))
529 $color = $_SESSION['authenticated_user_theme_color'];
531 $color = $this->colors[0];
532 $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('colors.'.$color.'.css').'" id="current_color_style" />';
535 if ( !empty($this->fonts) ) {
536 if ( isset($_SESSION['authenticated_user_theme_font']) && in_array($_SESSION['authenticated_user_theme_font'], $this->fonts))
537 $font = $_SESSION['authenticated_user_theme_font'];
539 $font = $this->fonts[0];
540 $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('fonts.'.$font.'.css').'" id="current_font_style" />';
547 * Returns javascript for the current theme
549 * @return string HTML code
551 public function getJS()
553 $styleJS = $this->getJSURL('style.js');
555 <script type="text/javascript" src="$styleJS"></script>
560 * Returns the path for the tpl file in the current theme. If not found in the current theme, will revert
561 * to looking in the base theme.
563 * @param string $templateName tpl file name
564 * @return string path of tpl file to include
566 public function getTemplate(
570 if ( isset($this->_templateCache[$templateName]) )
571 return $this->_templateCache[$templateName];
574 if (sugar_is_file('custom/'.$this->getTemplatePath().'/'.$templateName))
575 $templatePath = 'custom/'.$this->getTemplatePath().'/'.$templateName;
576 elseif (sugar_is_file($this->getTemplatePath().'/'.$templateName))
577 $templatePath = $this->getTemplatePath().'/'.$templateName;
578 elseif (isset($this->parentTheme)
579 && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
580 && ($filename = SugarThemeRegistry::get($this->parentTheme)->getTemplate($templateName)) != '')
581 $templatePath = $filename;
582 elseif (sugar_is_file('custom/'.$this->getDefaultTemplatePath().'/'.$templateName))
583 $templatePath = 'custom/'.$this->getDefaultTemplatePath().'/'.$templateName;
584 elseif (sugar_is_file($this->getDefaultTemplatePath().'/'.$templateName))
585 $templatePath = $this->getDefaultTemplatePath().'/'.$templateName;
587 $GLOBALS['log']->warn("Template $templateName not found");
591 $this->_imageCache[$templateName] = $templatePath;
593 return $templatePath;
597 * Returns an image tag for the given image.
599 * @param string $image image name
600 * @param string $other_attributes optional, other attributes to add to the image tag, not cached
601 * @param string $width optional, defaults to the actual image's width
602 * @param string $height optional, defaults to the actual image's height
603 * @return string HTML image tag
605 public function getImage(
607 $other_attributes = '',
613 static $cached_results = array();
616 if(!empty($cached_results[$imageName]))
617 return $cached_results[$imageName]."$other_attributes />";
619 $imageURL = $this->getImageURL($imageName,false);
620 if ( empty($imageURL) )
623 $size = getimagesize($imageURL);
624 if ( is_null($width) )
626 if ( is_null($height) )
629 // Cache everything but the other attributes....
630 $cached_results[$imageName] = "<img src=\"". getJSPath($imageURL) ."\" width=\"$width\" height=\"$height\" ";
632 return $cached_results[$imageName] . "$other_attributes />";
636 * Returns the URL for an image in the current theme. If not found in the current theme, will revert
637 * to looking in the base theme.
639 * @param string $imageName image file name
640 * @param bool $addJSPath call getJSPath() with the results to add some unique image tracking support
641 * @return string path to image
643 public function getImageURL(
648 if ( isset($this->_imageCache[$imageName]) ) {
650 return getJSPath($this->_imageCache[$imageName]);
652 return $this->_imageCache[$imageName];
655 if (($filename = $this->_getImageFileName('custom/'.$this->getImagePath().'/'.$imageName)) != '')
656 $imagePath = $filename;
657 elseif (($filename = $this->_getImageFileName($this->getImagePath().'/'.$imageName)) != '')
658 $imagePath = $filename;
659 elseif (isset($this->parentTheme)
660 && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
661 && ($filename = SugarThemeRegistry::get($this->parentTheme)->getImageURL($imageName,false)) != '')
662 $imagePath = $filename;
663 elseif (($filename = $this->_getImageFileName('custom/'.$this->getDefaultImagePath().'/'.$imageName)) != '')
664 $imagePath = $filename;
665 elseif (($filename = $this->_getImageFileName($this->getDefaultImagePath().'/'.$imageName)) != '')
666 $imagePath = $filename;
668 $GLOBALS['log']->warn("Image $imageName not found");
672 $this->_imageCache[$imageName] = $imagePath;
675 return getJSPath($imagePath);
681 * Checks for an image using all of the accepted image extensions
683 * @param string $imageName image file name
684 * @return string path to image
686 protected function _getImageFileName(
690 // return now if the extension matches that of which we are looking for
691 if ( sugar_is_file($imageName) )
693 $pathParts = pathinfo($imageName);
694 foreach ( $this->imageExtensions as $extension )
695 if ( isset($pathParts['extension']) )
696 if ( ( $extension != $pathParts['extension'] )
697 && sugar_is_file($pathParts['dirname'].'/'.$pathParts['filename'].'.'.$extension) )
698 return $pathParts['dirname'].'/'.$pathParts['filename'].'.'.$extension;
704 * Returns the URL for the css file in the current theme. If not found in the current theme, will revert
705 * to looking in the base theme.
707 * @param string $cssFileName css file name
708 * @param bool $addJSPath call getJSPath() with the results to add some unique image tracking support
709 * @return string path of css file to include
711 public function getCSSURL(
716 if ( isset($this->_cssCache[$cssFileName])) {
718 return getJSPath($this->_cssCache[$cssFileName]);
720 return $this->_cssCache[$cssFileName];
723 $cssFileContents = '';
724 if (isset($this->parentTheme)
725 && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
726 && ($filename = SugarThemeRegistry::get($this->parentTheme)->getCSSURL($cssFileName,false)) != '')
727 $cssFileContents .= file_get_contents($filename);
729 if (sugar_is_file($this->getDefaultCSSPath().'/'.$cssFileName))
730 $cssFileContents .= file_get_contents($this->getDefaultCSSPath().'/'.$cssFileName);
731 if (sugar_is_file('custom/'.$this->getDefaultCSSPath().'/'.$cssFileName))
732 $cssFileContents .= file_get_contents('custom/'.$this->getDefaultCSSPath().'/'.$cssFileName);
734 if (sugar_is_file($this->getCSSPath().'/'.$cssFileName))
735 $cssFileContents .= file_get_contents($this->getCSSPath().'/'.$cssFileName);
736 if (sugar_is_file('custom/'.$this->getCSSPath().'/'.$cssFileName))
737 $cssFileContents .= file_get_contents('custom/'.$this->getCSSPath().'/'.$cssFileName);
738 if (empty($cssFileContents)) {
739 $GLOBALS['log']->warn("CSS File $cssFileName not found");
743 // fix any image references that may be defined in css files
744 $cssFileContents = str_ireplace("entryPoint=getImage&",
745 "entryPoint=getImage&themeName={$this->dirName}&",
748 // create the cached file location
749 $cssFilePath = create_cache_directory($this->getCSSPath()."/$cssFileName");
751 // if this is the style.css file, prepend the base.css and calendar-win2k-cold-1.css
752 // files before the theme styles
753 if ( $cssFileName == 'style.css' && !isset($this->parentTheme) ) {
754 if ( inDeveloperMode() )
755 $cssFileContents = file_get_contents('include/javascript/yui/build/base/base.css') . $cssFileContents;
757 $cssFileContents = file_get_contents('include/javascript/yui/build/base/base-min.css') . $cssFileContents;
761 if ( !inDeveloperMode() && !sugar_is_file($cssFilePath) ) {
762 $cssFileContents = cssmin::minify($cssFileContents);
765 // now write the css to cache
766 sugar_file_put_contents($cssFilePath,$cssFileContents);
768 $this->_cssCache[$cssFileName] = $cssFilePath;
771 return getJSPath($cssFilePath);
777 * Returns the URL for an image in the current theme. If not found in the current theme, will revert
778 * to looking in the base theme.
780 * @param string $jsFileName js file name
781 * @param bool $addJSPath call getJSPath() with the results to add some unique image tracking support
782 * @return string path to js file
784 public function getJSURL(
789 if ( isset($this->_jsCache[$jsFileName])) {
791 return getJSPath($this->_jsCache[$jsFileName]);
793 return $this->_jsCache[$jsFileName];
796 $jsFileContents = '';
798 if (isset($this->parentTheme)
799 && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
800 && ($filename = SugarThemeRegistry::get($this->parentTheme)->getJSURL($jsFileName,false)) != '' && !in_array($jsFileName,$this->ignoreParentFiles))
801 $jsFileContents .= file_get_contents($filename);
803 if (sugar_is_file($this->getDefaultJSPath().'/'.$jsFileName))
804 $jsFileContents .= file_get_contents($this->getDefaultJSPath().'/'.$jsFileName);
805 if (sugar_is_file('custom/'.$this->getDefaultJSPath().'/'.$jsFileName))
806 $jsFileContents .= file_get_contents('custom/'.$this->getDefaultJSPath().'/'.$jsFileName);
808 if (sugar_is_file($this->getJSPath().'/'.$jsFileName))
809 $jsFileContents .= file_get_contents($this->getJSPath().'/'.$jsFileName);
810 if (sugar_is_file('custom/'.$this->getJSPath().'/'.$jsFileName))
811 $jsFileContents .= file_get_contents('custom/'.$this->getJSPath().'/'.$jsFileName);
812 if (empty($jsFileContents)) {
813 $GLOBALS['log']->warn("Javascript File $jsFileName not found");
817 // create the cached file location
818 $jsFilePath = create_cache_directory($this->getJSPath()."/$jsFileName");
821 if ( !inDeveloperMode()&& !sugar_is_file(str_replace('.js','-min.js',$jsFilePath)) ) {
822 $jsFileContents = JSMin::minify($jsFileContents);
823 $jsFilePath = str_replace('.js','-min.js',$jsFilePath);
826 // now write the js to cache
827 sugar_file_put_contents($jsFilePath,$jsFileContents);
829 $this->_jsCache[$jsFileName] = $jsFilePath;
832 return getJSPath($jsFilePath);
838 * Returns an array of all of the images available for the current theme
842 public function getAllImages()
844 // first, lets get all the paths of where to look
845 $pathsToSearch = array($this->getImagePath());
847 while (isset($theme->parentTheme) && SugarThemeRegistry::get($theme->parentTheme) instanceOf SugarTheme ) {
848 $theme = SugarThemeRegistry::get($theme->parentTheme);
849 $pathsToSearch[] = $theme->getImagePath();
851 $pathsToSearch[] = $this->getDefaultImagePath();
853 // now build the array
854 $imageArray = array();
855 foreach ( $pathsToSearch as $path )
857 if (!sugar_is_dir($path)) $path = "custom/$path";
858 if (sugar_is_dir($path) && is_readable($path) && $dir = opendir($path)) {
859 while (($file = readdir($dir)) !== false) {
867 if ( !isset($imageArray[$file]) )
868 $imageArray[$file] = $this->getImageURL($file,false);
882 * Registry for all the current classes in the system
884 class SugarThemeRegistry
887 * Array of all themes and thier object
891 private static $_themes = array();
894 * Name of the current theme; corresponds to an index key in SugarThemeRegistry::$_themes
898 private static $_currentTheme;
901 * Disable the constructor since this will be a singleton
903 private function __construct() {}
906 * Adds a new theme to the registry
908 * @param $themedef array
910 public static function add(
914 // make sure the we know the sugar version
915 if ( !isset($GLOBALS['sugar_version']) ) {
916 include('sugar_version.php');
917 $GLOBALS['sugar_version'] = $sugar_version;
920 // Assume theme is designed for 5.5.x if not specified otherwise
921 if ( !isset($themedef['version']) )
922 $themedef['version']['regex_matches'] = array('5\.5\.*');
924 // Check to see if theme is valid for this version of Sugar; return false if not
926 if( isset($themedef['version']['exact_matches']) ){
927 $matches_empty = false;
928 foreach( $themedef['version']['exact_matches'] as $match ){
929 if( $match == $GLOBALS['sugar_version'] ){
934 if( !$version_ok && isset($themedef['version']['regex_matches']) ){
935 $matches_empty = false;
936 foreach( $themedef['version']['regex_matches'] as $match ){
937 if( preg_match( "/$match/", $GLOBALS['sugar_version'] ) ){
945 $theme = new SugarTheme($themedef);
946 self::$_themes[$theme->dirName] = $theme;
950 * Removes a new theme from the registry
952 * @param $themeName string
954 public static function remove(
958 if ( self::exists($themeName) )
959 unset(self::$_themes[$themeName]);
963 * Returns a theme object in the registry specified by the given $themeName
965 * @param $themeName string
967 public static function get(
971 if ( isset(self::$_themes[$themeName]) )
972 return self::$_themes[$themeName];
976 * Returns the current theme object
978 * @return SugarTheme object
980 public static function current()
982 if ( !isset(self::$_currentTheme) )
983 self::buildRegistry();
985 return self::$_themes[self::$_currentTheme];
989 * Returns the default theme object
991 * @return SugarTheme object
993 public static function getDefault()
995 if ( !isset(self::$_currentTheme) )
996 self::buildRegistry();
998 if ( isset($GLOBALS['sugar_config']['default_theme']) && self::exists($GLOBALS['sugar_config']['default_theme']) ) {
999 return self::get($GLOBALS['sugar_config']['default_theme']);
1002 return self::get(array_pop(array_keys(self::availableThemes())));
1006 * Returns true if a theme object specified by the given $themeName exists in the registry
1008 * @param $themeName string
1011 public static function exists(
1015 return (self::get($themeName) !== null);
1019 * Sets the given $themeName to be the current theme
1021 * @param $themeName string
1023 public static function set(
1027 if ( !self::exists($themeName) )
1030 self::$_currentTheme = $themeName;
1032 // set some of the expected globals
1033 $GLOBALS['barChartColors'] = self::current()->barChartColors;
1034 $GLOBALS['pieChartColors'] = self::current()->pieChartColors;
1039 * Builds the theme registry
1041 public static function buildRegistry()
1043 self::$_themes = array();
1044 $dirs = array("themes/","custom/themes/");
1046 // check for a default themedef file
1047 $themedefDefault = array();
1048 if ( sugar_is_file("custom/themes/default/themedef.php") ) {
1049 $themedef = array();
1050 require("custom/themes/default/themedef.php");
1051 $themedefDefault = $themedef;
1054 foreach ($dirs as $dirPath ) {
1055 if (sugar_is_dir('./'.$dirPath) && is_readable('./'.$dirPath) && $dir = opendir('./'.$dirPath)) {
1056 while (($file = readdir($dir)) !== false) {
1062 || $file == "default"
1063 || !sugar_is_dir("./$dirPath".$file)
1064 || !sugar_is_file("./{$dirPath}{$file}/themedef.php")
1067 $themedef = array();
1068 require("./{$dirPath}{$file}/themedef.php");
1069 $themedef = array_merge($themedef,$themedefDefault);
1070 $themedef['dirName'] = $file;
1071 // check for theme already existing in the registry
1072 // if so, then it will override the current one
1073 if ( self::exists($themedef['dirName']) ) {
1074 $existingTheme = self::get($themedef['dirName']);
1075 foreach ( SugarTheme::getThemeDefFields() as $field )
1076 if ( !isset($themedef[$field]) )
1077 $themedef[$field] = $existingTheme->$field;
1078 self::remove($themedef['dirName']);
1080 if ( isset($themedef['name']) ) {
1081 self::add($themedef);
1087 // default to setting the default theme as the current theme
1088 if ( !isset($GLOBALS['sugar_config']['default_theme']) || !self::set($GLOBALS['sugar_config']['default_theme']) ) {
1089 if ( count(self::availableThemes()) == 0 )
1090 sugar_die('No valid themes are found on this instance');
1092 self::set(array_pop(array_keys(self::availableThemes())));
1097 * Returns an array of available themes. Designed to be absorbed into get_select_options_with_id()
1101 public static function availableThemes()
1103 $themelist = array();
1104 $disabledThemes = array();
1105 if ( isset($GLOBALS['sugar_config']['disabled_themes']) )
1106 $disabledThemes = explode(',',$GLOBALS['sugar_config']['disabled_themes']);
1108 foreach ( self::$_themes as $themename => $themeobject ) {
1109 if ( in_array($themename,$disabledThemes) )
1111 $themelist[$themeobject->dirName] = $themeobject->name;
1113 asort($themelist, SORT_STRING);
1118 * Returns an array of un-available themes. Designed used with the theme selector in the admin panel
1122 public static function unAvailableThemes()
1124 $themelist = array();
1125 $disabledThemes = array();
1126 if ( isset($GLOBALS['sugar_config']['disabled_themes']) )
1127 $disabledThemes = explode(',',$GLOBALS['sugar_config']['disabled_themes']);
1129 foreach ( self::$_themes as $themename => $themeobject ) {
1130 if ( in_array($themename,$disabledThemes) )
1131 $themelist[$themeobject->dirName] = $themeobject->name;
1138 * Returns an array of all themes found in the current installation
1142 public static function allThemes()
1144 $themelist = array();
1146 foreach ( self::$_themes as $themename => $themeobject )
1147 $themelist[$themeobject->dirName] = $themeobject->name;
1153 * Clears out the cached path locations for all themes
1155 public static function clearAllCaches()
1157 foreach ( self::$_themes as $themeobject ) {
1158 $themeobject->clearCache();