2 if(!defined('sugarEntry') || !sugarEntry) die('Not A Valid Entry Point');
3 /*********************************************************************************
4 * SugarCRM 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 * Theme directory name
84 protected $parentTheme;
87 * Colors sets provided by the theme
89 * @deprecated only here for BC during upgrades
92 protected $colors = array();
95 * Font sets provided by the theme
97 * @deprecated only here for BC during upgrades
100 protected $fonts = array();
103 * Maximum sugar version this theme is for; defaults to 5.5.1 as all the themes without this
104 * parameter as assumed to work thru 5.5.1
108 protected $version = '5.5.1';
111 * Colors used in bar charts
115 protected $barChartColors = array(
116 "docBorder" => "0xffffff",
117 "docBg1" => "0xffffff",
118 "docBg2" => "0xffffff",
119 "xText" => "0x33485c",
120 "yText" => "0x33485c",
121 "title" => "0x333333",
122 "misc" => "0x999999",
123 "altBorder" => "0xffffff",
124 "altBg" => "0xffffff",
125 "altText" => "0x666666",
126 "graphBorder" => "0xcccccc",
127 "graphBg1" => "0xf6f6f6",
128 "graphBg2" => "0xf6f6f6",
129 "graphLines" => "0xcccccc",
130 "graphText" => "0x333333",
131 "graphTextShadow" => "0xf9f9f9",
132 "barBorder" => "0xeeeeee",
133 "barBorderHilite" => "0x333333",
134 "legendBorder" => "0xffffff",
135 "legendBg1" => "0xffffff",
136 "legendBg2" => "0xffffff",
137 "legendText" => "0x444444",
138 "legendColorKeyBorder" => "0x777777",
139 "scrollBar" => "0xcccccc",
140 "scrollBarBorder" => "0xeeeeee",
141 "scrollBarTrack" => "0xeeeeee",
142 "scrollBarTrackBorder" => "0xcccccc",
146 * Colors used in pie charts
150 protected $pieChartColors = array(
151 "docBorder" => "0xffffff",
152 "docBg1" => "0xffffff",
153 "docBg2" => "0xffffff",
154 "title" => "0x333333",
155 "subtitle" => "0x666666",
156 "misc" => "0x999999",
157 "altBorder" => "0xffffff",
158 "altBg" => "0xffffff",
159 "altText" => "0x666666",
160 "graphText" => "0x33485c",
161 "graphTextShadow" => "0xf9f9f9",
162 "pieBorder" => "0xffffff",
163 "pieBorderHilite" => "0x333333",
164 "legendBorder" => "0xffffff",
165 "legendBg1" => "0xffffff",
166 "legendBg2" => "0xffffff",
167 "legendText" => "0x444444",
168 "legendColorKeyBorder" => "0x777777",
169 "scrollBar" => "0xdfdfdf",
170 "scrollBarBorder" => "0xfafafa",
171 "scrollBarTrack" => "0xeeeeee",
172 "scrollBarTrackBorder" => "0xcccccc",
176 * Does this theme support group tabs
180 protected $group_tabs;
184 * Cache built of all css files locations
188 private $_cssCache = array();
191 * Cache built of all image files locations
195 private $_imageCache = array();
198 * Cache built of all javascript files locations
202 private $_jsCache = array();
205 * Cache built of all template files locations
209 private $_templateCache = array();
212 * Size of the caches after the are initialized in the constructor
216 private $_initialCacheSize = array(
220 'templateCache' => 0,
224 * Controls whether or not to clear the cache on destroy; defaults to false
226 private $_clearCacheOnDestroy = false;
228 private $imageExtensions = array(
239 * Sets the theme properties from the defaults passed to it, and loads the file path cache from an external cache
241 * @param $defaults string defaults for the current theme
243 public function __construct(
247 // apply parent theme's properties first
248 if ( isset($defaults['parentTheme']) ) {
250 include("themes/{$defaults['parentTheme']}/themedef.php");
251 foreach ( $themedef as $key => $value ) {
252 if ( property_exists(__CLASS__,$key) ) {
253 // For all arrays ( except colors and fonts ) you can just specify the items
254 // to change instead of all of the values
255 if ( is_array($this->$key) && !in_array($key,array('colors','fonts')) )
256 $this->$key = array_merge($this->$key,$value);
258 $this->$key = $value;
262 foreach ( $defaults as $key => $value ) {
263 if ( property_exists(__CLASS__,$key) ) {
264 // For all arrays ( except colors and fonts ) you can just specify the items
265 // to change instead of all of the values
266 if ( is_array($this->$key) && !in_array($key,array('colors','fonts')) )
267 $this->$key = array_merge($this->$key,$value);
269 $this->$key = $value;
272 if ( !inDeveloperMode() ) {
273 // load stored theme cache from sugar cache if it's there
274 if ( $GLOBALS['external_cache_enabled']
275 && $GLOBALS['external_cache_type'] != 'base-in-memory' ) {
276 $this->_jsCache = sugar_cache_retrieve('theme_'.$this->dirName.'_jsCache');
277 $this->_cssCache = sugar_cache_retrieve('theme_'.$this->dirName.'_cssCache');
278 $this->_imageCache = sugar_cache_retrieve('theme_'.$this->dirName.'_imageCache');
279 $this->_templateCache = sugar_cache_retrieve('theme_'.$this->dirName.'_templateCache');
281 // otherwise, see if we serialized them to a file
282 elseif ( sugar_is_file($GLOBALS['sugar_config']['cache_dir'].$this->getFilePath().'/pathCache.php') ) {
283 $caches = unserialize(file_get_contents($GLOBALS['sugar_config']['cache_dir'].$this->getFilePath().'/pathCache.php'));
284 if ( isset($caches['jsCache']) )
285 $this->_jsCache = $caches['jsCache'];
286 if ( isset($caches['cssCache']) )
287 $this->_cssCache = $caches['cssCache'];
288 if ( isset($caches['imageCache']) )
289 $this->_imageCache = $caches['imageCache'];
290 if ( isset($caches['templateCache']) )
291 $this->_templateCache = $caches['templateCache'];
294 $this->_initialCacheSize = array(
295 'jsCache' => count($this->_jsCache),
296 'cssCache' => count($this->_cssCache),
297 'imageCache' => count($this->_imageCache),
298 'templateCache' => count($this->_templateCache),
304 * Here we'll write out the internal file path caches to an external cache of some sort.
306 public function __destruct()
308 // Bug 28309 - Set the current directory to one which we expect it to be (i.e. the root directory of the install
309 set_include_path(realpath(dirname(__FILE__) . '/../..') . PATH_SEPARATOR . get_include_path());
310 chdir(realpath(dirname(__FILE__) . '/../..'));
312 // Bug 30807/30808 - Re-setup the external cache since the object isn't there when calling this method.
313 $GLOBALS['external_cache_checked'] = false;
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');
320 if ( $GLOBALS['external_cache_enabled']
321 && $GLOBALS['external_cache_type'] != 'base-in-memory' ) {
322 sugar_cache_clear('theme_'.$this->dirName.'_jsCache');
323 sugar_cache_clear('theme_'.$this->dirName.'_cssCache');
324 sugar_cache_clear('theme_'.$this->dirName.'_imageCache');
325 sugar_cache_clear('theme_'.$this->dirName.'_templateCache');
328 elseif ( !inDeveloperMode() ) {
329 // push our cache into the sugar cache
330 if ( $GLOBALS['external_cache_enabled']
331 && $GLOBALS['external_cache_type'] != 'base-in-memory' ) {
332 // only update the caches if they have been changed in this request
333 if ( count($this->_jsCache) != $this->_initialCacheSize['jsCache'] )
334 sugar_cache_put('theme_'.$this->dirName.'_jsCache',$this->_jsCache);
335 if ( count($this->_cssCache) != $this->_initialCacheSize['cssCache'] )
336 sugar_cache_put('theme_'.$this->dirName.'_cssCache',$this->_cssCache);
337 if ( count($this->_imageCache) != $this->_initialCacheSize['imageCache'] )
338 sugar_cache_put('theme_'.$this->dirName.'_imageCache',$this->_imageCache);
339 if ( count($this->_templateCache) != $this->_initialCacheSize['templateCache'] )
340 sugar_cache_put('theme_'.$this->dirName.'_templateCache',$this->_templateCache);
342 // fallback in case there is no useful external caching available
343 // only update the caches if they have been changed in this request
344 elseif ( count($this->_jsCache) != $this->_initialCacheSize['jsCache']
345 || count($this->_cssCache) != $this->_initialCacheSize['cssCache']
346 || count($this->_imageCache) != $this->_initialCacheSize['imageCache']
347 || count($this->_templateCache) != $this->_initialCacheSize['templateCache']
349 sugar_file_put_contents(
350 create_cache_directory($this->getFilePath().'/pathCache.php'),
353 'jsCache' => $this->_jsCache,
354 'cssCache' => $this->_cssCache,
355 'imageCache' => $this->_imageCache,
356 'templateCache' => $this->_templateCache,
363 // clear out the cache if we are in developerMode
364 // ( so it will be freshly rebuilt for the next load )
365 elseif ( $GLOBALS['external_cache_enabled'] ) {
366 sugar_cache_clear('theme_'.$this->dirName.'_jsCache');
367 sugar_cache_clear('theme_'.$this->dirName.'_cssCache');
368 sugar_cache_clear('theme_'.$this->dirName.'_imageCache');
369 sugar_cache_clear('theme_'.$this->dirName.'_templateCache');
374 * Specifies what is returned when the object is cast to a string, in this case it will be the
375 * theme directory name.
377 * @return string theme directory name
379 public function __toString()
381 return $this->dirName;
385 * Generic public accessor method for all the properties of the theme ( which are kept protected )
389 public function __get(
393 if ( isset($this->$key) )
397 public function __isset($key){
398 return isset($this->$key);
403 * Clears out the caches used for this themes
405 public function clearCache()
407 $this->_clearCacheOnDestroy = true;
411 * Return array of all valid fields that can be specified in the themedef.php file
415 public static function getThemeDefFields()
432 * Returns the file path of the current theme
436 public function getFilePath()
438 return 'themes/'.$this->dirName;
442 * Returns the image path of the current theme
446 public function getImagePath()
448 return $this->getFilePath().'/images';
452 * Returns the css path of the current theme
456 public function getCSSPath()
458 return $this->getFilePath().'/css';
462 * Returns the javascript path of the current theme
466 public function getJSPath()
468 return $this->getFilePath().'/js';
472 * Returns the tpl path of the current theme
476 public function getTemplatePath()
478 return $this->getFilePath().'/tpls';
482 * Returns the file path of the theme defaults
486 public final function getDefaultFilePath()
488 return 'themes/default';
492 * Returns the image path of the theme defaults
496 public final function getDefaultImagePath()
498 return $this->getDefaultFilePath().'/images';
502 * Returns the css path of the theme defaults
506 public final function getDefaultCSSPath()
508 return $this->getDefaultFilePath().'/css';
512 * Returns the template path of the theme defaults
516 public final function getDefaultTemplatePath()
518 return $this->getDefaultFilePath().'/tpls';
522 * Returns the javascript path of the theme defaults
526 public final function getDefaultJSPath()
528 return $this->getDefaultFilePath().'/js';
532 * Returns CSS for the current theme.
534 * @param $color string optional, specifies the css color file to use if the theme supports it; defaults to cookie value or theme default
535 * @param $font string optional, specifies the css font file to use if the theme supports it; defaults to cookie value or theme default
536 * @return string HTML code
538 public function getCSS(
543 // include style.css file
544 $html = '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('yui.css').'" />';
545 $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('deprecated.css').'" />';
546 $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('style.css').'" />';
548 // for BC during upgrade
549 if ( !empty($this->colors) ) {
550 if ( isset($_SESSION['authenticated_user_theme_color']) && in_array($_SESSION['authenticated_user_theme_color'], $this->colors))
551 $color = $_SESSION['authenticated_user_theme_color'];
553 $color = $this->colors[0];
554 $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('colors.'.$color.'.css').'" id="current_color_style" />';
557 if ( !empty($this->fonts) ) {
558 if ( isset($_SESSION['authenticated_user_theme_font']) && in_array($_SESSION['authenticated_user_theme_font'], $this->fonts))
559 $font = $_SESSION['authenticated_user_theme_font'];
561 $font = $this->fonts[0];
562 $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('fonts.'.$font.'.css').'" id="current_font_style" />';
569 * Returns javascript for the current theme
571 * @return string HTML code
573 public function getJS()
575 $styleJS = $this->getJSURL('style.js');
577 <script type="text/javascript" src="$styleJS"></script>
582 * Returns the path for the tpl file in the current theme. If not found in the current theme, will revert
583 * to looking in the base theme.
585 * @param string $templateName tpl file name
586 * @return string path of tpl file to include
588 public function getTemplate(
592 if ( isset($this->_templateCache[$templateName]) )
593 return $this->_templateCache[$templateName];
596 if (sugar_is_file('custom/'.$this->getTemplatePath().'/'.$templateName))
597 $templatePath = 'custom/'.$this->getTemplatePath().'/'.$templateName;
598 elseif (sugar_is_file($this->getTemplatePath().'/'.$templateName))
599 $templatePath = $this->getTemplatePath().'/'.$templateName;
600 elseif (isset($this->parentTheme)
601 && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
602 && ($filename = SugarThemeRegistry::get($this->parentTheme)->getTemplate($templateName)) != '')
603 $templatePath = $filename;
604 elseif (sugar_is_file('custom/'.$this->getDefaultTemplatePath().'/'.$templateName))
605 $templatePath = 'custom/'.$this->getDefaultTemplatePath().'/'.$templateName;
606 elseif (sugar_is_file($this->getDefaultTemplatePath().'/'.$templateName))
607 $templatePath = $this->getDefaultTemplatePath().'/'.$templateName;
609 $GLOBALS['log']->warn("Template $templateName not found");
613 $this->_imageCache[$templateName] = $templatePath;
615 return $templatePath;
619 * Returns an image tag for the given image.
621 * @param string $image image name
622 * @param string $other_attributes optional, other attributes to add to the image tag, not cached
623 * @param string $width optional, defaults to the actual image's width
624 * @param string $height optional, defaults to the actual image's height
625 * @return string HTML image tag
627 public function getImage(
629 $other_attributes = '',
635 static $cached_results = array();
638 if(!empty($cached_results[$imageName]))
639 return $cached_results[$imageName]."$other_attributes />";
641 $imageURL = $this->getImageURL($imageName,false);
642 if ( empty($imageURL) )
645 $size = getimagesize($imageURL);
646 if ( is_null($width) )
648 if ( is_null($height) )
651 // Cache everything but the other attributes....
652 $cached_results[$imageName] = "<img src=\"". getJSPath($imageURL) ."\" width=\"$width\" height=\"$height\" ";
654 return $cached_results[$imageName] . "$other_attributes />";
658 * Returns the URL for an image in the current theme. If not found in the current theme, will revert
659 * to looking in the base theme.
661 * @param string $imageName image file name
662 * @param bool $addJSPath call getJSPath() with the results to add some unique image tracking support
663 * @return string path to image
665 public function getImageURL(
670 if ( isset($this->_imageCache[$imageName]) ) {
672 return getJSPath($this->_imageCache[$imageName]);
674 return $this->_imageCache[$imageName];
678 if (($filename = $this->_getImageFileName('custom/'.$this->getImagePath().'/'.$imageName)) != '')
679 $imagePath = $filename;
680 elseif (($filename = $this->_getImageFileName($this->getImagePath().'/'.$imageName)) != '')
681 $imagePath = $filename;
682 elseif (isset($this->parentTheme)
683 && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
684 && ($filename = SugarThemeRegistry::get($this->parentTheme)->getImageURL($imageName,false)) != '')
685 $imagePath = $filename;
686 elseif (($filename = $this->_getImageFileName('custom/'.$this->getDefaultImagePath().'/'.$imageName)) != '')
687 $imagePath = $filename;
688 elseif (($filename = $this->_getImageFileName($this->getDefaultImagePath().'/'.$imageName)) != '')
689 $imagePath = $filename;
691 $GLOBALS['log']->warn("Image $imageName not found");
695 $this->_imageCache[$imageName] = $imagePath;
698 return getJSPath($imagePath);
704 * Checks for an image using all of the accepted image extensions
706 * @param string $imageName image file name
707 * @return string path to image
709 protected function _getImageFileName(
713 // return now if the extension matches that of which we are looking for
714 if ( sugar_is_file($imageName) )
716 $pathParts = pathinfo($imageName);
717 foreach ( $this->imageExtensions as $extension )
718 if ( isset($pathParts['extension']) )
719 if ( ( $extension != $pathParts['extension'] )
720 && sugar_is_file($pathParts['dirname'].'/'.$pathParts['filename'].'.'.$extension) )
721 return $pathParts['dirname'].'/'.$pathParts['filename'].'.'.$extension;
727 * Returns the URL for the css file in the current theme. If not found in the current theme, will revert
728 * to looking in the base theme.
730 * @param string $cssFileName css file name
731 * @param bool $addJSPath call getJSPath() with the results to add some unique image tracking support
732 * @return string path of css file to include
734 public function getCSSURL(
739 if ( isset($this->_cssCache[$cssFileName]) ) {
741 return getJSPath($this->_cssCache[$cssFileName]);
743 return $this->_cssCache[$cssFileName];
746 $cssFileContents = '';
747 if (isset($this->parentTheme)
748 && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
749 && ($filename = SugarThemeRegistry::get($this->parentTheme)->getCSSURL($cssFileName,false)) != '')
750 $cssFileContents .= file_get_contents($filename);
752 if (sugar_is_file($this->getDefaultCSSPath().'/'.$cssFileName))
753 $cssFileContents .= file_get_contents($this->getDefaultCSSPath().'/'.$cssFileName);
754 if (sugar_is_file('custom/'.$this->getDefaultCSSPath().'/'.$cssFileName))
755 $cssFileContents .= file_get_contents('custom/'.$this->getDefaultCSSPath().'/'.$cssFileName);
757 if (sugar_is_file($this->getCSSPath().'/'.$cssFileName))
758 $cssFileContents .= file_get_contents($this->getCSSPath().'/'.$cssFileName);
759 if (sugar_is_file('custom/'.$this->getCSSPath().'/'.$cssFileName))
760 $cssFileContents .= file_get_contents('custom/'.$this->getCSSPath().'/'.$cssFileName);
761 if (empty($cssFileContents)) {
762 $GLOBALS['log']->warn("CSS File $cssFileName not found");
766 // fix any image references that may be defined in css files
767 $cssFileContents = str_ireplace("entryPoint=getImage&",
768 "entryPoint=getImage&themeName={$this->dirName}&",
771 // create the cached file location
772 $cssFilePath = create_cache_directory($this->getCSSPath()."/$cssFileName");
774 // if this is the style.css file, prepend the base.css and calendar-win2k-cold-1.css
775 // files before the theme styles
776 if ( $cssFileName == 'style.css' && !isset($this->parentTheme) ) {
777 $cssFileContents = file_get_contents('jscalendar/calendar-win2k-cold-1.css') . $cssFileContents;
778 if ( inDeveloperMode() )
779 $cssFileContents = file_get_contents('include/javascript/yui/build/base/base.css') . $cssFileContents;
781 $cssFileContents = file_get_contents('include/javascript/yui/build/base/base-min.css') . $cssFileContents;
785 if ( !inDeveloperMode() && !sugar_is_file($cssFilePath) ) {
786 $cssFileContents = cssmin::minify($cssFileContents);
789 // now write the css to cache
790 sugar_file_put_contents($cssFilePath,$cssFileContents);
792 $this->_cssCache[$cssFileName] = $cssFilePath;
795 return getJSPath($cssFilePath);
801 * Returns the URL for an image in the current theme. If not found in the current theme, will revert
802 * to looking in the base theme.
804 * @param string $jsFileName js file name
805 * @param bool $addJSPath call getJSPath() with the results to add some unique image tracking support
806 * @return string path to js file
808 public function getJSURL(
813 if ( isset($this->_jsCache[$jsFileName]) ) {
815 return getJSPath($this->_jsCache[$jsFileName]);
817 return $this->_jsCache[$jsFileName];
820 $jsFileContents = '';
822 if (isset($this->parentTheme)
823 && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
824 && ($filename = SugarThemeRegistry::get($this->parentTheme)->getJSURL($jsFileName,false)) != '')
825 $jsFileContents .= file_get_contents($filename);
827 if (sugar_is_file($this->getDefaultJSPath().'/'.$jsFileName))
828 $jsFileContents .= file_get_contents($this->getDefaultJSPath().'/'.$jsFileName);
829 if (sugar_is_file('custom/'.$this->getDefaultJSPath().'/'.$jsFileName))
830 $jsFileContents .= file_get_contents('custom/'.$this->getDefaultJSPath().'/'.$jsFileName);
832 if (sugar_is_file($this->getJSPath().'/'.$jsFileName))
833 $jsFileContents .= file_get_contents($this->getJSPath().'/'.$jsFileName);
834 if (sugar_is_file('custom/'.$this->getJSPath().'/'.$jsFileName))
835 $jsFileContents .= file_get_contents('custom/'.$this->getJSPath().'/'.$jsFileName);
836 if (empty($jsFileContents)) {
837 $GLOBALS['log']->warn("Javascript File $jsFileName not found");
841 // create the cached file location
842 $jsFilePath = create_cache_directory($this->getJSPath()."/$jsFileName");
845 if ( !inDeveloperMode() && !sugar_is_file(str_replace('.js','-min.js',$jsFilePath)) ) {
846 $jsFileContents = JSMin::minify($jsFileContents);
847 $jsFilePath = str_replace('.js','-min.js',$jsFilePath);
850 // now write the js to cache
851 sugar_file_put_contents($jsFilePath,$jsFileContents);
853 $this->_jsCache[$jsFileName] = $jsFilePath;
856 return getJSPath($jsFilePath);
862 * Returns an array of all of the images available for the current theme
866 public function getAllImages()
868 // first, lets get all the paths of where to look
869 $pathsToSearch = array($this->getImagePath());
871 while (isset($theme->parentTheme) && SugarThemeRegistry::get($theme->parentTheme) instanceOf SugarTheme ) {
872 $theme = SugarThemeRegistry::get($theme->parentTheme);
873 $pathsToSearch[] = $theme->getImagePath();
875 $pathsToSearch[] = $this->getDefaultImagePath();
877 // now build the array
878 $imageArray = array();
879 foreach ( $pathsToSearch as $path )
881 if (!sugar_is_dir($path)) $path = "custom/$path";
882 if (sugar_is_dir($path) && $dir = opendir($path)) {
883 while (($file = readdir($dir)) !== false) {
891 if ( !isset($imageArray[$file]) )
892 $imageArray[$file] = $this->getImageURL($file,false);
906 * Registry for all the current classes in the system
908 class SugarThemeRegistry
911 * Array of all themes and thier object
915 private static $_themes = array();
918 * Name of the current theme; corresponds to an index key in SugarThemeRegistry::$_themes
922 private static $_currentTheme;
925 * Disable the constructor since this will be a singleton
927 private function __construct() {}
930 * Adds a new theme to the registry
932 * @param $themedef array
934 public static function add(
938 // make sure the we know the sugar version
939 if ( !isset($GLOBALS['sugar_version']) ) {
940 include('sugar_version.php');
941 $GLOBALS['sugar_version'] = $sugar_version;
944 // Assume theme is designed for 5.5.x if not specified otherwise
945 if ( !isset($themedef['version']) )
946 $themedef['version']['regex_matches'] = array('5\.5\.*');
948 // Check to see if theme is valid for this version of Sugar; return false if not
950 if( isset($themedef['version']['exact_matches']) ){
951 $matches_empty = false;
952 foreach( $themedef['version']['exact_matches'] as $match ){
953 if( $match == $GLOBALS['sugar_version'] ){
958 if( !$version_ok && isset($themedef['version']['regex_matches']) ){
959 $matches_empty = false;
960 foreach( $themedef['version']['regex_matches'] as $match ){
961 if( preg_match( "/$match/", $GLOBALS['sugar_version'] ) ){
969 $theme = new SugarTheme($themedef);
970 self::$_themes[$theme->dirName] = $theme;
974 * Removes a new theme from the registry
976 * @param $themeName string
978 public static function remove(
982 if ( self::exists($themeName) )
983 unset(self::$_themes[$themeName]);
987 * Returns a theme object in the registry specified by the given $themeName
989 * @param $themeName string
991 public static function get(
995 if ( isset(self::$_themes[$themeName]) )
996 return self::$_themes[$themeName];
1000 * Returns the current theme object
1003 public static function current()
1005 if ( !isset(self::$_currentTheme) )
1006 self::buildRegistry();
1008 return self::$_themes[self::$_currentTheme];
1012 * Returns true if a theme object specified by the given $themeName exists in the registry
1014 * @param $themeName string
1017 public static function exists(
1021 return (self::get($themeName) !== null);
1025 * Sets the given $themeName to be the current theme
1027 * @param $themeName string
1029 public static function set(
1033 if ( !self::exists($themeName) )
1036 self::$_currentTheme = $themeName;
1038 // set some of the expected globals
1039 $GLOBALS['barChartColors'] = self::current()->barChartColors;
1040 $GLOBALS['pieChartColors'] = self::current()->pieChartColors;
1044 * Builds the theme registry
1046 public static function buildRegistry()
1048 self::$_themes = array();
1049 $dirs = array("themes/","custom/themes/");
1051 // check for a default themedef file
1052 $themedefDefault = array();
1053 if ( sugar_is_file("custom/themes/default/themedef.php") ) {
1054 $themedef = array();
1055 require("custom/themes/default/themedef.php");
1056 $themedefDefault = $themedef;
1059 foreach ($dirs as $dirPath ) {
1060 if (sugar_is_dir('./'.$dirPath) && $dir = opendir('./'.$dirPath)) {
1061 while (($file = readdir($dir)) !== false) {
1067 || $file == "default"
1068 || !sugar_is_dir("./$dirPath".$file)
1069 || !sugar_is_file("./{$dirPath}{$file}/themedef.php")
1072 $themedef = array();
1073 require("./{$dirPath}{$file}/themedef.php");
1074 $themedef = array_merge($themedef,$themedefDefault);
1075 $themedef['dirName'] = $file;
1076 // check for theme already existing in the registry
1077 // if so, then it will override the current one
1078 if ( self::exists($themedef['dirName']) ) {
1079 $existingTheme = self::get($themedef['dirName']);
1080 foreach ( SugarTheme::getThemeDefFields() as $field )
1081 if ( !isset($themedef[$field]) )
1082 $themedef[$field] = $existingTheme->$field;
1083 self::remove($themedef['dirName']);
1085 if ( isset($themedef['name']) ) {
1086 self::add($themedef);
1093 // default to setting the default theme as the current theme
1094 if ( !self::set($GLOBALS['sugar_config']['default_theme']) ) {
1095 if ( count(self::availableThemes()) == 0 )
1096 sugar_die('No valid themes are found on this instance');
1098 self::set(array_pop(array_keys(self::availableThemes())));
1103 * Returns an array of available themes. Designed to be absorbed into get_select_options_with_id()
1107 public static function availableThemes()
1109 $themelist = array();
1110 $disabledThemes = array();
1111 if ( isset($GLOBALS['sugar_config']['disabled_themes']) )
1112 $disabledThemes = explode(',',$GLOBALS['sugar_config']['disabled_themes']);
1114 foreach ( self::$_themes as $themename => $themeobject ) {
1115 if ( in_array($themename,$disabledThemes) )
1117 $themelist[$themeobject->dirName] = $themeobject->name;
1119 asort($themelist, SORT_STRING);
1124 * Returns an array of un-available themes. Designed used with the theme selector in the admin panel
1128 public static function unAvailableThemes()
1130 $themelist = array();
1131 $disabledThemes = array();
1132 if ( isset($GLOBALS['sugar_config']['disabled_themes']) )
1133 $disabledThemes = explode(',',$GLOBALS['sugar_config']['disabled_themes']);
1135 foreach ( self::$_themes as $themename => $themeobject ) {
1136 if ( in_array($themename,$disabledThemes) )
1137 $themelist[$themeobject->dirName] = $themeobject->name;
1144 * Returns an array of all themes found in the current installation
1148 public static function allThemes()
1150 $themelist = array();
1152 foreach ( self::$_themes as $themename => $themeobject )
1153 $themelist[$themeobject->dirName] = $themeobject->name;
1159 * Clears out the cached path locations for all themes
1161 public static function clearAllCaches()
1163 foreach ( self::$_themes as $themeobject ) {
1164 $themeobject->clearCache();