Issues (4069)

Security Analysis    not enabled

This project does not seem to handle request data directly as such no vulnerable execution paths were found.

  Cross-Site Scripting
Cross-Site Scripting enables an attacker to inject code into the response of a web-request that is viewed by other users. It can for example be used to bypass access controls, or even to take over other users' accounts.
  File Exposure
File Exposure allows an attacker to gain access to local files that he should not be able to access. These files can for example include database credentials, or other configuration files.
  File Manipulation
File Manipulation enables an attacker to write custom data to files. This potentially leads to injection of arbitrary code on the server.
  Object Injection
Object Injection enables an attacker to inject an object into PHP code, and can lead to arbitrary code execution, file exposure, or file manipulation attacks.
  Code Injection
Code Injection enables an attacker to execute arbitrary code on the server.
  Response Splitting
Response Splitting can be used to send arbitrary responses.
  File Inclusion
File Inclusion enables an attacker to inject custom files into PHP's file loading mechanism, either explicitly passed to include, or for example via PHP's auto-loading mechanism.
  Command Injection
Command Injection enables an attacker to inject a shell command that is execute with the privileges of the web-server. This can be used to expose sensitive data, or gain access of your server.
  SQL Injection
SQL Injection enables an attacker to execute arbitrary SQL code on your database server gaining access to user data, or manipulating user data.
  XPath Injection
XPath Injection enables an attacker to modify the parts of XML document that are read. If that XML document is for example used for authentication, this can lead to further vulnerabilities similar to SQL Injection.
  LDAP Injection
LDAP Injection enables an attacker to inject LDAP statements potentially granting permission to run unauthorized queries, or modify content inside the LDAP tree.
  Header Injection
  Other Vulnerability
This category comprises other attack vectors such as manipulating the PHP runtime, loading custom extensions, freezing the runtime, or similar.
  Regex Injection
Regex Injection enables an attacker to execute arbitrary code in your PHP process.
  XML Injection
XML Injection enables an attacker to read files on your local filesystem including configuration files, or can be abused to freeze your web-server process.
  Variable Injection
Variable Injection enables an attacker to overwrite program variables with custom data, and can lead to further vulnerabilities.
Unfortunately, the security analysis is currently not available for your project. If you are a non-commercial open-source project, please contact support to gain access.

include/SugarTheme/SugarTheme.php (28 issues)

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
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-2013 SugarCRM Inc.
6
7
 * SuiteCRM is an extension to SugarCRM Community Edition developed by Salesagility Ltd.
8
 * Copyright (C) 2011 - 2014 Salesagility Ltd.
9
 *
10
 * This program is free software; you can redistribute it and/or modify it under
11
 * the terms of the GNU Affero General Public License version 3 as published by the
12
 * Free Software Foundation with the addition of the following permission added
13
 * to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
14
 * IN WHICH THE COPYRIGHT IS OWNED BY SUGARCRM, SUGARCRM DISCLAIMS THE WARRANTY
15
 * OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
16
 *
17
 * This program is distributed in the hope that it will be useful, but WITHOUT
18
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
19
 * FOR A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more
20
 * details.
21
 *
22
 * You should have received a copy of the GNU Affero General Public License along with
23
 * this program; if not, see http://www.gnu.org/licenses or write to the Free
24
 * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25
 * 02110-1301 USA.
26
 *
27
 * You can contact SugarCRM, Inc. headquarters at 10050 North Wolfe Road,
28
 * SW2-130, Cupertino, CA 95014, USA. or at email address [email protected].
29
 *
30
 * The interactive user interfaces in modified source and object code versions
31
 * of this program must display Appropriate Legal Notices, as required under
32
 * Section 5 of the GNU Affero General Public License version 3.
33
 *
34
 * In accordance with Section 7(b) of the GNU Affero General Public License version 3,
35
 * these Appropriate Legal Notices must retain the display of the "Powered by
36
 * SugarCRM" logo and "Supercharged by SuiteCRM" logo. If the display of the logos is not
37
 * reasonably feasible for  technical reasons, the Appropriate Legal Notices must
38
 * display the words  "Powered by SugarCRM" and "Supercharged by SuiteCRM".
39
 ********************************************************************************/
40
41
42
/*********************************************************************************
43
44
 * Description:  Contains a variety of utility functions used to display UI
45
 * components such as form headers and footers.  Intended to be modified on a per
46
 * theme basis.
47
 ********************************************************************************/
48
49
if(!defined('JSMIN_AS_LIB'))
50
    define('JSMIN_AS_LIB', true);
51
52
require_once("include/SugarTheme/cssmin.php");
53
require_once("jssource/jsmin.php");
54
require_once('include/utils/sugar_file_utils.php');
55
56
/**
57
 * Class that provides tools for working with a theme.
58
 * @api
59
 */
60
class SugarTheme
61
{
62
    /**
63
     * Theme name
64
     *
65
     * @var string
66
     */
67
    protected $name;
68
69
    /**
70
     * Theme description
71
     *
72
     * @var string
73
     */
74
    protected $description;
75
76
    /**
77
     * Defines which parent files to not include
78
     *
79
     * @var string
80
     */
81
    protected $ignoreParentFiles = array();
82
83
    /**
84
     * Defines which parent files to not include
85
     *
86
     * @var string
87
     */
88
    public $directionality = 'ltr';
89
    /**
90
     * Theme directory name
91
     *
92
     * @var string
93
     */
94
    protected $dirName;
95
96
    /**
97
     * Parent theme name
98
     *
99
     * @var string
100
     */
101
    protected $parentTheme;
102
103
    /**
104
     * Colors sets provided by the theme
105
     *
106
     * @deprecated only here for BC during upgrades
107
     * @var array
108
     */
109
    protected $colors = array();
110
111
    /**
112
     * Font sets provided by the theme
113
     *
114
     * @deprecated only here for BC during upgrades
115
     * @var array
116
     */
117
    protected $fonts  = array();
118
119
    /**
120
     * Maximum sugar version this theme is for; defaults to 5.5.1 as all the themes without this
121
     * parameter as assumed to work thru 5.5.1
122
     *
123
     * @var int
124
     */
125
    protected $version = '5.5.1';
126
127
    /**
128
     * Colors used in bar charts
129
     *
130
     * @var array
131
     */
132
    protected $barChartColors = array(
133
        "docBorder"             => "0xffffff",
134
        "docBg1"                => "0xffffff",
135
        "docBg2"                => "0xffffff",
136
        "xText"                 => "0x33485c",
137
        "yText"                 => "0x33485c",
138
        "title"                 => "0x333333",
139
        "misc"                  => "0x999999",
140
        "altBorder"             => "0xffffff",
141
        "altBg"                 => "0xffffff",
142
        "altText"               => "0x666666",
143
        "graphBorder"           => "0xcccccc",
144
        "graphBg1"              => "0xf6f6f6",
145
        "graphBg2"              => "0xf6f6f6",
146
        "graphLines"            => "0xcccccc",
147
        "graphText"             => "0x333333",
148
        "graphTextShadow"       => "0xf9f9f9",
149
        "barBorder"             => "0xeeeeee",
150
        "barBorderHilite"       => "0x333333",
151
        "legendBorder"          => "0xffffff",
152
        "legendBg1"             => "0xffffff",
153
        "legendBg2"             => "0xffffff",
154
        "legendText"            => "0x444444",
155
        "legendColorKeyBorder"  => "0x777777",
156
        "scrollBar"             => "0xcccccc",
157
        "scrollBarBorder"       => "0xeeeeee",
158
        "scrollBarTrack"        => "0xeeeeee",
159
        "scrollBarTrackBorder"  => "0xcccccc",
160
        );
161
162
    /**
163
     * Colors used in pie charts
164
     *
165
     * @var array
166
     */
167
    protected $pieChartColors = array(
168
        "docBorder"             => "0xffffff",
169
        "docBg1"                => "0xffffff",
170
        "docBg2"                => "0xffffff",
171
        "title"                 => "0x333333",
172
        "subtitle"              => "0x666666",
173
        "misc"                  => "0x999999",
174
        "altBorder"             => "0xffffff",
175
        "altBg"                 => "0xffffff",
176
        "altText"               => "0x666666",
177
        "graphText"             => "0x33485c",
178
        "graphTextShadow"       => "0xf9f9f9",
179
        "pieBorder"             => "0xffffff",
180
        "pieBorderHilite"       => "0x333333",
181
        "legendBorder"          => "0xffffff",
182
        "legendBg1"             => "0xffffff",
183
        "legendBg2"             => "0xffffff",
184
        "legendText"            => "0x444444",
185
        "legendColorKeyBorder"  => "0x777777",
186
        "scrollBar"             => "0xdfdfdf",
187
        "scrollBarBorder"       => "0xfafafa",
188
        "scrollBarTrack"        => "0xeeeeee",
189
        "scrollBarTrackBorder"  => "0xcccccc",
190
        );
191
192
    /**
193
     * Does this theme support group tabs
194
     *
195
     * @var bool
196
     */
197
    public $group_tabs;
198
199
    /**
200
     * Support for classic themes
201
     *
202
     * @var bool
203
     */
204
    public $classic;
205
206
    /**
207
     * Is this theme configurable
208
     *
209
     * @var bool
210
     */
211
    public $configurable;
212
213
    /**
214
     * theme config options
215
     *
216
     * @var bool
217
     */
218
    public $config_options = array();
219
220
221
    /**
222
     * Cache built of all css files locations
223
     *
224
     * @var array
225
     */
226
    private $_cssCache = array();
227
228
    /**
229
     * Cache built of all image files locations
230
     *
231
     * @var array
232
     */
233
    private $_imageCache = array();
234
235
    /**
236
     * Cache built of all javascript files locations
237
     *
238
     * @var array
239
     */
240
    private $_jsCache = array();
241
242
    /**
243
     * Cache built of all template files locations
244
     *
245
     * @var array
246
     */
247
    private $_templateCache = array();
248
249
	/**
250
	 * Cache built of sprite meta data
251
	 *
252
	 * @var array
253
	 */
254
	private $_spriteCache = array();
255
256
    /**
257
     * Size of the caches after the are initialized in the constructor
258
     *
259
     * @var array
260
     */
261
    private $_initialCacheSize = array(
262
        'cssCache'      => 0,
263
        'imageCache'    => 0,
264
        'jsCache'       => 0,
265
        'templateCache' => 0,
266
		'spriteCache'	=> 0,
267
        );
268
269
    /**
270
     * Controls whether or not to clear the cache on destroy; defaults to false
271
     */
272
    private $_clearCacheOnDestroy = false;
273
274
    private $imageExtensions = array(
275
            'svg',
276
            'gif',
277
            'png',
278
            'jpg',
279
            'tif',
280
            'bmp',
281
    );
282
283
    /**
284
     * Constructor
285
     *
286
     * Sets the theme properties from the defaults passed to it, and loads the file path cache from an external cache
287
     *
288
     * @param  $defaults string defaults for the current theme
289
     */
290
    public function __construct(
291
        $defaults
292
        )
293
    {
294
        // apply parent theme's properties first
295
        if ( isset($defaults['parentTheme']) ) {
296
            $themedef = array();
297
            include("themes/{$defaults['parentTheme']}/themedef.php");
298
            foreach ( $themedef as $key => $value ) {
299
                if ( property_exists(__CLASS__,$key) ) {
300
                    // For all arrays ( except colors and fonts ) you can just specify the items
301
                    // to change instead of all of the values
302
                    if ( is_array($this->$key) && !in_array($key,array('colors','fonts')) )
303
                        $this->$key = array_merge($this->$key,$value);
304
                    else
305
                        $this->$key = $value;
306
                }
307
            }
308
        }
309
        foreach ( $defaults as $key => $value ) {
310
            if ( property_exists(__CLASS__,$key) ) {
311
                // For all arrays ( except colors and fonts ) you can just specify the items
312
                // to change instead of all of the values
313
                if ( is_array($this->$key) && !in_array($key,array('colors','fonts')) )
314
                    $this->$key = array_merge($this->$key,$value);
315
                else
316
                    $this->$key = $value;
317
            }
318
        }
319
        if ( !inDeveloperMode() ) {
320
            if ( sugar_is_file($cachedfile = sugar_cached($this->getFilePath().'/pathCache.php'))) {
321
                $caches = unserialize(file_get_contents($cachedfile));
322
                if ( isset($caches['jsCache']) )
323
                    $this->_jsCache       = $caches['jsCache'];
324
                if ( isset($caches['cssCache']) )
325
                    $this->_cssCache      = $caches['cssCache'];
326
                if ( isset($caches['imageCache']) )
327
                    $this->_imageCache    = $caches['imageCache'];
328
                if ( isset($caches['templateCache']) )
329
                    $this->_templateCache = $caches['templateCache'];
330
            }
331
            $cachedfile = sugar_cached($this->getFilePath().'/spriteCache.php');
332
			if(!empty($GLOBALS['sugar_config']['use_sprites']) && sugar_is_file($cachedfile)) {
333
				$this->_spriteCache = unserialize(sugar_file_get_contents($cachedfile));
334
			}
335
        }
336
        $this->_initialCacheSize = array(
337
            'jsCache'       => count($this->_jsCache),
338
            'cssCache'      => count($this->_cssCache),
339
            'imageCache'    => count($this->_imageCache),
340
            'templateCache' => count($this->_templateCache),
341
			'spriteCache' 	=> count($this->_spriteCache),
342
            );
343
    }
344
345
    /**
346
	 * This is needed to prevent unserialize vulnerability
347
     */
348
    public function __wakeup()
349
    {
350
        // clean all properties
351
        foreach(get_object_vars($this) as $k => $v) {
352
            $this->$k = null;
353
        }
354
        throw new Exception("Not a serializable object");
355
    }
356
357
    /**
358
     * Destructor
359
     * Here we'll write out the internal file path caches to an external cache of some sort.
360
     */
361
    public function __destruct()
362
    {
363
        // Set the current directory to one which we expect it to be (i.e. the root directory of the install
364
        $dir = realpath(dirname(__FILE__) . '/../..');
365
        static $includePathIsPatched = false;
366
        if ($includePathIsPatched == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
367
        {
368
            $path = explode(PATH_SEPARATOR, get_include_path());
369
            if (in_array($dir, $path) == false)
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
370
            {
371
                set_include_path($dir . PATH_SEPARATOR . get_include_path());
372
            }
373
            $includePathIsPatched = true;
374
        }
375
        chdir($dir); // destruct can be called late, and chdir could change
376
        $cachedir = sugar_cached($this->getFilePath());
377
        sugar_mkdir($cachedir, 0775, true);
378
        // clear out the cache on destroy if we are asked to
379
        if ( $this->_clearCacheOnDestroy ) {
380
381
            if (is_file("$cachedir/pathCache.php"))
382
                unlink("$cachedir/pathCache.php");
383
			if (is_file("$cachedir/spriteCache.php"))
384
				unlink("$cachedir/spriteCache.php");
385
386
        }
387
        elseif ( !inDeveloperMode() ) {
388
            // only update the caches if they have been changed in this request
389
            if ( count($this->_jsCache) != $this->_initialCacheSize['jsCache']
390
                    || count($this->_cssCache) != $this->_initialCacheSize['cssCache']
391
                    || count($this->_imageCache) != $this->_initialCacheSize['imageCache']
392
                    || count($this->_templateCache) != $this->_initialCacheSize['templateCache']
393
                ) {
394
                sugar_file_put_contents(
395
                    "$cachedir/pathCache.php",
396
                    serialize(
397
                        array(
398
                            'jsCache'       => $this->_jsCache,
399
                            'cssCache'      => $this->_cssCache,
400
                            'imageCache'    => $this->_imageCache,
401
                            'templateCache' => $this->_templateCache,
402
                            )
403
                        )
404
                    );
405
406
            }
407
			if ( count($this->_spriteCache) != $this->_initialCacheSize['spriteCache']) {
408
				sugar_file_put_contents(
409
					"$cachedir/spriteCache.php",
410
					serialize($this->_spriteCache)
411
				);
412
			}
413
        }
414
    }
415
416
    /**
417
     * Specifies what is returned when the object is cast to a string, in this case it will be the
418
     * theme directory name.
419
     *
420
     * @return string theme directory name
421
     */
422 3
    public function __toString()
423
    {
424 3
        return $this->dirName;
425
    }
426
427
    /**
428
     * Generic public accessor method for all the properties of the theme ( which are kept protected )
429
     *
430
     * @return string
431
     */
432 3
    public function __get(
433
        $key
434
        )
435
    {
436 3
        if ( isset($this->$key) )
437 1
            return $this->$key;
438 2
    }
439
440 2
    public function __isset($key){
441 2
    	return isset($this->$key);
442
443
    }
444
445
    public function clearJSCache()
446
    {
447
        $this->_jsCache = array();
448
    }
449
450
    /**
451
     * Clears out the caches used for this themes
452
     */
453
    public function clearCache()
454
    {
455
        $this->_clearCacheOnDestroy = true;
456
    }
457
458
    /**
459
     * Return array of all valid fields that can be specified in the themedef.php file
460
     *
461
     * @return array
462
     */
463
    public static function getThemeDefFields()
464
    {
465
        return array(
466
            'name',
467
            'description',
468
            'directionality',
469
            'dirName',
470
            'parentTheme',
471
            'version',
472
            'colors',
473
            'fonts',
474
            'barChartColors',
475
            'pieChartColors',
476
            'group_tabs',
477
            'classic',
478
            'configurable',
479
            'config_options',
480
            'ignoreParentFiles',
481
            );
482
    }
483
484
    /**
485
     * Returns the file path of the current theme
486
     *
487
     * @return string
488
     */
489 23
    public function getFilePath()
490
    {
491 23
        return 'themes/'.$this->dirName;
492
    }
493
494
    /**
495
     * Returns the image path of the current theme
496
     *
497
     * @return string
498
     */
499 22
    public function getImagePath()
500
    {
501 22
        return $this->getFilePath().'/images';
502
    }
503
504
    /**
505
     * Returns the css path of the current theme
506
     *
507
     * @return string
508
     */
509 1
    public function getCSSPath()
510
    {
511 1
        return $this->getFilePath().'/css';
512
    }
513
514
    /**
515
     * Returns the javascript path of the current theme
516
     *
517
     * @return string
518
     */
519 1
    public function getJSPath()
520
    {
521 1
        return $this->getFilePath().'/js';
522
    }
523
524
    /**
525
     * Returns the tpl path of the current theme
526
     *
527
     * @return string
528
     */
529 3
    public function getTemplatePath()
530
    {
531 3
        return $this->getFilePath().'/tpls';
532
    }
533
534
    /**
535
     * Returns the file path of the theme defaults
536
     *
537
     * @return string
538
     */
539 16
    public final function getDefaultFilePath()
0 ignored issues
show
As per PSR2, final should precede the visibility keyword.
Loading history...
540
    {
541 16
        return 'themes/default';
542
    }
543
544
    /**
545
     * Returns the image path of the theme defaults
546
     *
547
     * @return string
548
     */
549 16
    public final function getDefaultImagePath()
0 ignored issues
show
As per PSR2, final should precede the visibility keyword.
Loading history...
550
    {
551 16
        return $this->getDefaultFilePath().'/images';
552
    }
553
554
    /**
555
     * Returns the css path of the theme defaults
556
     *
557
     * @return string
558
     */
559 1
    public final function getDefaultCSSPath()
0 ignored issues
show
As per PSR2, final should precede the visibility keyword.
Loading history...
560
    {
561 1
        return $this->getDefaultFilePath().'/css';
562
    }
563
564
    /**
565
     * Returns the template path of the theme defaults
566
     *
567
     * @return string
568
     */
569
    public final function getDefaultTemplatePath()
0 ignored issues
show
As per PSR2, final should precede the visibility keyword.
Loading history...
570
    {
571
        return $this->getDefaultFilePath().'/tpls';
572
    }
573
574
    /**
575
     * Returns the javascript path of the theme defaults
576
     *
577
     * @return string
578
     */
579 1
    public final function getDefaultJSPath()
0 ignored issues
show
As per PSR2, final should precede the visibility keyword.
Loading history...
580
    {
581 1
        return $this->getDefaultFilePath().'/js';
582
    }
583
584
    /**
585
     * Returns CSS for the current theme.
586
     *
587
     * @param  $color string optional, specifies the css color file to use if the theme supports it; defaults to cookie value or theme default
588
     * @param  $font  string optional, specifies the css font file to use if the theme supports it; defaults to cookie value or theme default
589
     * @return string HTML code
590
     */
591 4
    public function getCSS(
592
        $color = null,
593
        $font = null
594
        )
595
    {
596
        // include style.css file
597
        $html = '
598
            <!-- qtip & suggestion box -->
599 4
            <link rel="stylesheet" type="text/css" href="include/javascript/qtip/jquery.qtip.min.css" />';
600 4
        $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('yui.css').'" />';
601 4
        $html .= '<link rel="stylesheet" type="text/css" href="include/javascript/jquery/themes/base/jquery.ui.all.css" />';
602 4
        $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('deprecated.css').'" />';
603 4
        $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('style.css').'" />';
604
605
606
		// sprites
607 4
		if(!empty($GLOBALS['sugar_config']['use_sprites']) && $GLOBALS['sugar_config']['use_sprites']) {
608
609
			// system wide sprites
610
			if(file_exists("cache/sprites/default/sprites.css"))
611
				$html .= '<link rel="stylesheet" type="text/css" href="'.getJSPath('cache/sprites/default/sprites.css').'" />';
612
613
			// theme specific sprites
614
			if(file_exists("cache/sprites/{$this->dirName}/sprites.css"))
615
				$html .= '<link rel="stylesheet" type="text/css" href="'.getJSPath('cache/sprites/'.$this->dirName.'/sprites.css').'" />';
616
617
			// parent sprites
618
			if($this->parentTheme && $parent = SugarThemeRegistry::get($this->parentTheme)) {
619
				if(file_exists("cache/sprites/{$parent->dirName}/sprites.css"))
620
					$html .= '<link rel="stylesheet" type="text/css" href="'.getJSPath('cache/sprites/'.$parent->dirName.'/sprites.css').'" />';
621
			}
622
623
			// repeatable sprites
624
			if(file_exists("cache/sprites/Repeatable/sprites.css"))
625
				$html .= '<link rel="stylesheet" type="text/css" href="'.getJSPath('cache/sprites/Repeatable/sprites.css').'" />';
626
		}
627
628
        // for BC during upgrade
629 4
        if ( !empty($this->colors) ) {
0 ignored issues
show
Deprecated Code introduced by
The property SugarTheme::$colors has been deprecated with message: only here for BC during upgrades

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
630
            if ( isset($_SESSION['authenticated_user_theme_color']) && in_array($_SESSION['authenticated_user_theme_color'], $this->colors))
0 ignored issues
show
Deprecated Code introduced by
The property SugarTheme::$colors has been deprecated with message: only here for BC during upgrades

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
631
                $color = $_SESSION['authenticated_user_theme_color'];
632
            else
633
                $color = $this->colors[0];
0 ignored issues
show
Deprecated Code introduced by
The property SugarTheme::$colors has been deprecated with message: only here for BC during upgrades

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
634
            $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('colors.'.$color.'.css').'" id="current_color_style" />';
635
        }
636
637 4
        if ( !empty($this->fonts) ) {
0 ignored issues
show
Deprecated Code introduced by
The property SugarTheme::$fonts has been deprecated with message: only here for BC during upgrades

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
638
            if ( isset($_SESSION['authenticated_user_theme_font']) && in_array($_SESSION['authenticated_user_theme_font'], $this->fonts))
0 ignored issues
show
Deprecated Code introduced by
The property SugarTheme::$fonts has been deprecated with message: only here for BC during upgrades

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
639
                $font = $_SESSION['authenticated_user_theme_font'];
640
            else
641
                $font = $this->fonts[0];
0 ignored issues
show
Deprecated Code introduced by
The property SugarTheme::$fonts has been deprecated with message: only here for BC during upgrades

This property has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the property will be removed from the class and what other property to use instead.

Loading history...
642
            $html .= '<link rel="stylesheet" type="text/css" href="'.$this->getCSSURL('fonts.'.$font.'.css').'" id="current_font_style" />';
643
        }
644
645 4
        return $html;
646
    }
647
648
    /**
649
     * Returns javascript for the current theme
650
     *
651
     * @return string HTML code
652
     */
653 2
    public function getJS()
654
    {
655 2
        $styleJS = $this->getJSURL('style.js');
656
        return <<<EOHTML
657 2
<script type="text/javascript" src="$styleJS"></script>
658
EOHTML;
659
    }
660
661
    /**
662
     * Returns the path for the tpl file in the current theme. If not found in the current theme, will revert
663
     * to looking in the base theme.
664
     *
665
     * @param  string $templateName tpl file name
666
     * @return string path of tpl file to include
667
     */
668 3
    public function getTemplate(
669
        $templateName
670
        )
671
    {
672 3
        if ( isset($this->_templateCache[$templateName]) )
673
            return $this->_templateCache[$templateName];
674
675 3
        $templatePath = '';
676 3
        if (sugar_is_file('custom/'.$this->getTemplatePath().'/'.$templateName))
677
            $templatePath = 'custom/'.$this->getTemplatePath().'/'.$templateName;
678 3
        elseif (sugar_is_file($this->getTemplatePath().'/'.$templateName))
679 3
            $templatePath = $this->getTemplatePath().'/'.$templateName;
680
        elseif (isset($this->parentTheme)
681
                && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
682
                && ($filename = SugarThemeRegistry::get($this->parentTheme)->getTemplate($templateName)) != '')
683
            $templatePath = $filename;
684
        elseif (sugar_is_file('custom/'.$this->getDefaultTemplatePath().'/'.$templateName))
685
            $templatePath = 'custom/'.$this->getDefaultTemplatePath().'/'.$templateName;
686
        elseif (sugar_is_file($this->getDefaultTemplatePath().'/'.$templateName))
687
            $templatePath = $this->getDefaultTemplatePath().'/'.$templateName;
688
        else {
689
            $GLOBALS['log']->warn("Template $templateName not found");
690
            return false;
691
        }
692
693 3
        $this->_imageCache[$templateName] = $templatePath;
694
695 3
        return $templatePath;
696
    }
697
698
    /**
699
     * Returns an image tag for the given image.
700
     *
701
     * @param  string $image image name
0 ignored issues
show
There is no parameter named $image. Was it maybe removed?

This check looks for PHPDoc comments describing methods or function parameters that do not exist on the corresponding method or function.

Consider the following example. The parameter $italy is not defined by the method finale(...).

/**
 * @param array $germany
 * @param array $island
 * @param array $italy
 */
function finale($germany, $island) {
    return "2:1";
}

The most likely cause is that the parameter was removed, but the annotation was not.

Loading history...
702
     * @param  string $other_attributes optional, other attributes to add to the image tag, not cached
703
	 * @param  string $width optional, defaults to the actual image's width
704
	 * @param  string $height optional, defaults to the actual image's height
705
	 * @param  string $ext optional, image extension (TODO can we deprecate this one ?)
706
     * @param  string $alt optional, only used when image contains something useful, i.e. "Sally's profile pic"
707
     * @return string HTML image tag or sprite
708
     */
709 26
    public function getImage(
710
        $imageName,
711
        $other_attributes = '',
712
		$width = null,
713
		$height = null,
714
		$ext = null,
715
        $alt = ''
716
    )
717
    {
718
719 26
        static $cached_results = array();
720
721
		// trap deprecated use of image extension
722 26
		if(is_null($ext)) {
723 7
			$imageNameExp = explode('.',$imageName);
724 7
			if(count($imageNameExp) == 1)
725 7
				$imageName .= '.gif';
726
		} else {
727 23
			$imageName .= $ext;
728
		}
729
730
		// trap alt attributes in other_attributes
731 26
		if(preg_match('/alt=["\']([^\'"]+)["\']/i', $other_attributes))
732
			$GLOBALS['log']->debug("Sprites: alt attribute detected for $imageName");
733
		// sprite handler, makes use of own caching mechanism
734 26
		if(!empty($GLOBALS['sugar_config']['use_sprites']) && $GLOBALS['sugar_config']['use_sprites']) {
735
			// get sprite metadata
736
			if($sp = $this->getSpriteMeta($imageName)) {
737
				// requested size should match
738
				if( (!is_null($width) && $sp['width'] == $width) || (is_null($width)) &&
739
					(!is_null($height) && $sp['height'] == $height) || (is_null($height)) )
740
				{
741
                    $other_attributes .= ' data-orig="'.$imageName.'"';
742
743
                     if($sprite = $this->getSprite($sp['class'], $other_attributes, $alt))
744
                     {
745
                         return $sprite;
746
                     }
747
				}
748
			}
749
		}
750
751
		// img caching
752 26
		if(empty($cached_results[$imageName])) {
753 10
			$imageURL = $this->getImageURL($imageName,false);
754 10
			if ( empty($imageURL) )
755
				return false;
756 10
            if(strpos($imageURL, '.svg', strlen($imageURL)-4)){
757 2
                $cached_results[$imageName] = file_get_contents($imageURL);
758
            } else {
759 9
                $cached_results[$imageName] = '<img src="'.getJSPath($imageURL).'" ';
760
            }
761
762
		}
763
764 26
		$attr_width = (is_null($width)) ? "" : "width=\"$width\"";
765 26
		$attr_height = (is_null($height)) ? "" : "height=\"$height\"";
766
767 26
        if(strpos($cached_results[$imageName], 'svg') !== false){
768 3
            return $cached_results[$imageName];
769
        }
770 24
		return $cached_results[$imageName] . " $attr_width $attr_height $other_attributes alt=\"$alt\" />";
771
    }
772
773
	/**
774
	 * Returns sprite meta data
775
	 *
776
	 * @param  string $imageName Image filename including extension
777
	 * @return array  Sprite meta data
778
	 */
779
	public function getSpriteMeta($imageName) {
780
781
		// return from cache
782
	    if(isset($this->_spriteCache[$imageName]))
783
			return $this->_spriteCache[$imageName];
784
785
			// sprite keys are base on imageURL
786
		$imageURL = $this->getImageURL($imageName,false);
787
		if(empty($imageURL)) {
788
			$this->_spriteCache[$imageName] = false;
789
			return false;
790
		}
791
792
		// load meta data, includes default images
793
		require_once("include/SugarTheme/SugarSprites.php");
794
		$meta = SugarSprites::getInstance();
795
		// add current theme dir
796
		$meta->loadSpriteMeta($this->dirName);
797
		// add parent theme dir
798
		if($this->parentTheme && $parent = SugarThemeRegistry::get($this->parentTheme)) {
799
			$meta->loadSpriteMeta($parent->dirName);
800
		}
801
802
		// add to cache
803
		if(isset($meta->sprites[$imageURL])) {
804
			$this->_spriteCache[$imageName] = $meta->sprites[$imageURL];
805
			// add imageURL to cache
806
			//$this->_spriteCache[$imageName]['imageURL'] = $imageURL;
807
		} else {
808
			$this->_spriteCache[$imageName] = false;
809
			$GLOBALS['log']->debug("Sprites: miss for $imageURL");
810
		}
811
		return $this->_spriteCache[$imageName];
812
	}
813
814
	/**
815
	 * Returns sprite HTML span tag
816
	 *
817
	 * @param  string class The md5 id used in the CSS sprites class
818
	 * @param  string attr  optional, list of additional html attributes
819
	 * @param  string title optional, the title (equivalent to alt on img)
820
	 * @return string HTML span tag
821
	 */
822
	public function getSprite($class, $attr, $title) {
823
824
		// handle multiple class tags
825
		$class_regex = '/class=["\']([^\'"]+)["\']/i';
826
		preg_match($class_regex, $attr, $match);
827
		if(isset($match[1])) {
828
			$attr = preg_replace($class_regex, 'class="spr_'.$class.' ${1}"', $attr);
829
830
		// single class
831
		} else {
832
			$attr .= ' class="spr_'.$class.'"';
833
		}
834
835
		if($title)
836
			$attr .= ' title="'.$title.'"';
837
838
		// use </span> instead of /> to prevent weird UI results
839
		$GLOBALS['log']->debug("Sprites: generated sprite -> $attr");
840
		return "<span {$attr}></span>";
841
	}
842
843
	/**
844
	 * Returns a link HTML tag with or without an embedded image
845
	 */
846 2
    public function getLink(
847
		$url,
848
		$title,
849
		$other_attributes = '',
850
        $img_name = '',
851
        $img_other_attributes = '',
852
		$img_width = null,
853
		$img_height = null,
854
		$img_alt = '',
855
		$img_placement = 'imageonly'
856
    )
857
    {
858
859 2
		if($img_name) {
860 2
			$img = $this->getImage($img_name, $img_other_attributes, $img_width, $img_height, null, $img_alt);
861 2
			if($img == false) {
0 ignored issues
show
Bug Best Practice introduced by
It seems like you are loosely comparing $img of type string to the boolean false. If you are specifically checking for an empty string, consider using the more explicit === '' instead.
Loading history...
862
				$GLOBALS['log']->debug('Sprites: unknown image getLink');
863
				$img = 'unknown';
864
			}
865
			switch($img_placement) {
866 2
				case 'left': 	$inner_html = $img."<span class='title'>".$title."</span>"; break;
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
867
				case 'right':	$inner_html = "<span class='title'>".$title."</span>".$img; break;
0 ignored issues
show
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
868 2
				default:		$inner_html = $img; break;
0 ignored issues
show
The default body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a default statement must start on the line immediately following the statement.

switch ($expr) {
    default:
        doSomething(); //right
        break;
}


switch ($expr) {
    default:

        doSomething(); //wrong
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
Terminating statement must be on a line by itself

As per the PSR-2 coding standard, the break (or other terminating) statement must be on a line of its own.

switch ($expr) {
     case "A":
         doSomething();
         break; //wrong
     case "B":
         doSomething();
         break; //right
     case "C:":
         doSomething();
         return true; //right
 }

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
869
			}
870
		} else {
871
			$inner_html = $title;
872
		}
873
874 2
		return '<a href="'.$url.'" title="'.$title.'" '.$other_attributes.'>'.$inner_html.'</a>';
875
876
	}
877
878
    /**
879
     * Returns the URL for an image in the current theme. If not found in the current theme, will revert
880
     * to looking in the base theme.
881
     * @param  string $imageName image file name
882
     * @param  bool   $addJSPath call getJSPath() with the results to add some unique image tracking support
883
     * @return string path to image
884
     */
885 30
    public function getImageURL(
886
        $imageName,
887
        $addJSPath = true
888
        ){
889 30
        if ( isset($this->_imageCache[$imageName]) ) {
890 23
            if ( $addJSPath )
891 22
                return getJSPath($this->_imageCache[$imageName]);
892
            else
893 9
                return $this->_imageCache[$imageName];
894
        }
895 21
        $imagePath = '';
896 21
        if (($filename = $this->_getImageFileName('custom/'.$this->getImagePath().'/'.$imageName)) != '')
897
            $imagePath = $filename;
898 21
        elseif (($filename = $this->_getImageFileName($this->getImagePath().'/'.$imageName)) != '')
899 10
            $imagePath = $filename;
900 16
        elseif (isset($this->parentTheme)
901 16
                && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
902 16
                && ($filename = SugarThemeRegistry::get($this->parentTheme)->getImageURL($imageName,false)) != '')
903
            $imagePath = $filename;
904 16
        elseif (($filename = $this->_getImageFileName('custom/'.$this->getDefaultImagePath().'/'.$imageName)) != '')
905
            $imagePath = $filename;
906 16
        elseif (($filename = $this->_getImageFileName($this->getDefaultImagePath().'/'.$imageName)) != '')
907 5
            $imagePath = $filename;
908 14
		elseif (($filename = $this->_getImageFileName('include/images/'.$imageName)) != '')
909
			$imagePath = $filename;
910
        else {
911 14
            $GLOBALS['log']->warn("Image $imageName not found");
912 14
            return false;
913
        }
914
915 12
        $this->_imageCache[$imageName] = $imagePath;
916
917 12
        if ( $addJSPath )
918 8
            return getJSPath($imagePath);
919
920 9
        return $imagePath;
921
    }
922
923
    /**
924
     * Checks for an image using all of the accepted image extensions
925
     *
926
     * @param  string $imageName image file name
927
     * @return string path to image
928
     */
929 21
    protected function _getImageFileName(
930
        $imageName
931
        )
932
    {
933
        // return now if the extension matches that of which we are looking for
934 21
        if ( sugar_is_file($imageName) )
935 11
            return $imageName;
936 21
        $pathParts = pathinfo($imageName);
937 21
        foreach ( $this->imageExtensions as $extension )
938 21
            if ( isset($pathParts['extension']) )
939 21
                if ( ( $extension != $pathParts['extension'] )
940 21
                        && sugar_is_file($pathParts['dirname'].'/'.$pathParts['filename'].'.'.$extension) )
941 21
                    return $pathParts['dirname'].'/'.$pathParts['filename'].'.'.$extension;
942
943 21
        return '';
944
    }
945
946
    /**
947
     * Returns the URL for the css file in the current theme. If not found in the current theme, will revert
948
     * to looking in the base theme.
949
     *
950
     * @param  string $cssFileName css file name
951
     * @param  bool   $returnURL if true, returns URL with unique image mark, otherwise returns path to the file
952
     * @return string path of css file to include
953
     */
954 4
    public function getCSSURL($cssFileName, $returnURL = true)
955
    {
956 4
        if ( isset($this->_cssCache[$cssFileName]) && sugar_is_file(sugar_cached($this->_cssCache[$cssFileName])) ) {
957 3
            if ( $returnURL )
958 3
                return getJSPath("cache/".$this->_cssCache[$cssFileName]);
959
            else
960
                return sugar_cached($this->_cssCache[$cssFileName]);
961
        }
962
963 1
        $cssFileContents = '';
964 1
        $defaultFileName = $this->getDefaultCSSPath().'/'.$cssFileName;
965 1
        $fullFileName = $this->getCSSPath().'/'.$cssFileName;
966 1
        if (isset($this->parentTheme)
967 1
                && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
968 1
                && ($filename = SugarThemeRegistry::get($this->parentTheme)->getCSSURL($cssFileName,false)) != '')
969
            $cssFileContents .= file_get_contents($filename);
970
        else {
971 1
            if (sugar_is_file($defaultFileName))
972 1
                $cssFileContents .= file_get_contents($defaultFileName);
973 1
            if (sugar_is_file('custom/'.$defaultFileName))
974
                $cssFileContents .= file_get_contents('custom/'.$defaultFileName);
975
        }
976 1
        if (sugar_is_file($fullFileName)) {
977 1
            $cssFileContents .= file_get_contents($fullFileName);
978
        }
979 1
        if (sugar_is_file('custom/'.$fullFileName)) {
980
            $cssFileContents .= file_get_contents('custom/'.$fullFileName);
981
        }
982 1
        if (empty($cssFileContents)) {
983
            $GLOBALS['log']->warn("CSS File $cssFileName not found");
984
            return false;
985
        }
986
987
        // fix any image references that may be defined in css files
988 1
        $cssFileContents = str_ireplace("entryPoint=getImage&",
989 1
            "entryPoint=getImage&themeName={$this->dirName}&",
990
            $cssFileContents);
991
992
        // create the cached file location
993 1
        $cssFilePath = create_cache_directory($fullFileName);
994
995
        // if this is the style.css file, prepend the base.css and calendar-win2k-cold-1.css
996
        // files before the theme styles
997 1
        if ( $cssFileName == 'style.css' && !isset($this->parentTheme) ) {
998 1
            if ( inDeveloperMode() )
999
                $cssFileContents = file_get_contents('include/javascript/yui/build/base/base.css') . $cssFileContents;
1000
            else
1001 1
                $cssFileContents = file_get_contents('include/javascript/yui/build/base/base-min.css') . $cssFileContents;
1002
        }
1003
1004
        // minify the css
1005 1
        if ( !inDeveloperMode() && !sugar_is_file($cssFilePath) ) {
1006 1
            $cssFileContents = cssmin::minify($cssFileContents);
1007
        }
1008
1009
        // now write the css to cache
1010 1
        sugar_file_put_contents($cssFilePath,$cssFileContents);
1011
1012 1
        $this->_cssCache[$cssFileName] = $fullFileName;
1013
1014 1
        if ( $returnURL )
1015 1
            return getJSPath("cache/".$fullFileName);
1016
1017
        return sugar_cached($fullFileName);
1018
    }
1019
1020
    /**
1021
     * Returns the URL for an image in the current theme. If not found in the current theme, will revert
1022
     * to looking in the base theme.
1023
     *
1024
     * @param  string $jsFileName js file name
1025
     * @param  bool   $returnURL if true, returns URL with unique image mark, otherwise returns path to the file
1026
     * @return string path to js file
1027
     */
1028 2
    public function getJSURL($jsFileName, $returnURL = true)
1029
    {
1030 2
        if ( isset($this->_jsCache[$jsFileName]) && sugar_is_file(sugar_cached($this->_jsCache[$jsFileName])) ) {
1031 1
            if ( $returnURL )
1032 1
                return getJSPath("cache/".$this->_jsCache[$jsFileName]);
1033
            else
1034
                return sugar_cached($this->_jsCache[$jsFileName]);
1035
        }
1036
1037 1
        $jsFileContents = '';
1038 1
        $fullFileName = $this->getJSPath().'/'.$jsFileName;
1039 1
        $defaultFileName = $this->getDefaultJSPath().'/'.$jsFileName;
1040 1
        if (isset($this->parentTheme)
1041 1
                && SugarThemeRegistry::get($this->parentTheme) instanceOf SugarTheme
1042 1
                && ($filename = SugarThemeRegistry::get($this->parentTheme)->getJSURL($jsFileName,false)) != ''    && !in_array($jsFileName,$this->ignoreParentFiles)) {
1043
           $jsFileContents .= file_get_contents($filename);
1044
       } else {
1045 1
            if (sugar_is_file($defaultFileName))
1046 1
                $jsFileContents .= file_get_contents($defaultFileName);
1047 1
            if (sugar_is_file('custom/'.$defaultFileName))
1048
                $jsFileContents .= file_get_contents('custom/'.$defaultFileName);
1049
        }
1050 1
        if (sugar_is_file($fullFileName))
1051 1
            $jsFileContents .= file_get_contents($fullFileName);
1052 1
        if (sugar_is_file('custom/'.$fullFileName))
1053
            $jsFileContents .= file_get_contents('custom/'.$fullFileName);
1054 1
        if (empty($jsFileContents)) {
1055
            $GLOBALS['log']->warn("Javascript File $jsFileName not found");
1056
            return false;
1057
        }
1058
1059
        // create the cached file location
1060 1
        $jsFilePath = create_cache_directory($fullFileName);
1061
1062
        // minify the js
1063 1
        if ( !inDeveloperMode()&& !sugar_is_file(str_replace('.js','-min.js',$jsFilePath)) ) {
1064 1
            $jsFileContents = SugarMin::minify($jsFileContents);
1065 1
            $jsFilePath = str_replace('.js','-min.js',$jsFilePath);
1066 1
            $fullFileName = str_replace('.js','-min.js',$fullFileName);
1067
        }
1068
1069
        // now write the js to cache
1070 1
        sugar_file_put_contents($jsFilePath,$jsFileContents);
1071
1072 1
        $this->_jsCache[$jsFileName] = $fullFileName;
1073
1074 1
        if ( $returnURL )
1075 1
            return getJSPath("cache/".$fullFileName);
1076
1077
        return sugar_cached($fullFileName);
1078
    }
1079
1080
    /**
1081
     * Returns an array of all of the images available for the current theme
1082
     *
1083
     * @return array
1084
     */
1085
    public function getAllImages()
1086
    {
1087
        // first, lets get all the paths of where to look
1088
        $pathsToSearch = array($this->getImagePath());
1089
        $theme = $this;
1090
        while (isset($theme->parentTheme) && SugarThemeRegistry::get($theme->parentTheme) instanceOf SugarTheme ) {
1091
            $theme = SugarThemeRegistry::get($theme->parentTheme);
1092
            $pathsToSearch[] = $theme->getImagePath();
1093
        }
1094
        $pathsToSearch[] = $this->getDefaultImagePath();
1095
1096
        // now build the array
1097
        $imageArray = array();
1098
        foreach ( $pathsToSearch as $path )
1099
        {
1100
            if (!sugar_is_dir($path)) $path = "custom/$path";
1101
            if (sugar_is_dir($path) && is_readable($path) && $dir = opendir($path)) {
1102
                while (($file = readdir($dir)) !== false) {
1103
                    if ($file == ".."
1104
                            || $file == "."
1105
                            || $file == ".svn"
1106
                            || $file == "CVS"
1107
                            || $file == "Attic"
1108
                            )
1109
                        continue;
1110
                    if ( !isset($imageArray[$file]) )
1111
                        $imageArray[$file] = $this->getImageURL($file,false);
1112
                }
1113
                closedir($dir);
1114
            }
1115
        }
1116
1117
        ksort($imageArray);
1118
1119
        return $imageArray;
1120
    }
1121
1122
    /**
1123
     * Returns an array of all of the config values for the current theme
1124
     *
1125
     * @return array
1126
     */
1127 2
    public function getConfig()
1128
    {
1129 2
        global $sugar_config;
1130
1131 2
        $config = array();
1132
1133 2
        foreach($this->config_options as $name => $def){
0 ignored issues
show
The expression $this->config_options of type boolean is not traversable.
Loading history...
1134 2
            $config[$name] = $def;
1135
1136 2
            $value = '';
1137 2
            if(isset($sugar_config['theme_settings'][$this->dirName][$name])){
1138
                $value = $sugar_config['theme_settings'][$this->dirName][$name];
1139 2
            } else if(isset($def['default'])){
1140 2
                $value = $def['default'];
1141
            }
1142 2
            $config[$name] = $value;
1143
1144
        }
1145
1146 2
        return $config;
1147
    }
1148
1149
}
1150
1151
/**
1152
 * Registry for all the current classes in the system
1153
 */
1154
class SugarThemeRegistry
0 ignored issues
show
Since you have declared the constructor as private, maybe you should also declare the class as final.
Loading history...
Coding Style Compatibility introduced by
PSR1 recommends that each class should be in its own file to aid autoloaders.

Having each class in a dedicated file usually plays nice with PSR autoloaders and is therefore a well established practice. If you use other autoloaders, you might not want to follow this rule.

Loading history...
1155
{
1156
    /**
1157
     * Array of all themes and thier object
1158
     *
1159
     * @var array
1160
     */
1161
    private static $_themes = array();
1162
1163
    /**
1164
     * Name of the current theme; corresponds to an index key in SugarThemeRegistry::$_themes
1165
     *
1166
     * @var string
1167
     */
1168
    private static $_currentTheme;
1169
1170
    /**
1171
     * Disable the constructor since this will be a singleton
1172
     */
1173
    private function __construct() {}
1174
1175
    /**
1176
     * Adds a new theme to the registry
1177
     *
1178
     * @param $themedef array
1179
     */
1180
    public static function add(
1181
        array $themedef
1182
        )
1183
    {
1184
        // make sure the we know the sugar version
1185
        global $sugar_version;
1186
        if (empty($sugar_version))
1187
        {
1188
            include('sugar_version.php');
1189
        }
1190
1191
        // Assume theme is designed for 5.5.x if not specified otherwise
1192
        if ( !isset($themedef['version']) )
1193
            $themedef['version']['regex_matches'] = array('5\.5\.*');
1194
1195
        // Check to see if theme is valid for this version of Sugar; return false if not
1196
        $version_ok = false;
1197
        if( isset($themedef['version']['exact_matches']) ){
1198
            $matches_empty = false;
1199
            foreach( $themedef['version']['exact_matches'] as $match ){
1200
                if( $match == $GLOBALS['sugar_version'] ){
1201
                    $version_ok = true;
1202
                }
1203
            }
1204
        }
1205
        if( !$version_ok && isset($themedef['version']['regex_matches']) ){
1206
            $matches_empty = false;
1207
            foreach( $themedef['version']['regex_matches'] as $match ){
1208
                if( preg_match( "/$match/", $GLOBALS['sugar_version'] ) ){
1209
                    $version_ok = true;
1210
                }
1211
            }
1212
        }
1213
        if ( !$version_ok )
1214
            return false;
1215
1216
        $theme = new SugarTheme($themedef);
1217
        self::$_themes[$theme->dirName] = $theme;
0 ignored issues
show
The property $dirName is declared protected in SugarTheme. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1218
    }
1219
1220
    /**
1221
     * Removes a new theme from the registry
1222
     *
1223
     * @param $themeName string
1224
     */
1225
    public static function remove(
1226
        $themeName
1227
        )
1228
    {
1229
        if ( self::exists($themeName) )
1230
            unset(self::$_themes[$themeName]);
1231
    }
1232
1233
    /**
1234
     * Returns a theme object in the registry specified by the given $themeName
1235
     *
1236
     * @param $themeName string
1237
     */
1238 1
    public static function get(
1239
        $themeName
1240
        )
1241
    {
1242 1
        if ( isset(self::$_themes[$themeName]) )
1243 1
            return self::$_themes[$themeName];
1244
    }
1245
1246
    /**
1247
     * Returns the current theme object
1248
     *
1249
     * @return SugarTheme object
1250
     */
1251 40
    public static function current()
1252
    {
1253 40
        if ( !isset(self::$_currentTheme) )
1254
            self::buildRegistry();
1255
1256 40
        return self::$_themes[self::$_currentTheme];
1257
    }
1258
1259
    /**
1260
     * Returns the default theme object
1261
     *
1262
     * @return SugarTheme object
1263
     */
1264
    public static function getDefault()
1265
    {
1266
        if ( !isset(self::$_currentTheme) )
1267
            self::buildRegistry();
1268
1269
        if ( isset($GLOBALS['sugar_config']['default_theme']) && self::exists($GLOBALS['sugar_config']['default_theme']) ) {
1270
            return self::get($GLOBALS['sugar_config']['default_theme']);
1271
        }
1272
        $array_keys = array_keys(self::availableThemes());
1273
        return self::get(array_pop($array_keys));
1274
    }
1275
1276
    /**
1277
     * Returns true if a theme object specified by the given $themeName exists in the registry
1278
     *
1279
     * @param  $themeName string
1280
     * @return bool
1281
     */
1282 1
    public static function exists(
1283
        $themeName
1284
        )
1285
    {
1286 1
        return (self::get($themeName) !== null);
1287
    }
1288
1289
    /**
1290
     * Sets the given $themeName to be the current theme
1291
     *
1292
     * @param  $themeName string
1293
     */
1294 1
    public static function set(
1295
        $themeName
1296
        )
1297
    {
1298 1
        if ( !self::exists($themeName) )
1299
            return false;
1300
1301 1
        self::$_currentTheme = $themeName;
1302
1303
        // set some of the expected globals
1304 1
        $GLOBALS['barChartColors'] = self::current()->barChartColors;
0 ignored issues
show
The property $barChartColors is declared protected in SugarTheme. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1305 1
        $GLOBALS['pieChartColors'] = self::current()->pieChartColors;
0 ignored issues
show
The property $pieChartColors is declared protected in SugarTheme. Since you implemented __get(), maybe consider adding a @property or @property-read annotation. This makes it easier for IDEs to provide auto-completion.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
1306 1
        return true;
1307
    }
1308
1309
    /**
1310
     * Builds the theme registry
1311
     */
1312
    public static function buildRegistry()
1313
    {
1314
        self::$_themes = array();
1315
        $dirs = array("themes/","custom/themes/");
1316
1317
        // check for a default themedef file
1318
        $themedefDefault = array();
1319
        if ( sugar_is_file("custom/themes/default/themedef.php") ) {
1320
            $themedef = array();
1321
            require("custom/themes/default/themedef.php");
1322
            $themedefDefault = $themedef;
1323
        }
1324
1325
        foreach ($dirs as $dirPath ) {
1326
            if (sugar_is_dir('./'.$dirPath) && is_readable('./'.$dirPath) && $dir = opendir('./'.$dirPath)) {
1327
                while (($file = readdir($dir)) !== false) {
1328
                    if ($file == ".."
1329
                            || $file == "."
1330
                            || $file == ".svn"
1331
                            || $file == "CVS"
1332
                            || $file == "Attic"
1333
                            || $file == "default"
1334
                            || !sugar_is_dir("./$dirPath".$file)
1335
                            || !sugar_is_file("./{$dirPath}{$file}/themedef.php")
1336
                            )
1337
                        continue;
1338
                    $themedef = array();
1339
                    require("./{$dirPath}{$file}/themedef.php");
1340
                    $themedef = array_merge($themedef,$themedefDefault);
1341
                    $themedef['dirName'] = $file;
1342
                    // check for theme already existing in the registry
1343
                    // if so, then it will override the current one
1344
                    if ( self::exists($themedef['dirName']) ) {
1345
                        $existingTheme = self::get($themedef['dirName']);
1346
                        foreach ( SugarTheme::getThemeDefFields() as $field )
1347
                            if ( !isset($themedef[$field]) )
1348
                                $themedef[$field] = $existingTheme->$field;
1349
                        self::remove($themedef['dirName']);
1350
                    }
1351
                    if ( isset($themedef['name']) ) {
1352
                        self::add($themedef);
1353
                    }
1354
                }
1355
                closedir($dir);
1356
            }
1357
        }
1358
        // default to setting the default theme as the current theme
1359
        if ( !isset($GLOBALS['sugar_config']['default_theme']) || !self::set($GLOBALS['sugar_config']['default_theme']) ) {
1360
            if ( count(self::availableThemes()) == 0 )
1361
            {
1362
                sugar_die('No valid themes are found on this instance');
1363
            } else {
1364
                self::set(self::getDefaultThemeKey());
1365
            }
1366
        }
1367
    }
1368
1369
1370
    /**
1371
     * getDefaultThemeKey
1372
     *
1373
     * This function returns the default theme key.  It takes into account string casing issues that may arise
1374
     * from upgrades.  It attempts to look for the Sugar theme and if not found, defaults to return the name of the last theme
1375
     * in the array of available themes loaded.
1376
     *
1377
     * @return $defaultThemeKey String value of the default theme key to use
0 ignored issues
show
The doc-type $defaultThemeKey could not be parsed: Unknown type name "$defaultThemeKey" at position 0. (view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
1378
     */
1379
    private static function getDefaultThemeKey()
1380
    {
1381
        $availableThemes = self::availableThemes();
1382
        foreach($availableThemes as $key=>$theme)
1383
        {
1384
            if(strtolower($key) == 'sugar')
1385
            {
1386
                return $key;
1387
            }
1388
        }
1389
        $array_keys = array_keys($availableThemes);
1390
        return array_pop($array_keys);
1391
    }
1392
1393
1394
    /**
1395
     * Returns an array of available themes. Designed to be absorbed into get_select_options_with_id()
1396
     *
1397
     * @return array
1398
     */
1399 1
    public static function availableThemes()
1400
    {
1401 1
        $themelist = array();
1402 1
        $disabledThemes = array();
1403 1
        if ( isset($GLOBALS['sugar_config']['disabled_themes']) )
1404
            $disabledThemes = explode(',',$GLOBALS['sugar_config']['disabled_themes']);
1405
1406 1
        foreach ( self::$_themes as $themename => $themeobject ) {
1407 1
            if ( in_array($themename,$disabledThemes) )
1408
                continue;
1409 1
            $themelist[$themeobject->dirName] = $themeobject->name;
1410
        }
1411 1
        asort($themelist, SORT_STRING);
1412 1
        return $themelist;
1413
    }
1414
1415
    /**
1416
     * Returns an array of un-available themes. Designed used with the theme selector in the admin panel
1417
     *
1418
     * @return array
1419
     */
1420
    public static function unAvailableThemes()
1421
    {
1422
        $themelist = array();
1423
        $disabledThemes = array();
1424
        if ( isset($GLOBALS['sugar_config']['disabled_themes']) )
1425
            $disabledThemes = explode(',',$GLOBALS['sugar_config']['disabled_themes']);
1426
1427
        foreach ( self::$_themes as $themename => $themeobject ) {
1428
            if ( in_array($themename,$disabledThemes) )
1429
                $themelist[$themeobject->dirName] = $themeobject->name;
1430
        }
1431
1432
        return $themelist;
1433
    }
1434
1435
    /**
1436
     * Returns an array of all themes found in the current installation
1437
     *
1438
     * @return array
1439
     */
1440
    public static function allThemes()
1441
    {
1442
        $themelist = array();
1443
1444
        foreach ( self::$_themes as $themename => $themeobject )
1445
            $themelist[$themeobject->dirName] = $themeobject->name;
1446
1447
        return $themelist;
1448
    }
1449
1450
    /**
1451
     * Returns an array of all themes def found in the current installation
1452
     *
1453
     * @return array
1454
     */
1455
    public static function allThemesDefs()
1456
    {
1457
        $themelist = array();
1458
        $disabledThemes = array();
1459
        if (isset($GLOBALS['sugar_config']['disabled_themes']))
1460
            $disabledThemes = explode(',', $GLOBALS['sugar_config']['disabled_themes']);
1461
1462
        foreach (self::$_themes as $themename => $themeobject) {
1463
            $themearray['name'] = $themeobject->name;
1464
            $themearray['configurable'] = $themeobject->configurable;
1465
            $themearray['enabled'] = !in_array($themename, $disabledThemes);
1466
            $themelist[$themeobject->dirName] = $themearray;
1467
        }
1468
1469
        return $themelist;
1470
    }
1471
1472
    /**
1473
     * get the configurable options for $themeName
1474
     *
1475
     * @param  $themeName string
1476
     */
1477
    public static function getThemeConfig($themeName)
1478
    {
1479
        global $sugar_config;
1480
1481
        if ( !self::exists($themeName) )
1482
            return false;
1483
1484
        $config = array();
1485
1486
        foreach(self::$_themes[$themeName]->config_options as $name => $def){
1487
            $config[$name] = $def;
1488
1489
            $value = '';
1490
            if(isset($sugar_config['theme_settings'][$themeName][$name])){
1491
                $value = $sugar_config['theme_settings'][$themeName][$name];
1492
            } else if(isset($def['default'])){
1493
                $value = $def['default'];
1494
            }
1495
            $config[$name]['value'] = $value;
1496
1497
        }
1498
1499
        return $config;
1500
1501
    }
1502
1503
    /**
1504
     * Clears out the cached path locations for all themes
1505
     */
1506
    public static function clearAllCaches()
1507
    {
1508
        foreach ( self::$_themes as $themeobject ) {
1509
            $themeobject->clearCache();
1510
        }
1511
    }
1512
}
1513