1: <?php
2: /**
3: * xos_opal_Theme component class file
4: *
5: * You may not change or alter any portion of this comment or credits
6: * of supporting developers from this source code or any supporting source code
7: * which is considered copyrighted (c) material of the original comment or credit authors.
8: * This program is distributed in the hope that it will be useful,
9: * but WITHOUT ANY WARRANTY; without even the implied warranty of
10: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
11: *
12: * @copyright (c) 2000-2020 XOOPS Project (https://xoops.org)
13: * @license GNU GPL 2 (https://www.gnu.org/licenses/gpl-2.0.html)
14: * @author Skalpa Keo <skalpa@xoops.org>
15: * @author Taiwen Jiang <phppp@users.sourceforge.net>
16: * @since 2.3.0
17: * @package kernel
18: * @subpackage xos_opal_Theme
19: */
20:
21: use Xmf\Request;
22:
23: defined('XOOPS_ROOT_PATH') || exit('Restricted access');
24:
25: /**
26: * xos_opal_ThemeFactory
27: *
28: * @author Skalpa Keo
29: * @package xos_opal
30: * @subpackage xos_opal_Theme
31: * @since 2.3.0
32: */
33: class xos_opal_ThemeFactory
34: {
35: public $xoBundleIdentifier = 'xos_opal_ThemeFactory';
36: /**
37: * Currently enabled themes (if empty, all the themes in themes/ are allowed)
38: *
39: * @var array
40: */
41: public $allowedThemes = array();
42: /**
43: * Default theme to instantiate if none specified
44: *
45: * @var string
46: */
47: public $defaultTheme = 'default';
48: /**
49: * If users are allowed to choose a custom theme
50: *
51: * @var bool
52: */
53: public $allowUserSelection = true;
54:
55: /**
56: * Instantiate the specified theme
57: * @param array $options
58: * @param array $initArgs
59: * @return null|xos_opal_Theme
60: */
61: public function createInstance($options = array(), $initArgs = array())
62: {
63: // Grab the theme folder from request vars if present
64: if (empty($options['folderName'])) {
65: if (isset($_REQUEST['xoops_theme_select']) && ($req = $_REQUEST['xoops_theme_select']) && $this->isThemeAllowed($req)) {
66: $options['folderName'] = $req;
67: if (isset($_SESSION) && $this->allowUserSelection) {
68: $_SESSION[$this->xoBundleIdentifier]['defaultTheme'] = $req;
69: }
70: } elseif (isset($_SESSION[$this->xoBundleIdentifier]['defaultTheme'])) {
71: $options['folderName'] = $_SESSION[$this->xoBundleIdentifier]['defaultTheme'];
72: } elseif (empty($options['folderName']) || !$this->isThemeAllowed($options['folderName'])) {
73: $options['folderName'] = $this->defaultTheme;
74: }
75: $GLOBALS['xoopsConfig']['theme_set'] = $options['folderName'];
76: }
77: $testPath = isset($options['themesPath'])
78: ? XOOPS_ROOT_PATH . '/' . $options['themesPath'] . '/' . $options['folderName']
79: : XOOPS_THEME_PATH . '/' . $options['folderName'];
80: if (!(file_exists($testPath . '/theme.tpl')
81: || file_exists($testPath . '/theme.html'))
82: ) {
83: trigger_error('Theme not found -- ' . $options['folderName']);
84: $this->defaultTheme = 'default';
85: $options['folderName'] = $this->defaultTheme;
86: $GLOBALS['xoopsConfig']['theme_set'] = $options['folderName'];
87: }
88: $options['path'] = XOOPS_THEME_PATH . '/' . $options['folderName'];
89: $inst = new xos_opal_Theme();
90: foreach ($options as $k => $v) {
91: $inst->$k = $v;
92: }
93: $inst->xoInit();
94:
95: return $inst;
96: }
97:
98: /**
99: * Checks if the specified theme is enabled or not
100: *
101: * @param string $name
102: * @return bool
103: */
104: public function isThemeAllowed($name)
105: {
106: return (empty($this->allowedThemes) || in_array($name, $this->allowedThemes));
107: }
108: }
109:
110: /**
111: * xos_opal_AdminThemeFactory
112: *
113: * @author Andricq Nicolas (AKA MusS)
114: * @author trabis
115: * @package xos_opal
116: * @subpackage xos_opal_Theme
117: * @since 2.4.0
118: */
119: class xos_opal_AdminThemeFactory extends xos_opal_ThemeFactory
120: {
121: /**
122: * @param array $options
123: * @param array $initArgs
124: *
125: * @return null|xos_opal_Theme
126: */
127: public function &createInstance($options = array(), $initArgs = array())
128: {
129: $options['plugins'] = array();
130: $options['renderBanner'] = false;
131: $inst = parent::createInstance($options, $initArgs);
132: $inst->path = XOOPS_ADMINTHEME_PATH . '/' . $inst->folderName;
133: $inst->url = XOOPS_ADMINTHEME_URL . '/' . $inst->folderName;
134: $inst->template->assign(array(
135: 'theme_path' => $inst->path,
136: 'theme_tpl' => $inst->path . '/xotpl',
137: 'theme_url' => $inst->url,
138: 'theme_img' => $inst->url . '/img',
139: 'theme_icons' => $inst->url . '/icons',
140: 'theme_css' => $inst->url . '/css',
141: 'theme_js' => $inst->url . '/js',
142: 'theme_lang' => $inst->url . '/language'));
143:
144: return $inst;
145: }
146: }
147:
148: /**
149: * Class xos_opal_Theme
150: */
151: class xos_opal_Theme
152: {
153: /**
154: * Should we render banner? Not for redirect pages or admin side
155: *
156: * @var bool
157: */
158: public $renderBanner = true;
159: /**
160: * The name of this theme
161: *
162: * @var string
163: */
164: public $folderName = '';
165: /**
166: * Physical path of this theme folder
167: *
168: * @var string
169: */
170: public $path = '';
171: public $url = '';
172:
173: /**
174: * Whether or not the theme engine should include the output generated by PHP
175: *
176: * @var string
177: */
178: public $bufferOutput = true;
179: /**
180: * Canvas-level template to use
181: *
182: * @var string
183: */
184: public $canvasTemplate = 'theme.tpl';
185:
186: /**
187: * Theme folder path
188: *
189: * @var string
190: */
191: public $themesPath = 'themes';
192:
193: /**
194: * Content-level template to use
195: *
196: * @var string
197: */
198: public $contentTemplate = '';
199:
200: public $contentCacheLifetime = 0;
201: public $contentCacheId;
202:
203: /**
204: * Text content to display right after the contentTemplate output
205: *
206: * @var string
207: */
208: public $content = '';
209: /**
210: * Page construction plug-ins to use
211: *
212: * @var array
213: * @access public
214: */
215: public $plugins = array(
216: 'xos_logos_PageBuilder');
217: public $renderCount = 0;
218: /**
219: * Pointer to the theme template engine
220: *
221: * @var XoopsTpl
222: */
223: public $template = false;
224:
225: /**
226: * Array containing the document meta-information
227: *
228: * @var array
229: */
230: public $metas = array(
231: //'http' => array(
232: // 'Content-Script-Type' => 'text/javascript' ,
233: // 'Content-Style-Type' => 'text/css') ,
234: 'meta' => array(),
235: 'link' => array(),
236: 'script' => array());
237:
238: /**
239: * Array of strings to be inserted in the head tag of HTML documents
240: *
241: * @var array
242: */
243: public $htmlHeadStrings = array();
244: /**
245: * Custom variables that will always be assigned to the template
246: *
247: * @var array
248: */
249: public $templateVars = array();
250:
251: /**
252: * User extra information for cache id, like language, user groups
253: *
254: * @var boolean
255: */
256: public $use_extra_cache_id = true;
257:
258: /**
259: * *#@-
260: */
261:
262: /**
263: * *#@+
264: *
265: * @tasktype 10 Initialization
266: */
267: /**
268: * Initializes this theme
269: *
270: * Upon initialization, the theme creates its template engine and instantiates the
271: * plug-ins from the specified {@link $plugins} list. If the theme is a 2.0 theme, that does not
272: * display redirection messages, the HTTP redirections system is disabled to ensure users will
273: * see the redirection screen.
274: *
275: * @param array $options
276: * @return bool
277: */
278: public function xoInit($options = array())
279: {
280: /** @var XoopsConfigHandler $configHandler */
281: $configHandler = xoops_getHandler('config');
282:
283: $this->path = XOOPS_THEME_PATH . '/' . $this->folderName;
284: $this->url = XOOPS_THEME_URL . '/' . $this->folderName;
285: $this->template = null;
286: $this->template = new XoopsTpl();
287: $this->template->currentTheme = $this;
288: $this->template->assignByRef('xoTheme', $this);
289: $GLOBALS['xoTheme'] = $this;
290: $GLOBALS['xoopsTpl'] = $this->template;
291: $tempPath = str_replace('\\', '/', realpath(XOOPS_ROOT_PATH) . '/');
292: $tempName = str_replace('\\', '/', realpath($_SERVER['SCRIPT_FILENAME']));
293: $xoops_page = str_replace($tempPath, '', $tempName);
294: if (strpos($xoops_page, 'modules') !== false) {
295: $xoops_page = str_replace('modules/', '', $xoops_page);
296: }
297: $tempScriptname = str_replace('\\', '/', $_SERVER['SCRIPT_NAME']);
298: $tempRequesturi = str_replace('\\', '/', Request::getString('REQUEST_URI', '', 'SERVER'));
299: if (strlen($tempRequesturi) > strlen($tempScriptname)) {
300: $xoops_modulepage = $xoops_page . str_replace($tempScriptname, '', $tempRequesturi);
301: } else {
302: $xoops_modulepage = '';
303: }
304: $xoops_page = str_replace('.php', '', $xoops_page);
305: if (isset($GLOBALS['xoopsConfig']['startpage'])) {
306: $xoops_startpage = $GLOBALS['xoopsConfig']['startpage'];
307: if ($xoops_startpage == '--') {
308: $xoops_startpage = 'system';
309: }
310: } else {
311: $xoops_startpage = 'system';
312: }
313: // call the theme_autorun.php if the theme has one
314: if (file_exists($this->path . "/theme_autorun.php")) {
315: include_once($this->path . "/theme_autorun.php");
316: }
317:
318: $searchConfig = $configHandler->getConfigsByCat(XOOPS_CONF_SEARCH);
319: $xoops_search = (bool) (isset($searchConfig['enable_search']) && $searchConfig['enable_search'] === 1);
320: $this->template->assign(array(
321: 'xoops_theme' => $GLOBALS['xoopsConfig']['theme_set'],
322: 'xoops_imageurl' => XOOPS_THEME_URL . '/' . $GLOBALS['xoopsConfig']['theme_set'] . '/',
323: 'xoops_themecss' => xoops_getcss($GLOBALS['xoopsConfig']['theme_set']),
324: 'xoops_requesturi' => htmlspecialchars($_SERVER['REQUEST_URI'], ENT_QUOTES),
325: 'xoops_sitename' => htmlspecialchars($GLOBALS['xoopsConfig']['sitename'], ENT_QUOTES),
326: 'xoops_slogan' => htmlspecialchars($GLOBALS['xoopsConfig']['slogan'], ENT_QUOTES),
327: 'xoops_dirname' => isset($GLOBALS['xoopsModule']) && is_object($GLOBALS['xoopsModule'])
328: ? $GLOBALS['xoopsModule']->getVar('dirname') : 'system',
329: 'xoops_page' => $xoops_page,
330: 'xoops_startpage' => $xoops_startpage,
331: 'xoops_modulepage' => $xoops_modulepage,
332: 'xoops_banner' => ($GLOBALS['xoopsConfig']['banners'] && $this->renderBanner)
333: ? xoops_getbanner() : '&nbsp;',
334: 'xoops_pagetitle' => isset($GLOBALS['xoopsModule']) && is_object($GLOBALS['xoopsModule'])
335: ? $GLOBALS['xoopsModule']->getVar('name')
336: : htmlspecialchars($GLOBALS['xoopsConfig']['slogan'], ENT_QUOTES),
337: 'xoops_search' => $xoops_search,
338: ));
339: if (isset($GLOBALS['xoopsUser']) && is_object($GLOBALS['xoopsUser'])) {
340: $this->template->assign(array(
341: 'xoops_isuser' => true,
342: 'xoops_avatar' => XOOPS_UPLOAD_URL . '/' . $GLOBALS['xoopsUser']->getVar('user_avatar'),
343: 'xoops_userid' => $GLOBALS['xoopsUser']->getVar('uid'),
344: 'xoops_uname' => $GLOBALS['xoopsUser']->getVar('uname'),
345: 'xoops_name' => $GLOBALS['xoopsUser']->getVar('name'),
346: 'xoops_isadmin' => $GLOBALS['xoopsUserIsAdmin'],
347: 'xoops_usergroups' => $GLOBALS['xoopsUser']->getGroups(),
348: ));
349: } else {
350: $this->template->assign(array(
351: 'xoops_isuser' => false,
352: 'xoops_isadmin' => false,
353: 'xoops_usergroups' => array(XOOPS_GROUP_ANONYMOUS),
354: ));
355: }
356:
357: // Meta tags
358: $criteria = new CriteriaCompo(new Criteria('conf_modid', 0));
359: $criteria->add(new Criteria('conf_catid', XOOPS_CONF_METAFOOTER));
360: $config = $configHandler->getConfigs($criteria, true);
361: foreach (array_keys($config) as $i) {
362: $name = $config[$i]->getVar('conf_name', 'n');
363: $value = $config[$i]->getVar('conf_value', 'n');
364: // limited substitutions for {X_SITEURL} and {X_YEAR}
365: if ($name === 'footer' || $name === 'meta_copyright') {
366: $value = str_replace('{X_SITEURL}', XOOPS_URL . '/', $value);
367: $value = str_replace('{X_YEAR}', date('Y', time()), $value);
368: }
369: if (substr($name, 0, 5) === 'meta_') {
370: $this->addMeta('meta', substr($name, 5), $value);
371: } else {
372: // prefix each tag with 'xoops_'
373: $this->template->assign("xoops_$name", $value);
374: }
375: }
376: // Load global javascript
377: $this->addScript('include/xoops.js');
378: $this->loadLocalization();
379:
380: if ($this->bufferOutput) {
381: ob_start();
382: }
383: // Instantiate and initialize all the theme plugins
384: foreach ($this->plugins as $k => $bundleId) {
385: if (!is_object($bundleId)) {
386: $this->plugins[$bundleId] = null;
387: $this->plugins[$bundleId] = new $bundleId();
388: $this->plugins[$bundleId]->theme =& $this;
389: $this->plugins[$bundleId]->xoInit();
390: unset($this->plugins[$k]);
391: }
392: }
393:
394: return true;
395: }
396:
397: /**
398: * Generate cache id based on extra information of language and user groups
399: *
400: * User groups other than anonymous should be detected to avoid disclosing group sensitive contents
401: *
402: * @param string $cache_id raw cache id
403: * @param string $extraString extra string
404: * @return string complete cache id
405: */
406: public function generateCacheId($cache_id, $extraString = '')
407: {
408: static $extra_string;
409: if (!$this->use_extra_cache_id) {
410: return $cache_id;
411: }
412:
413: if (empty($extraString)) {
414: if (empty($extra_string)) {
415: // Generate language section
416: $extra_string = $GLOBALS['xoopsConfig']['language'];
417: // Generate group section
418: if (!isset($GLOBALS['xoopsUser']) || !is_object($GLOBALS['xoopsUser'])) {
419: $extra_string .= '-' . XOOPS_GROUP_ANONYMOUS;
420: } else {
421: $groups = $GLOBALS['xoopsUser']->getGroups();
422: sort($groups);
423: // Generate group string for non-anonymous groups,
424: // XOOPS_DB_PASS and XOOPS_DB_NAME (before we find better variables) are used to protect group sensitive contents
425: $extra_string .= '-' . substr(md5(implode('-', $groups)), 0, 8) . '-' . substr(md5(XOOPS_DB_PASS . XOOPS_DB_NAME . XOOPS_DB_USER), 0, 8);
426: }
427: }
428: $extraString = $extra_string;
429: }
430: $cache_id .= '-' . $extraString;
431:
432: return $cache_id;
433: }
434:
435: /**
436: * xos_opal_Theme::checkCache()
437: *
438: * @return bool
439: */
440: public function checkCache()
441: {
442: if ($_SERVER['REQUEST_METHOD'] !== 'POST' && $this->contentCacheLifetime) {
443: $template = $this->contentTemplate ?: 'db:system_dummy.tpl';
444: $this->template->caching = 2;
445: $this->template->cache_lifetime = $this->contentCacheLifetime;
446: $uri = str_replace(XOOPS_URL, '', $_SERVER['REQUEST_URI']);
447: // Clean uri by removing session id
448: if (defined('SID') && SID && strpos($uri, SID)) {
449: $uri = preg_replace("/([\?&])(" . SID . "$|" . SID . '&)/', "\\1", $uri);
450: }
451: $this->contentCacheId = $this->generateCacheId('page_' . substr(md5($uri), 0, 8));
452: if ($this->template->isCached($template, $this->contentCacheId)) {
453: $xoopsLogger = XoopsLogger::getInstance();
454: $xoopsLogger->addExtra($template, sprintf('Cached (regenerates every %d seconds)', $this->contentCacheLifetime));
455: $this->render(null, null, $template);
456:
457: return true;
458: }
459: }
460:
461: return false;
462: }
463:
464: /**
465: * Render the page
466: *
467: * The theme engine builds pages from 2 templates: canvas and content.
468: *
469: * A module can call this method directly and specify what templates the theme engine must use.
470: * If render() hasn't been called before, the theme defaults will be used for the canvas and
471: * page template (and xoopsOption['template_main'] for the content).
472: *
473: * @param string $canvasTpl The canvas template, if different from the theme default
474: * @param string $pageTpl The page template, if different from the theme default (unsupported, 2.3+ only)
475: * @param string $contentTpl The content template
476: * @param array $vars Template variables to send to the template engine
477: *
478: * @return bool
479: */
480: public function render($canvasTpl = null, $pageTpl = null, $contentTpl = null, $vars = array())
481: {
482: if ($this->renderCount) {
483: return false;
484: }
485: $xoopsLogger = XoopsLogger::getInstance();
486: $xoopsLogger->startTime('Page rendering');
487:
488: xoops_load('xoopscache');
489: $cache = XoopsCache::getInstance();
490:
491: //Get meta information for cached pages
492: if ($this->contentCacheLifetime && $this->contentCacheId && $content = $cache->read($this->contentCacheId)) {
493: //we need to merge metas set by blocks, with the module cached meta
494: $this->htmlHeadStrings = array_merge($this->htmlHeadStrings, $content['htmlHeadStrings']);
495: foreach ($content['metas'] as $type => $value) {
496: $this->metas[$type] = array_merge($this->metas[$type], $content['metas'][$type]);
497: }
498: $GLOBALS['xoopsOption']['xoops_pagetitle'] = $content['xoops_pagetitle'];
499: $GLOBALS['xoopsOption']['xoops_module_header'] = $content['header'];
500: }
501: /** if cache was not found, define $content[] */
502: if (!isset($content) || false === $content) {
503: $content = array();
504: }
505: if (!empty($GLOBALS['xoopsOption']['xoops_pagetitle'])) {
506: $this->template->assign('xoops_pagetitle', $GLOBALS['xoopsOption']['xoops_pagetitle']);
507: }
508: $header = empty($GLOBALS['xoopsOption']['xoops_module_header']) ? $this->template->getTemplateVars('xoops_module_header') : $GLOBALS['xoopsOption']['xoops_module_header'];
509:
510: //save meta information of cached pages
511: if ($this->contentCacheLifetime && $this->contentCacheId && !$contentTpl) {
512: $content['htmlHeadStrings'] = $this->htmlHeadStrings;
513: $content['metas'] = $this->metas;
514: $content['xoops_pagetitle'] = $this->template->getTemplateVars('xoops_pagetitle');
515: $content['header'] = $header;
516: $cache->write($this->contentCacheId, $content);
517: }
518:
519: // @internal : Lame fix to ensure the metas specified in the xoops config page don't appear twice
520: $old = array(
521: 'robots',
522: 'keywords',
523: 'description',
524: 'rating',
525: 'author',
526: 'copyright');
527: foreach ($this->metas['meta'] as $name => $value) {
528: if (in_array($name, $old)) {
529: $this->template->assign("xoops_meta_$name", htmlspecialchars($value, ENT_QUOTES));
530: unset($this->metas['meta'][$name]);
531: }
532: }
533:
534: // We assume no overlap between $GLOBALS['xoopsOption']['xoops_module_header'] and $this->template->getTemplateVars( 'xoops_module_header' ) ?
535: $this->template->assign('xoops_module_header', $this->renderMetas(null, true) . "\n" . $header);
536:
537: if ($canvasTpl) {
538: $this->canvasTemplate = $canvasTpl;
539: }
540: if ($contentTpl) {
541: $this->contentTemplate = $contentTpl;
542: }
543: if (!empty($vars)) {
544: $this->template->assign($vars);
545: }
546: if ($this->contentTemplate) {
547: $this->content = $this->template->fetch($this->contentTemplate, $this->contentCacheId);
548: }
549: if ($this->bufferOutput) {
550: $this->content .= ob_get_contents();
551: ob_end_clean();
552: }
553:
554: $this->template->assignByRef('xoops_contents', $this->content);
555:
556: // Do not cache the main (theme.html) template output
557: $this->template->caching = 0;
558: if (file_exists($this->path . '/' . $this->canvasTemplate)) {
559: $this->template->display($this->path . '/' . $this->canvasTemplate);
560: } else {
561: $this->template->display($this->path . '/theme.html');
562: }
563: $this->renderCount++;
564: $xoopsLogger->stopTime('Page rendering');
565:
566: return true;
567: }
568:
569: /**
570: * Load localization information
571: *
572: * Folder structure for localization:
573: * <ul>themes/themefolder/english
574: * <li>main.php - language definitions</li>
575: * <li>style.css - localization stylesheet</li>
576: * <li>script.js - localization script</li>
577: * </ul>
578: * @param string $type
579: * @return bool
580: */
581: public function loadLocalization($type = 'main')
582: {
583: $language = $GLOBALS['xoopsConfig']['language'];
584: // Load global localization stylesheet if available
585: if (file_exists($GLOBALS['xoops']->path('language/' . $language . '/style.css'))) {
586: $this->addStylesheet($GLOBALS['xoops']->url('language/' . $language . '/style.css'));
587: }
588: $this->addLanguage($type, $language);
589: // Load theme localization stylesheet and scripts if available
590: if (file_exists($this->path . '/language/' . $language . '/script.js')) {
591: $this->addScript($this->url . '/language/' . $language . '/script.js');
592: }
593: if (file_exists($this->path . '/language/' . $language . '/style.css')) {
594: $this->addStylesheet($this->url . '/language/' . $language . '/style.css');
595: }
596:
597: return true;
598: }
599:
600: /**
601: * Load theme specific language constants
602: *
603: * @param string $type language type, like 'main', 'admin'; Needs to be declared in theme xo-info.php
604: * @param string $language specific language
605: *
606: * @return bool|mixed
607: */
608: public function addLanguage($type = 'main', $language = null)
609: {
610: $language = (null === $language) ? $GLOBALS['xoopsConfig']['language'] : $language;
611: if (!file_exists($fileinc = $this->path . "/language/{$language}/{$type}.php")) {
612: if (!file_exists($fileinc = $this->path . "/language/english/{$type}.php")) {
613: return false;
614: }
615: }
616: $ret = include_once $fileinc;
617:
618: return $ret;
619: }
620:
621: /**
622: * *#@+
623: *
624: * @tasktype 20 Manipulating page meta-information
625: */
626: /**
627: * Adds script code to the document head
628: *
629: * This methods allows the insertion of an external script file (if $src is provided), or
630: * of a script snippet. The file URI is parsed to take benefit of the theme resource
631: * overloading system.
632: *
633: * The $attributes parameter allows you to specify the attributes that will be added to the
634: * inserted <script> tag. If unspecified, the <var>type</var> attribute value will default to
635: * 'text/javascript'.
636: *
637: * <code>
638: * // Add an external script using a physical path
639: * $theme->addScript( 'www/script.js', null, '' );
640: * $theme->addScript( 'modules/newbb/script.js', null, '' );
641: * // Specify attributes for the <script> tag
642: * $theme->addScript( 'mod_xoops_SiteManager#common.js', array( 'type' => 'application/x-javascript' ), '', 'mod_xoops_Sitemanager' );
643: * // Insert a code snippet
644: * $theme->addScript( null, array( 'type' => 'application/x-javascript' ), 'window.open("Hello world");', 'hello' );
645: * </code>
646: *
647: * @param string $src path to an external script file
648: * @param array $attributes hash of attributes to add to the <script> tag
649: * @param string $content Code snippet to output within the <script> tag
650: * @param string $name Element Name in array scripts are stored in.
651: * @return void
652: */
653: public function addScript($src = '', $attributes = array(), $content = '', $name = '')
654: {
655: if (empty($attributes)) {
656: $attributes = array();
657: }
658: if (!empty($src)) {
659: $src = $GLOBALS['xoops']->url($this->resourcePath($src));
660: $attributes['src'] = $src;
661: }
662: if (!empty($content)) {
663: $attributes['_'] = $content;
664: }
665: if (!isset($attributes['type'])) {
666: $attributes['type'] = 'text/javascript';
667: }
668: if (empty($name)) {
669: $name = md5(serialize($attributes));
670: }
671: $this->addMeta('script', $name, $attributes);
672: }
673:
674: /**
675: * Add StyleSheet or CSS code to the document head
676: *
677: * @param string $src path to .css file
678: * @param array $attributes name => value paired array of attributes such as title
679: * @param string $content CSS code to output between the <style> tags (in case $src is empty)
680: * @param string $name Element Name in array stylesheets are stored in.
681: * @return void
682: */
683: public function addStylesheet($src = '', $attributes = array(), $content = '', $name = '')
684: {
685: if (empty($attributes)) {
686: $attributes = array();
687: }
688: if (!empty($src)) {
689: $src = $GLOBALS['xoops']->url($this->resourcePath($src));
690: $attributes['href'] = $src;
691: }
692: if (!isset($attributes['type'])) {
693: $attributes['type'] = 'text/css';
694: }
695: if (!empty($content)) {
696: $attributes['_'] = $content;
697: }
698: if (empty($name)) {
699: $name = md5(serialize($attributes));
700: }
701: $this->addMeta('stylesheet', $name, $attributes);
702: }
703:
704: /**
705: * Add a <link> to the header
706: *
707: * @param string $rel Relationship from the current doc to the anchored one
708: * @param string $href URI of the anchored document
709: * @param array $attributes Additional attributes to add to the <link> element
710: * @param string $name Element Name in array links are stored in.
711: */
712: public function addLink($rel, $href = '', $attributes = array(), $name = '')
713: {
714: if (empty($attributes)) {
715: $attributes = array();
716: }
717: if (!empty($href)) {
718: $attributes['href'] = $href;
719: }
720: $attributes['rel'] = $rel;
721: if (empty($name)) {
722: $name = md5(serialize($attributes));
723: }
724: $this->addMeta('link', $name, $attributes);
725: }
726:
727: /**
728: * Set a meta http-equiv value
729: * @param $name
730: * @param null $value
731: * @return string
732: */
733: public function addHttpMeta($name, $value = null)
734: {
735: if (isset($value)) {
736: return $this->addMeta('http', $name, $value);
737: }
738: unset($this->metas['http'][$name]);
739: return null;
740: }
741:
742: /**
743: * Change output page meta-information
744: * @param string $type
745: * @param string $name
746: * @param string $value
747: * @return string
748: */
749: public function addMeta($type = 'meta', $name = '', $value = '')
750: {
751: if (!isset($this->metas[$type])) {
752: $this->metas[$type] = array();
753: }
754: if (!empty($name)) {
755: $this->metas[$type][$name] = $value;
756: } else {
757: $this->metas[$type][md5(serialize(array($value)))] = $value;
758: }
759:
760: return $value;
761: }
762:
763: /**
764: * xos_opal_Theme::headContent()
765: *
766: * @param mixed $params
767: * @param mixed $content
768: * @param mixed $smarty
769: * @param mixed $repeat
770: *
771: * @return void
772: */
773: public function headContent($params, $content, &$smarty, &$repeat)
774: {
775: if (!$repeat) {
776: $this->htmlHeadStrings[] = $content;
777: }
778: }
779:
780: /**
781: * xos_opal_Theme::renderMetas()
782: *
783: * @param mixed $type
784: * @param mixed $return
785: * @return bool|string
786: */
787: public function renderMetas($type = null, $return = false)
788: {
789: $str = '';
790: if (!isset($type)) {
791: foreach (array_keys($this->metas) as $type) {
792: $str .= $this->renderMetas($type, true);
793: }
794: $str .= implode("\n", $this->htmlHeadStrings);
795: } else {
796: switch ($type) {
797: case 'script':
798: foreach ($this->metas[$type] as $attrs) {
799: $str .= '<script' . $this->renderAttributes($attrs) . '>';
800: if (isset($attrs['_'])) {
801: $str .= "\n//<![CDATA[\n" . $attrs['_'] . "\n//]]>";
802: }
803: $str .= "</script>\n";
804: }
805: break;
806: case 'link':
807: foreach ($this->metas[$type] as $attrs) {
808: $rel = $attrs['rel'];
809: unset($attrs['rel']);
810: $str .= '<link rel="' . $rel . '"' . $this->renderAttributes($attrs) . " />\n";
811: }
812: break;
813: case 'stylesheet':
814: foreach ($this->metas[$type] as $attrs) {
815: if (isset($attrs['_'])) {
816: $str .= '<style' . $this->renderAttributes($attrs) . ">\n/* <![CDATA[ */\n" . (isset($attrs['_'])?$attrs['_']:'') . "\n/* //]]> */\n</style>";
817: } else {
818: $str .= '<link rel="stylesheet"' . $this->renderAttributes($attrs) . " />\n";
819: }
820: }
821: break;
822: case 'http':
823: foreach ($this->metas[$type] as $name => $content) {
824: $str .= '<meta http-equiv="' . htmlspecialchars($name, ENT_QUOTES) . '" content="' . htmlspecialchars($content, ENT_QUOTES) . "\" />\n";
825: }
826: break;
827: default:
828: foreach ($this->metas[$type] as $name => $content) {
829: $str .= '<meta name="' . htmlspecialchars($name, ENT_QUOTES) . '" content="' . htmlspecialchars($content, ENT_QUOTES) . "\" />\n";
830: }
831: break;
832: }
833: }
834: if ($return) {
835: return $str;
836: }
837: echo $str;
838:
839: return true;
840: }
841:
842: /**
843: * Generates a unique element ID
844: *
845: * @param string $tagName
846: * @return string
847: */
848: public function genElementId($tagName = 'xos')
849: {
850: static $cache = array();
851: if (!isset($cache[$tagName])) {
852: $cache[$tagName] = 1;
853: }
854:
855: return $tagName . '-' . $cache[$tagName]++;
856: }
857:
858: /**
859: * Transform an attribute collection to an XML string
860: *
861: * @param array $coll
862: * @return string
863: */
864: public function renderAttributes($coll)
865: {
866: $str = '';
867: foreach ($coll as $name => $val) {
868: if ($name !== '_') {
869: $str .= ' ' . $name . '="' . htmlspecialchars($val, ENT_QUOTES) . '"';
870: }
871: }
872:
873: return $str;
874: }
875:
876: /**
877: * Return a themeable file resource path
878: *
879: * @param string $path
880: * @return string
881: */
882: public function resourcePath($path)
883: {
884: $path = (string) $path;
885: if (substr($path, 0, 1) === '/') {
886: $path = substr($path, 1);
887: }
888:
889: if (file_exists(XOOPS_ROOT_PATH . "/{$this->themesPath}/{$this->folderName}/{$path}")) {
890: return "{$this->themesPath}/{$this->folderName}/{$path}";
891: }
892:
893: if (file_exists(XOOPS_ROOT_PATH . "/themes/{$this->folderName}/{$path}")) {
894: return "themes/{$this->folderName}/{$path}";
895: }
896:
897: return $path;
898: }
899: }
900: