Completed
Pull Request — master (#221)
by Richard
12:03
created

xos_opal_Theme::resourcePath()   A

Complexity

Conditions 4
Paths 6

Size

Total Lines 16
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 8
nc 6
nop 1
dl 0
loc 16
rs 9.2
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 31 and the first side effect is on line 21.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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-2016 XOOPS Project (www.xoops.org)
13
 * @license             GNU GPL 2 (http://www.gnu.org/licenses/gpl-2.0.html)
14
 * @author              Skalpa Keo <[email protected]>
15
 * @author              Taiwen Jiang <[email protected]>
16
 * @since               2.3.0
17
 * @package             kernel
18
 * @subpackage          xos_opal_Theme
19
 */
20
21
defined('XOOPS_ROOT_PATH') || exit('Restricted access');
22
23
/**
24
 * xos_opal_ThemeFactory
25
 *
26
 * @author     Skalpa Keo
27
 * @package    xos_opal
28
 * @subpackage xos_opal_Theme
29
 * @since      2.3.0
30
 */
31
class xos_opal_ThemeFactory
32
{
33
    public $xoBundleIdentifier = 'xos_opal_ThemeFactory';
34
    /**
35
     * Currently enabled themes (if empty, all the themes in themes/ are allowed)
36
     *
37
     * @var array
38
     */
39
    public $allowedThemes = array();
40
    /**
41
     * Default theme to instanciate if none specified
42
     *
43
     * @var string
44
     */
45
    public $defaultTheme = 'default';
46
    /**
47
     * If users are allowed to choose a custom theme
48
     *
49
     * @var bool
50
     */
51
    public $allowUserSelection = true;
52
53
    /**
54
     * Instanciate the specified theme
55
     * @param  array $options
56
     * @param  array $initArgs
57
     * @return null|xos_opal_Theme
58
     */
59
    public function createInstance($options = array(), $initArgs = array())
60
    {
61
        // Grab the theme folder from request vars if present
62
        if (empty($options['folderName'])) {
63
            if (($req = @$_REQUEST['xoops_theme_select']) && $this->isThemeAllowed($req)) {
64
                $options['folderName'] = $req;
65
                if (isset($_SESSION) && $this->allowUserSelection) {
66
                    $_SESSION[$this->xoBundleIdentifier]['defaultTheme'] = $req;
67
                }
68
            } elseif (isset($_SESSION[$this->xoBundleIdentifier]['defaultTheme'])) {
69
                $options['folderName'] = $_SESSION[$this->xoBundleIdentifier]['defaultTheme'];
70
            } elseif (empty($options['folderName']) || !$this->isThemeAllowed($options['folderName'])) {
71
                $options['folderName'] = $this->defaultTheme;
72
            }
73
            $GLOBALS['xoopsConfig']['theme_set'] = $options['folderName'];
74
        }
75
        $options['path'] = XOOPS_THEME_PATH . '/' . $options['folderName'];
76
        $inst            = null;
0 ignored issues
show
Unused Code introduced by
$inst is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
77
        $inst            = new xos_opal_Theme();
78
        foreach ($options as $k => $v) {
79
            $inst->$k = $v;
80
        }
81
        $inst->xoInit();
82
83
        return $inst;
84
    }
85
86
    /**
87
     * Checks if the specified theme is enabled or not
88
     *
89
     * @param  string $name
90
     * @return bool
91
     */
92
    public function isThemeAllowed($name)
93
    {
94
        return (empty($this->allowedThemes) || in_array($name, $this->allowedThemes));
95
    }
96
}
97
98
/**
99
 * xos_opal_AdminThemeFactory
100
 *
101
 * @author     Andricq Nicolas (AKA MusS)
102
 * @author     trabis
103
 * @package    xos_opal
104
 * @subpackage xos_opal_Theme
105
 * @since      2.4.0
106
 */
107
class xos_opal_AdminThemeFactory extends xos_opal_ThemeFactory
0 ignored issues
show
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...
108
{
109
    /**
110
     * @param array $options
111
     * @param array $initArgs
112
     *
113
     * @return null|xos_opal_Theme
114
     */
115
    public function &createInstance($options = array(), $initArgs = array())
116
    {
117
        $options['plugins']      = array();
118
        $options['renderBanner'] = false;
119
        $inst                    = parent::createInstance($options, $initArgs);
120
        $inst->path              = XOOPS_ADMINTHEME_PATH . '/' . $inst->folderName;
121
        $inst->url               = XOOPS_ADMINTHEME_URL . '/' . $inst->folderName;
122
        $inst->template->assign(array(
123
                                    'theme_path'  => $inst->path,
124
                                    'theme_tpl'   => $inst->path . '/xotpl',
125
                                    'theme_url'   => $inst->url,
126
                                    'theme_img'   => $inst->url . '/img',
127
                                    'theme_icons' => $inst->url . '/icons',
128
                                    'theme_css'   => $inst->url . '/css',
129
                                    'theme_js'    => $inst->url . '/js',
130
                                    'theme_lang'  => $inst->url . '/language'));
131
132
        return $inst;
133
    }
134
}
135
136
/**
137
 * Class xos_opal_Theme
138
 */
139
class xos_opal_Theme
0 ignored issues
show
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...
140
{
141
    /**
142
     * Should we render banner? Not for redirect pages or admin side
143
     *
144
     * @var bool
145
     */
146
    public $renderBanner = true;
147
    /**
148
     * The name of this theme
149
     *
150
     * @var string
151
     */
152
    public $folderName = '';
153
    /**
154
     * Physical path of this theme folder
155
     *
156
     * @var string
157
     */
158
    public $path = '';
159
    public $url  = '';
160
161
    /**
162
     * Whether or not the theme engine should include the output generated by php
163
     *
164
     * @var string
165
     */
166
    public $bufferOutput = true;
167
    /**
168
     * Canvas-level template to use
169
     *
170
     * @var string
171
     */
172
    public $canvasTemplate = 'theme.html';
173
174
    /**
175
     * Theme folder path
176
     *
177
     * @var string
178
     */
179
    public $themesPath = 'themes';
180
181
    /**
182
     * Content-level template to use
183
     *
184
     * @var string
185
     */
186
    public $contentTemplate = '';
187
188
    public $contentCacheLifetime = 0;
189
    public $contentCacheId;
190
191
    /**
192
     * Text content to display right after the contentTemplate output
193
     *
194
     * @var string
195
     */
196
    public $content = '';
197
    /**
198
     * Page construction plug-ins to use
199
     *
200
     * @var array
201
     * @access public
202
     */
203
    public $plugins     = array(
204
        'xos_logos_PageBuilder');
205
    public $renderCount = 0;
206
    /**
207
     * Pointer to the theme template engine
208
     *
209
     * @var XoopsTpl
210
     */
211
    public $template = false;
212
213
    /**
214
     * Array containing the document meta-information
215
     *
216
     * @var array
217
     */
218
    public $metas = array(
219
        //'http' => array(
220
        //    'Content-Script-Type' => 'text/javascript' ,
221
        //    'Content-Style-Type' => 'text/css') ,
222
        'meta'   => array(),
223
        'link'   => array(),
224
        'script' => array());
225
226
    /**
227
     * Array of strings to be inserted in the head tag of HTML documents
228
     *
229
     * @var array
230
     */
231
    public $htmlHeadStrings = array();
232
    /**
233
     * Custom variables that will always be assigned to the template
234
     *
235
     * @var array
236
     */
237
    public $templateVars = array();
238
239
    /**
240
     * User extra information for cache id, like language, user groups
241
     *
242
     * @var boolean
243
     */
244
    public $use_extra_cache_id = true;
245
246
    /**
247
     * *#@-
248
     */
249
250
    /**
251
     * *#@+
252
     *
253
     * @tasktype 10 Initialization
254
     */
255
    /**
256
     * Initializes this theme
257
     *
258
     * Upon initialization, the theme creates its template engine and instantiates the
259
     * plug-ins from the specified {@link $plugins} list. If the theme is a 2.0 theme, that does not
260
     * display redirection messages, the HTTP redirections system is disabled to ensure users will
261
     * see the redirection screen.
262
     *
263
     * @param  array $options
264
     * @return bool
265
     */
266
    public function xoInit($options = array())
0 ignored issues
show
Unused Code introduced by
The parameter $options is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
267
    {
268
        /** @var XoopsConfigHandler $configHandler */
269
        $configHandler = xoops_getHandler('config');
270
271
        $this->path                   = XOOPS_THEME_PATH . '/' . $this->folderName;
272
        $this->url                    = XOOPS_THEME_URL . '/' . $this->folderName;
273
        $this->template               = null;
274
        $this->template               = new XoopsTpl();
275
        $this->template->currentTheme = $this;
0 ignored issues
show
Bug introduced by
The property currentTheme does not seem to exist in XoopsTpl.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
276
        $this->template->assign_by_ref('xoTheme', $this);
277
        $xoops_page = str_replace(realpath(XOOPS_ROOT_PATH) . '/', '', realpath($_SERVER['SCRIPT_FILENAME']));
278
        if (strpos($xoops_page, 'modules') !== false) {
279
            $xoops_page = str_replace('modules/', '', $xoops_page);
280
        }
281
        $xoops_page = str_replace('.php', '', $xoops_page);
282
        if (isset($GLOBALS['xoopsConfig']['startpage'])) {
283
            $xoops_startpage = $GLOBALS['xoopsConfig']['startpage'];
284
            if ($xoops_startpage == '--') {
285
                $xoops_startpage = 'system';
286
            }
287
        } else {
288
            $xoops_startpage = 'system';
289
        }
290
291
        $searchConfig = $configHandler->getConfigsByCat(XOOPS_CONF_SEARCH);
292
        $xoops_search = (bool) (isset($searchConfig['enable_search']) && $searchConfig['enable_search'] === 1);
293
        $this->template->assign(array(
294
            'xoops_theme'      => $GLOBALS['xoopsConfig']['theme_set'],
295
            'xoops_imageurl'   => XOOPS_THEME_URL . '/' . $GLOBALS['xoopsConfig']['theme_set'] . '/',
296
            'xoops_themecss'   => xoops_getcss($GLOBALS['xoopsConfig']['theme_set']),
297
            'xoops_requesturi' => htmlspecialchars($_SERVER['REQUEST_URI'], ENT_QUOTES),
298
            'xoops_sitename'   => htmlspecialchars($GLOBALS['xoopsConfig']['sitename'], ENT_QUOTES),
299
            'xoops_slogan'     => htmlspecialchars($GLOBALS['xoopsConfig']['slogan'], ENT_QUOTES),
300
            'xoops_dirname'    => isset($GLOBALS['xoopsModule']) && is_object($GLOBALS['xoopsModule'])
301
                ? $GLOBALS['xoopsModule']->getVar('dirname') : 'system',
302
            'xoops_page'       => $xoops_page,
303
            'xoops_startpage'  => $xoops_startpage,
304
            'xoops_banner'     => ($GLOBALS['xoopsConfig']['banners'] && $this->renderBanner)
305
                ? xoops_getbanner() : '&nbsp;',
306
            'xoops_pagetitle'  => isset($GLOBALS['xoopsModule']) && is_object($GLOBALS['xoopsModule'])
307
                ? $GLOBALS['xoopsModule']->getVar('name')
308
                : htmlspecialchars($GLOBALS['xoopsConfig']['slogan'], ENT_QUOTES),
309
            'xoops_search'     => $xoops_search,
310
        ));
311
        if (isset($GLOBALS['xoopsUser']) && is_object($GLOBALS['xoopsUser'])) {
312
            $this->template->assign(array(
313
                'xoops_isuser'     => true,
314
                'xoops_avatar'     => XOOPS_UPLOAD_URL . '/' . $GLOBALS['xoopsUser']->getVar('user_avatar'),
315
                'xoops_userid'     => $GLOBALS['xoopsUser']->getVar('uid'),
316
                'xoops_uname'      => $GLOBALS['xoopsUser']->getVar('uname'),
317
                'xoops_name'       => $GLOBALS['xoopsUser']->getVar('name'),
318
                'xoops_isadmin'    => $GLOBALS['xoopsUserIsAdmin'],
319
                'xoops_usergroups' => $GLOBALS['xoopsUser']->getGroups(),
320
            ));
321
        } else {
322
            $this->template->assign(array(
323
                'xoops_isuser'     => false,
324
                'xoops_isadmin'    => false,
325
                'xoops_usergroups' => array(XOOPS_GROUP_ANONYMOUS),
326
            ));
327
        }
328
329
        // Meta tags
330
        $criteria       = new CriteriaCompo(new Criteria('conf_modid', 0));
331
        $criteria->add(new Criteria('conf_catid', XOOPS_CONF_METAFOOTER));
332
        $config = $configHandler->getConfigs($criteria, true);
333
        foreach (array_keys($config) as $i) {
334
            $name  = $config[$i]->getVar('conf_name', 'n');
335
            $value = $config[$i]->getVar('conf_value', 'n');
336
            if (substr($name, 0, 5) === 'meta_') {
337
                $this->addMeta('meta', substr($name, 5), $value);
338
            } else {
339
                // prefix each tag with 'xoops_'
340
                $this->template->assign("xoops_$name", $value);
341
            }
342
        }
343
        // Load global javascript
344
        $this->addScript('include/xoops.js');
345
        $this->loadLocalization();
346
347
        if ($this->bufferOutput) {
348
            ob_start();
349
        }
350
        $GLOBALS['xoTheme']  =& $this;
351
        $GLOBALS['xoopsTpl'] =& $this->template;
352
        // Instanciate and initialize all the theme plugins
353
        foreach ($this->plugins as $k => $bundleId) {
354
            if (!is_object($bundleId)) {
355
                $this->plugins[$bundleId]        = null;
356
                $this->plugins[$bundleId]        = new $bundleId();
357
                $this->plugins[$bundleId]->theme =& $this;
358
                $this->plugins[$bundleId]->xoInit();
359
                unset($this->plugins[$k]);
360
            }
361
        }
362
363
        return true;
364
    }
365
366
    /**
367
     * Generate cache id based on extra information of language and user groups
368
     *
369
     * User groups other than anonymous should be detected to avoid disclosing group sensitive contents
370
     *
371
     * @param  string $cache_id    raw cache id
372
     * @param  string $extraString extra string
373
     * @return string complete cache id
374
     */
375
    public function generateCacheId($cache_id, $extraString = '')
376
    {
377
        static $extra_string;
378
        if (!$this->use_extra_cache_id) {
379
            return $cache_id;
380
        }
381
382
        if (empty($extraString)) {
383
            if (empty($extra_string)) {
384
                // Generate language section
385
                $extra_string = $GLOBALS['xoopsConfig']['language'];
386
                // Generate group section
387
                if (!isset($GLOBALS['xoopsUser']) || !is_object($GLOBALS['xoopsUser'])) {
388
                    $extra_string .= '-' . XOOPS_GROUP_ANONYMOUS;
389
                } else {
390
                    $groups = $GLOBALS['xoopsUser']->getGroups();
391
                    sort($groups);
392
                    // Generate group string for non-anonymous groups,
393
                    // XOOPS_DB_PASS and XOOPS_DB_NAME (before we find better variables) are used to protect group sensitive contents
394
                    $extra_string .= '-' . substr(md5(implode('-', $groups)), 0, 8) . '-' . substr(md5(XOOPS_DB_PASS . XOOPS_DB_NAME . XOOPS_DB_USER), 0, 8);
395
                }
396
            }
397
            $extraString = $extra_string;
398
        }
399
        $cache_id .= '-' . $extraString;
400
401
        return $cache_id;
402
    }
403
404
    /**
405
     * xos_opal_Theme::checkCache()
406
     *
407
     * @return bool
408
     */
409
    public function checkCache()
410
    {
411
        if ($_SERVER['REQUEST_METHOD'] !== 'POST' && $this->contentCacheLifetime) {
412
            $template                       = $this->contentTemplate ?: 'db:system_dummy.tpl';
413
            $this->template->caching        = 2;
414
            $this->template->cache_lifetime = $this->contentCacheLifetime;
415
            $uri                            = str_replace(XOOPS_URL, '', $_SERVER['REQUEST_URI']);
416
            // Clean uri by removing session id
417
            if (defined('SID') && SID && strpos($uri, SID)) {
418
                $uri = preg_replace("/([\?&])(" . SID . "$|" . SID . '&)/', "\\1", $uri);
419
            }
420
            $this->contentCacheId = $this->generateCacheId('page_' . substr(md5($uri), 0, 8));
421
            if ($this->template->is_cached($template, $this->contentCacheId)) {
422
                $xoopsLogger = XoopsLogger::getInstance();
423
                $xoopsLogger->addExtra($template, sprintf('Cached (regenerates every %d seconds)', $this->contentCacheLifetime));
424
                $this->render(null, null, $template);
425
426
                return true;
427
            }
428
        }
429
430
        return false;
431
    }
432
433
    /**
434
     * Render the page
435
     *
436
     * The theme engine builds pages from 2 templates: canvas and content.
437
     *
438
     * A module can call this method directly and specify what templates the theme engine must use.
439
     * If render() hasn't been called before, the theme defaults will be used for the canvas and
440
     * page template (and xoopsOption['template_main'] for the content).
441
     *
442
     * @param string $canvasTpl  The canvas template, if different from the theme default
443
     * @param string $pageTpl    The page template, if different from the theme default (unsupported, 2.3+ only)
444
     * @param string $contentTpl The content template
445
     * @param array  $vars       Template variables to send to the template engine
446
     *
447
     * @return bool
448
     */
449
    public function render($canvasTpl = null, $pageTpl = null, $contentTpl = null, $vars = array())
0 ignored issues
show
Unused Code introduced by
The parameter $pageTpl is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
450
    {
451
        if ($this->renderCount) {
452
            return false;
453
        }
454
        $xoopsLogger = XoopsLogger::getInstance();
455
        $xoopsLogger->startTime('Page rendering');
456
457
        xoops_load('xoopscache');
458
        $cache = XoopsCache::getInstance();
459
460
        //Get meta information for cached pages
461
        if ($this->contentCacheLifetime && $this->contentCacheId && $content = $cache->read($this->contentCacheId)) {
462
            //we need to merge metas set by blocks ) with the module cached meta
463
            $this->htmlHeadStrings = array_merge($this->htmlHeadStrings, $content['htmlHeadStrings']);
464
            foreach ($content['metas'] as $type => $value) {
465
                $this->metas[$type] = array_merge($this->metas[$type], $content['metas'][$type]);
466
            }
467
            $GLOBALS['xoopsOption']['xoops_pagetitle']     = $content['xoops_pagetitle'];
468
            $GLOBALS['xoopsOption']['xoops_module_header'] = $content['header'];
469
        }
470
471
        if (!empty($GLOBALS['xoopsOption']['xoops_pagetitle'])) {
472
            $this->template->assign('xoops_pagetitle', $GLOBALS['xoopsOption']['xoops_pagetitle']);
473
        }
474
        $header = empty($GLOBALS['xoopsOption']['xoops_module_header']) ? $this->template->get_template_vars('xoops_module_header') : $GLOBALS['xoopsOption']['xoops_module_header'];
475
476
        //save meta information of cached pages
477
        if ($this->contentCacheLifetime && $this->contentCacheId && !$contentTpl) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $contentTpl of type string|null is loosely compared to false; this is ambiguous if the string can be empty. You might want to explicitly use === null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
478
            $content['htmlHeadStrings'] = $this->htmlHeadStrings;
0 ignored issues
show
Bug introduced by
The variable $content does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
479
            $content['metas']           = $this->metas;
480
            $content['xoops_pagetitle'] =& $this->template->get_template_vars('xoops_pagetitle');
481
            $content['header']          = $header;
482
            $cache->write($this->contentCacheId, $content);
483
        }
484
485
        //  @internal : Lame fix to ensure the metas specified in the xoops config page don't appear twice
486
        $old = array(
487
            'robots',
488
            'keywords',
489
            'description',
490
            'rating',
491
            'author',
492
            'copyright');
493
        foreach ($this->metas['meta'] as $name => $value) {
494
            if (in_array($name, $old)) {
495
                $this->template->assign("xoops_meta_$name", htmlspecialchars($value, ENT_QUOTES));
496
                unset($this->metas['meta'][$name]);
497
            }
498
        }
499
500
        // We assume no overlap between $GLOBALS['xoopsOption']['xoops_module_header'] and $this->template->get_template_vars( 'xoops_module_header' ) ?
501
        $this->template->assign('xoops_module_header', $this->renderMetas(null, true) . "\n" . $header);
502
503
        if ($canvasTpl) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $canvasTpl of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
504
            $this->canvasTemplate = $canvasTpl;
505
        }
506
        if ($contentTpl) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $contentTpl of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
507
            $this->contentTemplate = $contentTpl;
508
        }
509
        if (!empty($vars)) {
510
            $this->template->assign($vars);
511
        }
512
        if ($this->contentTemplate) {
513
            $this->content = $this->template->fetch($this->contentTemplate, $this->contentCacheId);
514
        }
515
        if ($this->bufferOutput) {
516
            $this->content .= ob_get_contents();
517
            ob_end_clean();
518
        }
519
520
        $this->template->assign_by_ref('xoops_contents', $this->content);
521
522
        // Do not cache the main (theme.html) template output
523
        $this->template->caching = 0;
524
        //mb -------------------------
525
//        $this->template->display($this->path . '/' . $this->canvasTemplate);
526
        if (file_exists($this->path . '/' . $this->canvasTemplate)) {
527
            $this->template->display($this->path . '/' . $this->canvasTemplate);
528
        } else {
529
            $this->template->display($this->path . '/theme.tpl');
530
        }
531
        //mb -------------------------
532
        $this->renderCount++;
533
        $xoopsLogger->stopTime('Page rendering');
534
535
        return true;
536
    }
537
538
    /**
539
     * Load localization information
540
     *
541
     * Folder structure for localization:
542
     * <ul>themes/themefolder/english
543
     *     <li>main.php - language definitions</li>
544
     *     <li>style.css - localization stylesheet</li>
545
     *     <li>script.js - localization script</li>
546
     * </ul>
547
     * @param  string $type
548
     * @return bool
549
     */
550
    public function loadLocalization($type = 'main')
551
    {
552
        $language = $GLOBALS['xoopsConfig']['language'];
553
        // Load global localization stylesheet if available
554
        if (file_exists($GLOBALS['xoops']->path('language/' . $language . '/style.css'))) {
555
            $this->addStylesheet($GLOBALS['xoops']->url('language/' . $language . '/style.css'));
556
        }
557
        $this->addLanguage($type, $language);
558
        // Load theme localization stylesheet and scripts if available
559 View Code Duplication
        if (file_exists($this->path . '/language/' . $language . '/script.js')) {
560
            $this->addScript($this->url . '/language/' . $language . '/script.js');
561
        }
562 View Code Duplication
        if (file_exists($this->path . '/language/' . $language . '/style.css')) {
563
            $this->addStylesheet($this->url . '/language/' . $language . '/style.css');
564
        }
565
566
        return true;
567
    }
568
569
    /**
570
     * Load theme specific language constants
571
     *
572
     * @param string $type     language type, like 'main', 'admin'; Needs to be declared in theme xo-info.php
573
     * @param string $language specific language
574
     *
575
     * @return bool|mixed
576
     */
577
    public function addLanguage($type = 'main', $language = null)
578
    {
579
        $language = (null === $language) ? $GLOBALS['xoopsConfig']['language'] : $language;
580
        if (!file_exists($fileinc = $this->path . "/language/{$language}/{$type}.php")) {
581
            if (!file_exists($fileinc = $this->path . "/language/english/{$type}.php")) {
582
                return false;
583
            }
584
        }
585
        $ret = include_once $fileinc;
586
587
        return $ret;
588
    }
589
590
    /**
591
     * *#@+
592
     *
593
     * @tasktype 20 Manipulating page meta-information
594
     */
595
    /**
596
     * Adds script code to the document head
597
     *
598
     * This methods allows the insertion of an external script file (if $src is provided), or
599
     * of a script snippet. The file URI is parsed to take benefit of the theme resource
600
     * overloading system.
601
     *
602
     * The $attributes parameter allows you to specify the attributes that will be added to the
603
     * inserted <script> tag. If unspecified, the <var>type</var> attribute value will default to
604
     * 'text/javascript'.
605
     *
606
     * <code>
607
     * // Add an external script using a physical path
608
     * $theme->addScript( 'www/script.js', null, '' );
609
     * $theme->addScript( 'modules/newbb/script.js', null, '' );
610
     * // Specify attributes for the <script> tag
611
     * $theme->addScript( 'mod_xoops_SiteManager#common.js', array( 'type' => 'application/x-javascript' ), '', 'mod_xoops_Sitemanager' );
612
     * // Insert a code snippet
613
     * $theme->addScript( null, array( 'type' => 'application/x-javascript' ), 'window.open("Hello world");', 'hello' );
614
     * </code>
615
     *
616
     * @param  string $src        path to an external script file
617
     * @param  array  $attributes hash of attributes to add to the <script> tag
618
     * @param  string $content    Code snippet to output within the <script> tag
619
     * @param  string $name       Element Name in array scripts are stored in.
620
     * @return void
621
     */
622 View Code Duplication
    public function addScript($src = '', $attributes = array(), $content = '', $name = '')
623
    {
624
        if (empty($attributes)) {
625
            $attributes = array();
626
        }
627
        if (!empty($src)) {
628
            $src               = $GLOBALS['xoops']->url($this->resourcePath($src));
629
            $attributes['src'] = $src;
630
        }
631
        if (!empty($content)) {
632
            $attributes['_'] = $content;
633
        }
634
        if (!isset($attributes['type'])) {
635
            $attributes['type'] = 'text/javascript';
636
        }
637
        if (empty($name)) {
638
            $name = md5(serialize($attributes));
639
        }
640
        $this->addMeta('script', $name, $attributes);
641
    }
642
643
    /**
644
     * Add StyleSheet or CSS code to the document head
645
     *
646
     * @param  string $src        path to .css file
647
     * @param  array  $attributes name => value paired array of attributes such as title
648
     * @param  string $content    CSS code to output between the <style> tags (in case $src is empty)
649
     * @param  string $name       Element Name in array stylesheets are stored in.
650
     * @return void
651
     */
652 View Code Duplication
    public function addStylesheet($src = '', $attributes = array(), $content = '', $name = '')
653
    {
654
        if (empty($attributes)) {
655
            $attributes = array();
656
        }
657
        if (!empty($src)) {
658
            $src                = $GLOBALS['xoops']->url($this->resourcePath($src));
659
            $attributes['href'] = $src;
660
        }
661
        if (!isset($attributes['type'])) {
662
            $attributes['type'] = 'text/css';
663
        }
664
        if (!empty($content)) {
665
            $attributes['_'] = $content;
666
        }
667
        if (empty($name)) {
668
            $name = md5(serialize($attributes));
669
        }
670
        $this->addMeta('stylesheet', $name, $attributes);
671
    }
672
673
    /**
674
     * Add a <link> to the header
675
     *
676
     * @param string $rel        Relationship from the current doc to the anchored one
677
     * @param string $href       URI of the anchored document
678
     * @param array  $attributes Additional attributes to add to the <link> element
679
     * @param string $name       Element Name in array links are stored in.
680
     */
681
    public function addLink($rel, $href = '', $attributes = array(), $name = '')
682
    {
683
        if (empty($attributes)) {
684
            $attributes = array();
685
        }
686
        if (!empty($href)) {
687
            $attributes['href'] = $href;
688
        }
689
        $attributes['rel'] = $rel;
690
        if (empty($name)) {
691
            $name = md5(serialize($attributes));
692
        }
693
        $this->addMeta('link', $name, $attributes);
694
    }
695
696
    /**
697
     * Set a meta http-equiv value
698
     * @param         $name
699
     * @param  null   $value
700
     * @return string
0 ignored issues
show
Documentation introduced by
Should the return type not be string|null?

This check compares the return type specified in the @return annotation of a function or method doc comment with the types returned by the function and raises an issue if they mismatch.

Loading history...
701
     */
702
    public function addHttpMeta($name, $value = null)
703
    {
704
        if (isset($value)) {
705
            return $this->addMeta('http', $name, $value);
706
        }
707
        unset($this->metas['http'][$name]);
708
        return null;
709
    }
710
711
    /**
712
     * Change output page meta-information
713
     * @param  string $type
714
     * @param  string $name
715
     * @param  string $value
716
     * @return string
717
     */
718
    public function addMeta($type = 'meta', $name = '', $value = '')
719
    {
720
        if (!isset($this->metas[$type])) {
721
            $this->metas[$type] = array();
722
        }
723
        if (!empty($name)) {
724
            $this->metas[$type][$name] = $value;
725
        } else {
726
            $this->metas[$type][md5(serialize(array($value)))] = $value;
727
        }
728
729
        return $value;
730
    }
731
732
    /**
733
     * xos_opal_Theme::headContent()
734
     *
735
     * @param mixed $params
736
     * @param mixed $content
737
     * @param mixed $smarty
738
     * @param mixed $repeat
739
     *
740
     * @return void
741
     */
742
    public function headContent($params, $content, &$smarty, &$repeat)
0 ignored issues
show
Unused Code introduced by
The parameter $params is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $smarty is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
743
    {
744
        if (!$repeat) {
745
            $this->htmlHeadStrings[] = $content;
746
        }
747
    }
748
749
    /**
750
     * xos_opal_Theme::renderMetas()
751
     *
752
     * @param  mixed $type
753
     * @param  mixed $return
754
     * @return bool|string
755
     */
756
    public function renderMetas($type = null, $return = false)
757
    {
758
        $str = '';
759
        if (!isset($type)) {
760
            foreach (array_keys($this->metas) as $type) {
761
                $str .= $this->renderMetas($type, true);
762
            }
763
            $str .= implode("\n", $this->htmlHeadStrings);
764
        } else {
765
            switch ($type) {
766 View Code Duplication
                case 'script':
767
                    foreach ($this->metas[$type] as $attrs) {
768
                        $str .= '<script' . $this->renderAttributes($attrs) . '>';
769
                        if (@$attrs['_']) {
770
                            $str .= "\n//<![CDATA[\n" . $attrs['_'] . "\n//]]>";
771
                        }
772
                        $str .= "</script>\n";
773
                    }
774
                    break;
775
                case 'link':
776
                    foreach ($this->metas[$type] as $attrs) {
777
                        $rel = $attrs['rel'];
778
                        unset($attrs['rel']);
779
                        $str .= '<link rel="' . $rel . '"' . $this->renderAttributes($attrs) . " />\n";
780
                    }
781
                    break;
782 View Code Duplication
                case 'stylesheet':
783
                    foreach ($this->metas[$type] as $attrs) {
784
                        if (@$attrs['_']) {
785
                            $str .= '<style' . $this->renderAttributes($attrs) . ">\n/* <![CDATA[ */\n" . $attrs['_'] . "\n/* //]]> */\n</style>";
786
                        } else {
787
                            $str .= '<link rel="stylesheet"' . $this->renderAttributes($attrs) . " />\n";
788
                        }
789
                    }
790
                    break;
791 View Code Duplication
                case 'http':
792
                    foreach ($this->metas[$type] as $name => $content) {
793
                        $str .= '<meta http-equiv="' . htmlspecialchars($name, ENT_QUOTES) . '" content="' . htmlspecialchars($content, ENT_QUOTES) . "\" />\n";
794
                    }
795
                    break;
796 View Code Duplication
                default:
797
                    foreach ($this->metas[$type] as $name => $content) {
798
                        $str .= '<meta name="' . htmlspecialchars($name, ENT_QUOTES) . '" content="' . htmlspecialchars($content, ENT_QUOTES) . "\" />\n";
799
                    }
800
                    break;
801
            }
802
        }
803
        if ($return) {
804
            return $str;
805
        }
806
        echo $str;
807
808
        return true;
809
    }
810
811
    /**
812
     * Generates a unique element ID
813
     *
814
     * @param  string $tagName
815
     * @return string
816
     */
817
    public function genElementId($tagName = 'xos')
818
    {
819
        static $cache = array();
820
        if (!isset($cache[$tagName])) {
821
            $cache[$tagName] = 1;
822
        }
823
824
        return $tagName . '-' . $cache[$tagName]++;
825
    }
826
827
    /**
828
     * Transform an attributes collection to an XML string
829
     *
830
     * @param  array $coll
831
     * @return string
832
     */
833
    public function renderAttributes($coll)
834
    {
835
        $str = '';
836
        foreach ($coll as $name => $val) {
837
            if ($name !== '_') {
838
                $str .= ' ' . $name . '="' . htmlspecialchars($val, ENT_QUOTES) . '"';
839
            }
840
        }
841
842
        return $str;
843
    }
844
845
    /**
846
     * Return a themable file resource path
847
     *
848
     * @param  string $path
849
     * @return string
850
     */
851
    public function resourcePath($path)
852
    {
853
        if (substr($path, 0, 1) === '/') {
854
            $path = substr($path, 1);
855
        }
856
857
        if (file_exists(XOOPS_ROOT_PATH . "/{$this->themesPath}/{$this->folderName}/{$path}")) {
858
            return "{$this->themesPath}/{$this->folderName}/{$path}";
859
        }
860
861
        if (file_exists(XOOPS_ROOT_PATH . "/themes/{$this->folderName}/{$path}")) {
862
            return "themes/{$this->folderName}/{$path}";
863
        }
864
865
        return $path;
866
    }
867
}
868