1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10:
11:
12: namespace Xoops\Core\Theme;
13:
14: use Xoops\Core\FixedGroups;
15: use Xoops\Core\XoopsTpl;
16:
17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27:
28: class XoopsTheme
29: {
30: 31: 32: 33: 34:
35: public $renderBanner = true;
36:
37: 38: 39: 40: 41:
42: public $folderName = '';
43:
44: 45: 46: 47: 48:
49: public $path = '';
50:
51: 52: 53:
54: public $url = '';
55:
56: 57: 58: 59: 60:
61: public $bufferOutput = true;
62:
63: 64: 65: 66: 67:
68: public $canvasTemplate = 'theme.html';
69:
70: 71: 72: 73: 74:
75: public $themesPath = 'themes';
76:
77: 78: 79: 80: 81:
82: public $contentTemplate = '';
83:
84: 85: 86:
87: public $contentCacheLifetime = 0;
88:
89: 90: 91:
92: public $contentCacheId = null;
93:
94: 95: 96: 97: 98:
99: public $content = '';
100:
101: 102: 103: 104: 105: 106:
107: public $plugins = array('\Xoops\Core\Theme\Plugins\Blocks');
108:
109: 110: 111:
112: public $renderCount = 0;
113:
114: 115: 116: 117: 118:
119: public $template = false;
120:
121: 122: 123: 124: 125:
126: public $metas = array(
127: 'meta' => array(), 'link' => array(), 'script' => array()
128: );
129:
130: 131: 132: 133: 134:
135: public $assets = null;
136:
137: 138: 139: 140: 141:
142: public $baseAssets = array(
143: 'js' => array(),
144: 'css' => array(),
145: );
146:
147: 148: 149: 150: 151:
152: public $htmlHeadStrings = array();
153:
154: 155: 156: 157: 158:
159: public $templateVars = array();
160:
161: 162: 163: 164: 165:
166: public $use_extra_cache_id = true;
167:
168: 169: 170: 171: 172: 173: 174:
175: public $headersCacheEngine = 'default';
176:
177: 178: 179:
180:
181: 182: 183: 184:
185: 186: 187: 188: 189: 190: 191: 192: 193:
194: public function xoInit()
195: {
196: $xoops = \Xoops::getInstance();
197: $this->assets = $xoops->assets();
198: $this->path = \XoopsBaseConfig::get('themes-path') . '/' . $this->folderName;
199: $this->url = \XoopsBaseConfig::get('themes-url') . '/' . $this->folderName;
200: $this->template = null;
201: $this->template = new XoopsTpl();
202:
203: $this->template->assignByRef('xoTheme', $this);
204: $this->template->assign(array(
205: 'xoops_theme' => $xoops->getConfig('theme_set'),
206: 'xoops_imageurl' => \XoopsBaseConfig::get('themes-url') . '/' . $xoops->getConfig('theme_set') . '/',
207: 'xoops_themecss' => $xoops->getCss($xoops->getConfig('theme_set')),
208: 'xoops_requesturi' => htmlspecialchars($_SERVER['REQUEST_URI'], ENT_QUOTES),
209: 'xoops_sitename' => htmlspecialchars($xoops->getConfig('sitename'), ENT_QUOTES),
210: 'xoops_slogan' => htmlspecialchars($xoops->getConfig('slogan'), ENT_QUOTES),
211: 'xoops_dirname' => $xoops->moduleDirname,
212: 'xoops_banner' => $this->renderBanner ? $xoops->getBanner() : ' ',
213: 'xoops_pagetitle' => $xoops->isModule()
214: ? $xoops->module->getVar('name')
215: : htmlspecialchars($xoops->getConfig('slogan'), ENT_QUOTES)
216: ));
217: $this->template->assign(array(
218: 'theme_path' => $this->path, 'theme_tpl' => $this->path . '/xotpl', 'theme_url' => $this->url,
219: 'theme_img' => $this->url . '/img', 'theme_icons' => $this->url . '/icons',
220: 'theme_css' => $this->url . '/css', 'theme_js' => $this->url . '/js',
221: 'theme_lang' => $this->url . '/language',
222: ));
223:
224: if ($xoops->isUser()) {
225: $response = $xoops->service("Avatar")->getAvatarUrl($xoops->user);
226: $avatar = $response->getValue();
227: $avatar = empty($avatar) ? '' : $avatar;
228:
229: $this->template->assign(array(
230: 'xoops_isuser' => true,
231: 'xoops_avatar' => $avatar,
232: 'xoops_userid' => $xoops->user->getVar('uid'), 'xoops_uname' => $xoops->user->getVar('uname'),
233: 'xoops_name' => $xoops->user->getVar('name'), 'xoops_isadmin' => $xoops->isAdmin(),
234: 'xoops_usergroups' => $xoops->user->getGroups()
235: ));
236: } else {
237: $this->template->assign(array(
238: 'xoops_isuser' => false,
239: 'xoops_isadmin' => false,
240: 'xoops_usergroups' => array(FixedGroups::ANONYMOUS)
241: ));
242: }
243:
244:
245: $metas = array(
246: 'description', 'keywords', 'robots', 'rating', 'author', 'copyright'
247: );
248: foreach ($metas as $name) {
249: $this->addMeta('meta', $name, $xoops->getConfig('meta_' . $name));
250: }
251:
252:
253: $assigns = array(
254: 'title', 'slogan', 'locale', 'footer', 'jquery_theme', 'startpage'
255: );
256: foreach ($assigns as $name) {
257:
258: $this->template->assign("xoops_$name", $xoops->getConfig($name));
259: }
260:
261:
262:
263:
264: list($cssAssets, $jsAssets) = $this->getLocalizationAssets();
265: if (!empty($cssAssets)) {
266: $this->addBaseStylesheetAssets($cssAssets);
267: }
268: $this->addBaseScriptAssets('include/xoops.js');
269: $this->addBaseScriptAssets('@jquery');
270:
271: if (!empty($jsAssets)) {
272: $this->addBaseScriptAssets($jsAssets);
273: }
274:
275: if ($this->bufferOutput) {
276: ob_start();
277: }
278: $xoops->setTheme($this);
279: $xoops->setTpl($this->template);
280:
281:
282: $GLOBALS['xoTheme'] = $xoops->theme();
283: $GLOBALS['xoopsTpl'] = $xoops->tpl();
284:
285:
286:
287:
288: if (\XoopsLoad::fileExists($this->path . "/theme_onload.php")) {
289: include_once($this->path . "/theme_onload.php");
290: }
291:
292:
293: foreach ($this->plugins as $k => $bundleId) {
294: if (!is_object($bundleId)) {
295:
296: $plugin = new $bundleId();
297: $plugin->theme = $this;
298: $plugin->xoInit();
299:
300: $this->plugins[$bundleId] = null;
301: $this->plugins[$bundleId] = $plugin;
302: unset($this->plugins[$k]);
303: }
304: }
305: return true;
306: }
307:
308: 309: 310: 311: 312: 313: 314: 315: 316:
317: public function generateCacheId($cache_id, $extraString = '')
318: {
319: $xoops = \Xoops::getInstance();
320: static $extra_string;
321: if (!$this->use_extra_cache_id) {
322: return $cache_id;
323: }
324:
325: if (empty($extraString)) {
326: if (empty($extra_string)) {
327:
328: $extra_string = $xoops->getConfig('locale');
329:
330: if (!$xoops->isUser()) {
331: $extra_string .= '-' . FixedGroups::ANONYMOUS;
332: } else {
333: $groups = $xoops->user->getGroups();
334: sort($groups);
335:
336:
337:
338: $extra_string .= '-' . substr(md5(implode('-', $groups)), 0, 8) . '-' . substr(md5(
339: \XoopsBaseConfig::get('db-pass') . \XoopsBaseConfig::get('db-name')
340: . \XoopsBaseConfig::get('db-user')
341: ), 0, 8);
342: }
343: }
344: $extraString = $extra_string;
345: }
346: $cache_id .= '-' . $extraString;
347: return $cache_id;
348: }
349:
350: 351: 352: 353: 354:
355: public function checkCache()
356: {
357: if ($_SERVER['REQUEST_METHOD'] !== 'POST' && $this->contentCacheLifetime) {
358: $template = $this->contentTemplate ? $this->contentTemplate : 'module:system/system_dummy.tpl';
359: $this->template->caching = 2;
360: $this->template->cache_lifetime = $this->contentCacheLifetime;
361: $uri = str_replace(\XoopsBaseConfig::get('url'), '', $_SERVER['REQUEST_URI']);
362:
363: if (defined('SID') && SID && strpos($uri, SID)) {
364: $uri = preg_replace("/([\?&])(" . SID . "$|" . SID . "&)/", "\\1", $uri);
365: }
366: $this->contentCacheId = $this->generateCacheId('page_' . substr(md5($uri), 0, 8));
367: if ($this->template->isCached($template, $this->contentCacheId)) {
368: \Xoops::getInstance()->events()->triggerEvent('core.theme.checkcache.success', array($template, $this));
369: $this->render(null, null, $template);
370: return true;
371: }
372: }
373: return false;
374: }
375:
376: 377: 378: 379: 380: 381: 382: 383: 384: 385: 386: 387: 388: 389:
390: public function render($canvasTpl = null, $pageTpl = null, $contentTpl = null, $vars = array())
391: {
392: if ($this->renderCount) {
393: return false;
394: }
395: $xoops = \Xoops::getInstance();
396: $xoops->events()->triggerEvent('core.theme.render.start', array($this));
397: $cache = $xoops->cache($this->headersCacheEngine);
398:
399:
400: if ($this->contentCacheLifetime && $this->contentCacheId && $content = $cache->read($this->contentCacheId)) {
401:
402: $this->htmlHeadStrings = array_merge($this->htmlHeadStrings, $content['htmlHeadStrings']);
403: foreach ($content['metas'] as $type => $value) {
404: $this->metas[$type] = array_merge($this->metas[$type], $content['metas'][$type]);
405: }
406: $xoops->setOption('xoops_pagetitle', $content['xoops_pagetitle']);
407: $xoops->setOption('xoops_module_header', $content['header']);
408: }
409:
410: if ($xoops->getOption('xoops_pagetitle')) {
411: $this->template->assign('xoops_pagetitle', $xoops->getOption('xoops_pagetitle'));
412: }
413: $header = !$xoops->getOption('xoops_module_header')
414: ? $this->template->getTemplateVars('xoops_module_header')
415: : $xoops->getOption('xoops_module_header');
416:
417:
418: if ($this->contentCacheLifetime && $this->contentCacheId && !$contentTpl) {
419: $content['htmlHeadStrings'] = (array)$this->htmlHeadStrings;
420: $content['metas'] = (array)$this->metas;
421: $content['xoops_pagetitle'] = $this->template->getTemplateVars('xoops_pagetitle');
422: $content['header'] = $header;
423: $cache->write($this->contentCacheId, $content);
424: }
425:
426:
427: $old = array('robots', 'keywords', 'description', 'rating', 'author', 'copyright');
428: foreach ($this->metas['meta'] as $name => $value) {
429: if (in_array($name, $old)) {
430: $this->template->assign("xoops_meta_$name", htmlspecialchars($value, ENT_QUOTES));
431: unset($this->metas['meta'][$name]);
432: }
433: }
434:
435:
436:
437: $this->template->assign('xoops_module_header', $this->renderMetas(true) . "\n" . $header);
438:
439: if ($canvasTpl) {
440: $this->canvasTemplate = $canvasTpl;
441: }
442: if ($contentTpl) {
443: $this->contentTemplate = $contentTpl;
444: }
445: if (!empty($vars)) {
446: $this->template->assign($vars);
447: }
448: if ($this->contentTemplate) {
449: $this->content = $this->template->fetch($this->contentTemplate, $this->contentCacheId);
450: }
451: if ($this->bufferOutput) {
452: $this->content .= ob_get_contents();
453: ob_end_clean();
454: }
455:
456: $this->template->assignByRef('xoops_contents', $this->content);
457:
458:
459: $this->template->caching = 0;
460: if (false === (bool)($xoops->getConfig('disable_theme_shortcodes'))) {
461: $this->template->loadFilter('output', 'shortcodes');
462: }
463: $this->template->display($this->path . '/' . $this->canvasTemplate);
464: $this->renderCount++;
465: $xoops->events()->triggerEvent('core.theme.render.end', array($this));
466: return true;
467: }
468:
469: 470: 471: 472: 473: 474: 475: 476: 477: 478: 479: 480:
481: public function getLocalizationAssets($type = "main")
482: {
483: $cssAssets = array();
484: $jsAssets = array();
485:
486: $xoops = \Xoops::getInstance();
487:
488: \Xoops\Locale::loadThemeLocale($this);
489:
490: $language = \XoopsLocale::getLocale();
491:
492: if (\XoopsLoad::fileExists($xoops->path('locale/' . $language . '/style.css'))) {
493: $cssAssets[] = $xoops->path('locale/' . $language . '/style.css');
494: }
495:
496:
497: if (\XoopsLoad::fileExists($this->path . '/locale/' . $language . '/script.js')) {
498: $jsAssets[] = $this->url . '/locale/' . $language . '/script.js';
499: }
500: if (\XoopsLoad::fileExists($this->path . '/locale/' . $language . '/style.css')) {
501: $cssAssets[] = $this->path . '/locale/' . $language . '/style.css';
502: }
503: return array($cssAssets, $jsAssets);
504: }
505:
506: 507: 508: 509: 510: 511: 512: 513:
514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524: 525: 526:
527:
528: 529: 530: 531:
532: 533: 534: 535: 536: 537: 538: 539: 540: 541: 542: 543: 544: 545: 546: 547: 548: 549: 550: 551: 552: 553: 554: 555:
556: public function addScript($src = '', $attributes = array(), $content = '')
557: {
558: $xoops = \Xoops::getInstance();
559: if (empty($attributes)) {
560: $attributes = array();
561: }
562: if (!empty($src)) {
563: $src = $xoops->url($this->resourcePath($src));
564: $attributes['src'] = $src;
565: }
566: if (!empty($content)) {
567: $attributes['_'] = $content;
568: }
569: if (!isset($attributes['type'])) {
570: $attributes['type'] = 'text/javascript';
571: }
572: $this->addMeta('script', $src, $attributes);
573: }
574:
575: 576: 577: 578: 579: 580: 581: 582: 583:
584: public function addStylesheet($src = '', $attributes = array(), $content = '')
585: {
586: $xoops = \Xoops::getInstance();
587: if (empty($attributes)) {
588: $attributes = array();
589: }
590: if (!empty($src)) {
591: $src = $xoops->url($this->resourcePath($src));
592: $attributes['href'] = $src;
593: }
594: if (!isset($attributes['type'])) {
595: $attributes['type'] = 'text/css';
596: }
597: if (!empty($content)) {
598: $attributes['_'] = $content;
599: }
600: $this->addMeta('stylesheet', $src, $attributes);
601: }
602:
603: 604: 605: 606: 607: 608: 609: 610: 611:
612: public function addScriptAssets($assets, $filters = 'default', $target = null)
613: {
614: $url = $this->assets->getUrlToAssets('js', $assets, $filters, $target);
615: $this->addScript($url);
616: }
617:
618: 619: 620: 621: 622: 623: 624: 625: 626:
627: public function addStylesheetAssets($assets, $filters = 'default', $target = null)
628: {
629: $url = $this->assets->getUrlToAssets('css', $assets, $filters, $target);
630: $this->addStylesheet($url);
631: }
632:
633: 634: 635: 636: 637: 638: 639: 640: 641:
642: public function addBaseAssets($type, $assets)
643: {
644: if (is_scalar($assets)) {
645: $this->baseAssets[$type][]=$assets;
646: } elseif (is_array($assets)) {
647: $this->baseAssets[$type] = array_merge($this->baseAssets[$type], $assets);
648: }
649: }
650:
651: 652: 653: 654: 655: 656: 657:
658: public function addBaseScriptAssets($assets)
659: {
660: $this->addBaseAssets('js', $assets);
661: }
662:
663: 664: 665: 666: 667: 668: 669:
670: public function addBaseStylesheetAssets($assets)
671: {
672: $this->addBaseAssets('css', $assets);
673: }
674:
675: 676: 677: 678: 679: 680: 681: 682: 683: 684: 685: 686: 687: 688: 689: 690: 691: 692: 693: 694:
695: public function setNamedAsset($name, $assets, $filters = null)
696: {
697: return $this->assets->registerAssetReference($name, $assets, $filters);
698: }
699:
700: 701: 702: 703: 704: 705: 706: 707: 708:
709: public function addLink($rel, $href = '', $attributes = array())
710: {
711: if (empty($attributes)) {
712: $attributes = array();
713: }
714: if (!empty($href)) {
715: $attributes['href'] = $href;
716: }
717: $attributes['rel'] = $rel;
718: $this->addMeta('link', '', $attributes);
719: }
720:
721: 722: 723: 724: 725: 726: 727: 728:
729: public function addHttpMeta($name, $value = null)
730: {
731: if (isset($value)) {
732: return $this->addMeta('http', $name, $value);
733: }
734: unset($this->metas['http'][$name]);
735: return false;
736: }
737:
738: 739: 740: 741: 742: 743: 744: 745: 746:
747: public function addMeta($type = 'meta', $name = '', $value = '')
748: {
749: if (!isset($this->metas[$type])) {
750: $this->metas[$type] = array();
751: }
752: if (!empty($name)) {
753: $this->metas[$type][$name] = $value;
754: } else {
755: $this->metas[$type][md5(serialize(array($value)))] = $value;
756: }
757: return $value;
758: }
759:
760: 761: 762: 763: 764: 765: 766: 767: 768: 769:
770: public function headContent($params, $content, &$smarty, &$repeat)
771: {
772: if (!$repeat) {
773: $this->htmlHeadStrings[] = $content;
774: }
775: }
776:
777: 778: 779: 780: 781: 782: 783:
784: public function renderMetas($return = false)
785: {
786: $str = '';
787:
788: if (!empty($this->baseAssets['js'])) {
789: $url = $this->assets->getUrlToAssets('js', $this->baseAssets['js']);
790: if (!empty($url)) {
791: $str .= '<script src="' . $url . '" type="text/javascript"></script>'."\n";
792: }
793: }
794:
795: if (!empty($this->baseAssets['css'])) {
796: $url = $this->assets->getUrlToAssets('css', $this->baseAssets['css']);
797: if (!empty($url)) {
798: $str .= '<link rel="stylesheet" href="' . $url . '" type="text/css" />'."\n";
799: }
800: }
801:
802: foreach (array_keys($this->metas) as $type) {
803: $str .= $this->renderMetasByType($type);
804: }
805: $str .= implode("\n", $this->htmlHeadStrings);
806:
807: if ($return) {
808: return $str;
809: }
810: echo $str;
811: return true;
812: }
813:
814: 815: 816: 817: 818: 819: 820:
821: public function renderMetasByType($type)
822: {
823: if (!isset($type)) {
824: return '';
825: }
826:
827: $str = '';
828: switch ($type) {
829: case 'script':
830: foreach ($this->metas[$type] as $attrs) {
831: $str .= "<script" . $this->renderAttributes($attrs) . ">";
832: if (@$attrs['_']) {
833: $str .= "\n//<![CDATA[\n" . $attrs['_'] . "\n//]]>";
834: }
835: $str .= "</script>\n";
836: }
837: break;
838: case 'link':
839: foreach ($this->metas[$type] as $attrs) {
840: $rel = $attrs['rel'];
841: unset($attrs['rel']);
842: $str .= '<link rel="' . $rel . '"' . $this->renderAttributes($attrs) . " />\n";
843: }
844: break;
845: case 'stylesheet':
846: foreach ($this->metas[$type] as $attrs) {
847: if (@$attrs['_']) {
848: $str .= '<style' . $this->renderAttributes($attrs)
849: . ">\n/* <![CDATA[ */\n" . $attrs['_'] . "\n/* //]]> */\n</style>";
850: } else {
851: $str .= '<link rel="stylesheet"' . $this->renderAttributes($attrs) . " />\n";
852: }
853: }
854: break;
855: case 'http':
856: foreach ($this->metas[$type] as $name => $content) {
857: $str .= '<meta http-equiv="' . htmlspecialchars($name, ENT_QUOTES) . '" content="'
858: . htmlspecialchars($content, ENT_QUOTES) . "\" />\n";
859: }
860: break;
861: default:
862: foreach ($this->metas[$type] as $name => $content) {
863: $str .= '<meta name="' . htmlspecialchars($name, ENT_QUOTES) . '" content="'
864: . htmlspecialchars($content, ENT_QUOTES) . "\" />\n";
865: }
866: break;
867: }
868:
869: return $str;
870: }
871:
872: 873: 874: 875: 876: 877: 878:
879: public function genElementId($tagName = 'xos')
880: {
881: static $cache = array();
882: if (!isset($cache[$tagName])) {
883: $cache[$tagName] = 1;
884: }
885: return $tagName . '-' . $cache[$tagName]++;
886: }
887:
888: 889: 890: 891: 892: 893: 894:
895: public function renderAttributes($coll)
896: {
897: $str = '';
898: foreach ($coll as $name => $val) {
899: if ($name !== '_') {
900: $str .= ' ' . $name . '="' . htmlspecialchars($val, ENT_QUOTES) . '"';
901: }
902: }
903: return $str;
904: }
905:
906: 907: 908: 909: 910: 911: 912:
913: public function resourcePath($path)
914: {
915: if (substr($path, 0, 1) === '/') {
916: $path = substr($path, 1);
917: }
918: $xoops_root_path = \XoopsBaseConfig::get('root-path');
919:
920: if (\XoopsLoad::fileExists($xoops_root_path . "/{$this->themesPath}/{$this->folderName}/{$path}")) {
921:
922: return "{$this->themesPath}/{$this->folderName}/{$path}";
923: }
924:
925: if (\XoopsLoad::fileExists($xoops_root_path . "/themes/{$this->folderName}/{$path}")) {
926:
927: return "themes/{$this->folderName}/{$path}";
928: }
929:
930: return $path;
931: }
932: }
933: