1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35:
36:
37:
38:
39:
40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85:
86: class ThemeGenerator {
87:
88: 89: 90:
91: const TEMPLATE_PATH = 'components/ThemeGenerator/templates';
92:
93: 94: 95:
96: public static $defaultLight = 'Default';
97:
98: 99: 100:
101: public static $defaultDark = 'Terminal';
102:
103: 104: 105: 106: 107:
108: public static $settingsList = array(
109: 'background',
110: 'content',
111: 'text',
112: 'link',
113: 'highlight1',
114: 'highlight2',
115: );
116:
117: private static function getCacheKey () {
118: return get_called_class ().'_theme_'.Yii::app()->user->getName ();
119: }
120:
121: public static function getSettingsList () {
122: return array_merge (
123: self::getProfileKeys (false, true, false));
124: }
125:
126: public static function clearCache () {
127: Yii::app()->cache->set (self::getCacheKey (), false, 0);
128: }
129:
130: 131: 132: 133:
134: public static function generatePalette($preferences, $refresh=false){
135: $computedTheme = Yii::app()->cache->get (self::getCacheKey ());
136: if (!Yii::app()->user->isGuest && $computedTheme && !$refresh) {
137: return $computedTheme;
138: }
139:
140: $colors = $preferences;
141:
142:
143: $colors['smart_text'] = '';
144: $colors['smart_text2'] = '';
145:
146: if(isset($colors['backgroundImg']) && $colors['backgroundImg']) {
147: $colors['background']='';
148: }
149:
150: $settings = self::getSettingsList ();
151: foreach($settings as $key){
152:
153: if (!isset ($colors[$key])) $colors[$key] = '';
154: $value = $colors[$key];
155:
156: if (!preg_match("/#/", $value) && $value){
157: $colors[$key] = '#'.$value;
158: }
159:
160: if (!preg_match ('/_override$/', $key)) {
161: $colors['darker_'.$key] = X2Color::brightness($value, -0.1, false);
162: $colors['dark_'.$key] = X2Color::brightness($value, -0.05, false);
163:
164: $colors['brighter_'.$key] = X2Color::brightness($value, 0.1, false);
165: $colors['bright_'.$key] = X2Color::brightness($value, 0.05, false);
166:
167:
168: $colors['opaque_'.$key] = X2Color::opaque($value, 0.2);
169: }
170: $colors['light_'.$key] = X2Color::brightness($value, 0.05, true);
171: $colors['lighter_'.$key] = X2Color::brightness($value, 0.1, true);
172: }
173:
174:
175: foreach (array_filter ($settings, function ($key) {
176: return preg_match ('/^background_.*_override$/', $key);
177: }) as $key) {
178: if (isset ($colors[$key]) && $colors[$key]) {
179: $colors[preg_replace ('/^background_/', 'smart_text_', $key)] = X2Color::smartText (
180: $colors[$key], $colors['text'] ? '' : '#000000');
181: } else {
182: $colors[preg_replace ('/^background_/', 'smart_text_', $key)] = '';
183: }
184: }
185:
186:
187: $colors['border'] = $colors['lighter_content'];
188:
189:
190: if( isset($colors['highlight1'], $colors['text']) &&
191: !empty($colors['highlight1']) && !empty($colors['text']) ) {
192: $colors['smart_text'] = X2Color::smartText($colors['highlight1'], $colors['text']);
193: }
194:
195:
196: if( isset($colors['highlight2'], $colors['text']) &&
197: !empty($colors['highlight2']) && !empty($colors['text']) ) {
198: $colors['smart_text2'] = X2Color::smartText($colors['highlight2'], $colors['text']);
199: }
200:
201: Yii::app()->cache->set (self::getCacheKey (), $colors, 0);
202: return $colors;
203: }
204:
205: 206: 207: 208: 209: 210:
211: public static function formatColorArray($colors) {
212: foreach($colors as $key => $value){
213:
214: $colors[$key.'_hex'] = $value;
215:
216: $colors[$key] = self::formatColor($value);
217: }
218:
219: return $colors;
220: }
221:
222: 223: 224: 225: 226:
227: public static function render() {
228: self::renderThemeWithColors ();
229: }
230:
231: 232: 233: 234: 235:
236: public static function loadDefault($themeName, $computed=true) {
237:
238: if ($themeName == self::$defaultLight) {
239: return array('themeName'=>self::$defaultLight);
240: }
241:
242: $media = X2Model::model('Media')->findByAttributes(
243: array(
244: 'associationType' => 'theme',
245: 'fileName' => $themeName,
246: 'private' => 0,
247: )
248: );
249:
250: if (!$media) {
251: $media = X2Model::model('Media')->findByAttributes(
252: array(
253: 'associationType' => 'theme',
254: 'fileName' => self::$defaultDark
255: )
256: );
257:
258: if (!$media) {
259: return self::loadDefault(self::$defaultLight);
260: }
261: }
262:
263: $theme = CJSON::decode ($media->description);
264: if ($computed) {
265: $colors = ThemeGenerator::generatePalette($theme, true);
266: } else {
267: $colors = $theme;
268: }
269: return $colors;
270: }
271:
272:
273: 274: 275:
276: public static function renderTheme($themeName=null) {
277: if ($themeName) {
278: $colors = self::loadDefault($themeName);
279: self::renderThemeWithColors($colors);
280: } else {
281: self::render();
282: }
283: }
284:
285: public static function renderThemeColorSelector (
286: $label, $key, $value, $htmlOptions=array (), $disabled=false) {
287:
288: $htmlOptions = X2Html::mergeHtmlOptions (array (
289: 'class' => 'row theme-color-selector',
290: ), $htmlOptions);
291: echo X2Html::openTag ('div', $htmlOptions);
292: echo "
293: <label>
294: ".CHtml::encode ($label)."
295: </label>
296: <input type='text' name='preferences[$key]' id='preferences_$key' value='$value'
297: class='color-picker-input theme-attr' ".($disabled ? 'disabled="disabled"': '').">
298: </input>
299: </div>";
300: }
301:
302: 303: 304: 305:
306: public static function renderSettings(){
307: $colors = self::generatePalette (Yii::app()->params->profile->getTheme());
308: $translations = self::getTranslations();
309: $settings = self::getSettingsList ();
310:
311: $i = 0;
312: foreach($settings as $key){
313: if (preg_match ('/_override$/', $key) && (!isset ($colors[$key]) || !$colors[$key])) {
314: continue;
315: }
316: $setting = isset ($translations[$key]) ? $translations[$key] : '';
317: $value = isset($colors[$key]) ? $colors[$key] : '' ;
318: self::renderThemeColorSelector ($setting, $key, $value);
319: if (++$i % 3 == 0)
320: echo '</br>';
321: }
322:
323: echo "<div style='clear:both;'></div>";
324: }
325:
326: 327: 328:
329: public static function getTranslations() {
330: $translations = array(
331: 'background' => Yii::t('profile', 'Background'),
332: 'content' => Yii::t('profile', 'Content'),
333: 'text' => Yii::t('profile', 'Text'),
334: 'link' => Yii::t('profile', 'Links'),
335: 'highlight1' => Yii::t('profile', 'Windows and Buttons'),
336: 'highlight2' => Yii::t('profile', 'Highlight')
337: );
338: $moduleOverrideKeys = self::getModuleOverrideKeys (true);
339: foreach ($moduleOverrideKeys as $key) {
340: if (preg_match ('/^background_/', $key )) {
341: $translations[$key] = Modules::displayName (
342: true, preg_replace ('/background_([^_]*)_override$/', '$1', $key));
343: }
344: }
345: return $translations;
346: }
347:
348: 349: 350:
351: public static function removeBackdrop() {
352: Yii::app()->clientScript->registerScript ('RemoveBackropJS', '
353: $(function() {
354: $("#content").addClass("no-backdrop");
355: });
356: ', CClientScript::POS_END);
357: }
358:
359: public static function isThemed() {
360: $theme = Yii::app()->params->profile->theme['themeName'];
361: return ($theme && $theme != self::$defaultLight);
362: }
363:
364: 365: 366:
367: private static $_profileKeys;
368: public static function getProfileKeys(
369: $internal=true, $base=true, $computed=true) {
370:
371: if (!isset (self::$_profileKeys)) {
372: $profileKeys = array(
373: 'internal' => array (
374:
375: 'themeName',
376: ),
377:
378: 379: 380:
381:
382: 'base' => array (
383:
384:
385: 'background',
386:
387:
388:
389: 'content',
390:
391:
392: 'text',
393:
394:
395: 'link',
396:
397:
398: 'highlight1',
399:
400:
401:
402: 'highlight2',
403:
404: ),
405:
406: 407: 408:
409:
410: 'computed' => array (
411:
412:
413: 'smart_text',
414:
415:
416: 'smart_text2',
417:
418:
419: 'border',
420:
421:
422: 'darker_background',
423: 'darker_content',
424: 'darker_text',
425: 'darker_link',
426: 'darker_highlight1',
427: 'darker_highlight2',
428:
429:
430: 'dark_background',
431: 'dark_content',
432: 'dark_text',
433: 'dark_link',
434: 'dark_highlight1',
435: 'dark_highlight2',
436:
437:
438: 'bright_background',
439: 'bright_content',
440: 'bright_text',
441: 'bright_link',
442: 'bright_highlight1',
443: 'bright_highlight2',
444:
445:
446: 'brighter_background',
447: 'brighter_content',
448: 'brighter_text',
449: 'brighter_link',
450: 'brighter_highlight1',
451: 'brighter_highlight2',
452:
453:
454:
455: 'light_background',
456: 'light_content',
457: 'light_text',
458: 'light_link',
459: 'light_highlight1',
460: 'light_highlight2',
461:
462:
463: 'lighter_background',
464: 'lighter_content',
465: 'lighter_text',
466: 'lighter_link',
467: 'lighter_highlight1',
468: 'lighter_highlight2',
469:
470:
471:
472: 'opaque_background',
473: 'opaque_content',
474: 'opaque_text',
475: 'opaque_link',
476: 'opaque_highlight1',
477: 'opaque_highlight2',
478: ),
479: );
480:
481: $moduleOverrideKeys = self::getModuleOverrideKeys ();
482: foreach ($moduleOverrideKeys as $type => $keys) {
483: $profileKeys[$type] = array_merge ($profileKeys[$type], $keys);
484: }
485:
486: self::$_profileKeys = $profileKeys;
487: }
488: $profileKeys = self::$_profileKeys;
489:
490: $requestedKeys = array ();
491: if ($internal) $requestedKeys = array_merge ($requestedKeys, $profileKeys['internal']);
492: if ($base) $requestedKeys = array_merge ($requestedKeys, $profileKeys['base']);
493: if ($computed) $requestedKeys = array_merge ($requestedKeys, $profileKeys['computed']);
494:
495: return $requestedKeys;
496: }
497:
498: private static $_moduleOverrideKeys;
499: private static function getModuleOverrideKeys ($flatten=false) {
500: if (!isset (self::$_moduleOverrideKeys)) {
501: $moduleNames = Modules::getModuleNames ();
502: self::$_moduleOverrideKeys =
503: array (
504: 'base' => array_map (function ($name) {
505: return 'background_'.$name.'_override';
506: }, $moduleNames)
507: );
508: self::$_moduleOverrideKeys['computed'] =
509: array_merge (
510: array_map (function ($name) {
511: return 'smart_text_'.$name.'_override';
512: }, $moduleNames),
513: array_map (function ($name) {
514: return 'light_background_'.$name.'_override';
515: }, $moduleNames),
516: array_map (function ($name) {
517: return 'lighter_background_'.$name.'_override';
518: }, $moduleNames)
519: );
520: }
521: $moduleOverrideKeys = self::$_moduleOverrideKeys;
522: if ($flatten) {
523: $flattened = array ();
524: foreach ($moduleOverrideKeys as $type => $keys) {
525: $flattened = array_merge ($flattened, $keys);
526: }
527: return $flattened;
528: }
529: return $moduleOverrideKeys;
530: }
531:
532: 533: 534: 535:
536: private static function loadTemplates($colors){
537: $colors = self::validateColors ($colors);
538:
539: $css = '';
540: $dir = new DirectoryIterator(
541: Yii::app()->basePath.DIRECTORY_SEPARATOR.self::TEMPLATE_PATH );
542: foreach ($dir as $fileinfo) {
543:
544: if (preg_match ('/generatedModuleOverrides.php$/', $fileinfo)) {
545: $customModuleNames = Modules::getModuleNames (true);
546: foreach ($customModuleNames as $module) {
547: $css .= include $fileinfo->getPathname();
548: }
549: } elseif (preg_match ('/\.php$/', $fileinfo)) {
550: $css .= include $fileinfo->getPathname();
551: }
552: }
553:
554: return $css;
555: }
556:
557: 558: 559:
560: private static function validateColors (array $colors) {
561: $expected = self::getProfileKeys (false, true, true);
562: foreach ($expected as $key) {
563: if (!isset ($colors[$key])) $colors[$key] = '';
564: if (!isset ($colors[$key.'_hex'])) $colors[$key.'_hex'] = '';
565: }
566: return $colors;
567: }
568:
569: 570: 571: 572: 573:
574: private static function getCss($colors) {
575: if (!$colors['themeName'] || $colors['themeName'] == self::$defaultLight){
576: return "";
577: }
578:
579: $colors = self::formatColorArray($colors);
580: $css = self::loadTemplates($colors);
581: return $css;
582: }
583:
584: 585: 586: 587:
588: private static function renderThemeWithColors ($colors=null) {
589: if (!$colors) {
590: $profile = Yii::app()->params->profile;
591:
592:
593: if (!$profile) {
594: self::renderTheme(self::$defaultLight);
595: return;
596: }
597:
598: $colors = $profile->getTheme();
599: $colors = self::generatePalette($colors);
600: }
601:
602:
603: $css = self::getCss($colors);
604:
605: Yii::app()->clientScript->registerCSS(
606: 'ProfileGeneratedCSS', $css);
607: }
608:
609: 610: 611: 612: 613: 614:
615: private static function formatColor($value){
616: if (!$value) $value = ';';
617: if (!preg_match('/#/', $value) && !preg_match('/rgb/', $value)) {
618: return $value;
619: }
620:
621: if(!isset($value) || !$value){
622: $value = ';';
623: } else {
624: $value = "$value !important;";
625: }
626:
627: return $value;
628: }
629:
630: }
631:
632: ?>
633: