Passed
Push — master ( 380a00...3979db )
by Terrence
12:18
created

Skin::hasGreenlitIdps()   A

Complexity

Conditions 6
Paths 6

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 42

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 6
eloc 9
nc 6
nop 0
dl 0
loc 16
ccs 0
cts 10
cp 0
crap 42
rs 9.2222
c 1
b 0
f 0
1
<?php
2
3
namespace CILogon\Service;
4
5
use CILogon\Service\Util;
6
use tubalmartin\CssMin\Minifier as CSSmin;
7
use PEAR;
8
use DB;
9
10
/**
11
 * Skin
12
 *
13
 * This class reads in CSS and configuration options
14
 * for a 'skin'. The skin is set by passing the
15
 * '?skin=...' (or '?cilogon_skin=...') query parameter.
16
 * If found, the associated config XML and CSS are read
17
 * in from either the filesystem (under the /skin/NAME
18
 * directory) or the database. It also sets a PHP
19
 * session variable so that the skin name is remembered
20
 * across page loads. If no skin name is found, then the
21
 * default /skin/config.xml file is read in.
22
 *
23
 * Note that this class uses the SimpleXML class to parse
24
 * the config XML. This stores the XML in a special
25
 * SimpleXMLElement object, which is NOT an array. But
26
 * you can iterate over elements in the structure. See
27
 * the PHP SimpleXML online manual 'Basic Usage' for
28
 * more information.
29
 *
30
 * This class provides a getConfigOption() method to
31
 * access XML (sub)blocks to get at a config value.
32
 * It is important to rememeber that the values returned
33
 * by the getConfigOption() method must be typecast to
34
 * native datatypes in order to be used effectively.
35
 *
36
 * An example configuration file (with all available options) is at
37
 *     /var/www/html/skin/config-example.xml
38
 *
39
 * Example usage:
40
 *   require_once 'Skin.php';
41
 *   $skin = new Skin();
42
 *   // While outputting the <head> HTML block...
43
 *   $skin->printSkinCSS();
44
 *   // Get the value of a configuration option
45
 *   $idpgreenlit = $skin->getConfigOption('idpgreenlit');!!
46
 *   // Now, process entries in the $idpgreenlit list
47
 *   if ((!is_null($idpgreenlit)) && (!empty($idpgreenlit->idp))) {
48
 *       foreach ($idpgreenlit->idp as $entityID) {
49
 *           echo '<p>' , (string)$entityID , "<\p>\n";
50
 *       }
51
 *   }
52
 *   // Check to see if <hideportalinfo> is set
53
 *   $hideportalinfo = false;
54
 *   $hpi=$skin->getConfigOption('portallistaction','hideportalinfo');
55
 *   if ((!is_null($hpi)) && ((int)$hpi > 0)) {
56
 *       $hideportalinfo = true;
57
 *   }
58
 */
59
class Skin
60
{
61
    /**
62
     * @var string $skinname The name of the skin
63
     */
64
    protected $skinname;
65
66
    /**
67
     * @var \SimpleXMLElement $configxml A SimpleXMLElement object for the
68
     *      config.xml file
69
     */
70
    protected $configxml;
71
72
    /**
73
     * @var string $css The un-minified CSS for the skin
74
     */
75
    protected $css;
76
77
    /**
78
     * @var array $forcearray An array of (URI,skinname) pairs for forcing
79
     *      skin application
80
     */
81
    protected $forcearray;
82
83
    /**
84
     *  __construct
85
     *
86
     * Default constructor. Calls init() to do the actual work.
87
     *
88
     * @return Skin A new Skin object.
89
     */
90
    public function __construct()
91
    {
92
        $this->init();
93
    }
94
95
    /**
96
     * init
97
     *
98
     * This function does the work of (re)initializing the skin object.
99
     * It finds the name of the skin (if any) and reads in the skin's
100
     * config XML file (if found). Call this function to reset the
101
     * skin in case of possible forced skin due to IdP or portal
102
     * callbackURL.
103
     *
104
     * @param bool $reset True to reset the 'cilogon_skin' PHP session var
105
     *        to blank so that readSkinConfig doesn't check for it.
106
     *        Defaults to 'false'.
107
     */
108
    public function init($reset = false)
109
    {
110
        if ($reset) {
111
            Util::unsetSessionVar('cilogon_skin');
112
        }
113
114
        $this->skinname = '';
115
        $this->configxml = null;
116
        $this->css = '';
117
        $this->forcearray = FORCE_SKIN_ARRAY;
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\FORCE_SKIN_ARRAY was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
118
        $this->readSkinConfig();
119
        $this->setMyProxyInfo();
120
    }
121
122
    /**
123
     * readSkinConfig
124
     *
125
     * This function checks for the name of the skin in several places:
126
     * (1) The FORCE_SKIN_ARRAY for a matching IdP entityID or portal
127
     * callbackURL, (2) in a URL parameter (can be '?skin=',
128
     * '?cilogon_skin=', '?vo='), (3) cilogon_vo form input variable
129
     * (POST for ECP case), or (4) 'cilogon_skin' PHP session
130
     * variable.  If it finds the skin name in any of these, it then
131
     * checks to see if such a named skin exists, either on the filesystem
132
     * or in the database. If found, the class variable $skinname AND the
133
     * 'cilogon_skin' PHP session variable (for use on future page
134
     * loads by the user) are set. It then reads in the config XML and the
135
     * CSS and stores them in the class variables $configxml and $css.
136
     * If no skin name is found, read in the default /skin/config.xml file.
137
     *
138
     * Side Effect: Sets the 'cilogon_skin' session variable if needed.
139
     */
140
    protected function readSkinConfig()
141
    {
142
        $skinvar = '';
143
144
        // Check for matching IdP, callbackURI (OAuth1),
145
        // redirect_uri (OAuth2), or client_id (OAuth2)
146
        // in the FORCE_SKIN_ARRAY.
147
        $uristocheck = array(
148
            Util::getGetVar('redirect_uri'),
149
            Util::getGetVar('client_id'),
150
            Util::getSessionVar('callbackuri'),
151
            Util::getSessionVar('idp')
152
        );
153
154
        foreach ($uristocheck as $value) {
155
            if (strlen($value) > 0) {
156
                $skin = $this->getForceSkin($value);
157
                if (strlen($skin) > 0) {
158
                    $skinvar = $skin;
159
                    break;
160
                }
161
            }
162
        }
163
164
        // If no force skin found, check GET and POST parameters, as well as
165
        // previously set cilogon_skin PHP session variable.
166
        if (strlen($skinvar) == 0) {
167
            // First, look for '?skin=...'
168
            $skinvar = Util::getGetVar('skin');
169
        }
170
        if (strlen($skinvar) == 0) {
171
            // Next, look for '?cilogon_skin=...'
172
            $skinvar = Util::getGetVar('cilogon_skin');
173
        }
174
        if (strlen($skinvar) == 0) {
175
            // Next, look for '?vo=...'
176
            $skinvar = Util::getGetVar('vo');
177
        }
178
        if (strlen($skinvar) == 0) {
179
            // Next, check 'cilogon_vo' form input variable
180
            $skinvar = Util::getPostVar('cilogon_vo');
181
        }
182
        if (strlen($skinvar) == 0) {
183
            // Finally, check 'cilogon_skin' PHP session variable
184
            $skinvar = Util::getSessionVar('cilogon_skin');
185
        }
186
187
        // If we found $skinvar, attempt to read the skin config/css from
188
        // either the filesystem or the database, or failing that, read the
189
        // default /skin/config.xml file.
190
        $skinvar = strtolower($skinvar); // All skin dirs are lowercase
191
        $this->readSkinFromFile($skinvar) ||
192
            $this->readSkinFromDatabase($skinvar) ||
193
            $this->readDefaultSkin();
194
    }
195
196
    /**
197
     * readSkinFromFile
198
     *
199
     * This function reads in the skin config XML and CSS from the
200
     * filesystem into the class variables $configxml and $css.
201
     *
202
     * @param string $skinvar The name of the skin as found by
203
     *        readSkinConfig().
204
     * @return bool True if at least one of config.xml or skin.css could
205
     *         be found (and read in) for the skin (i.e., the skin
206
     *         directory exists and isn't empty). False otherwise.
207
     */
208
    protected function readSkinFromFile($skinvar)
209
    {
210
        $readin = false; // Make sure we read in either XML or CSS (or both)
211
212
        if (strlen($skinvar) > 0) {
213
            $skindir = Util::getServerVar('DOCUMENT_ROOT') . "/skin/$skinvar";
214
            if (is_dir($skindir)) {
215
                // Read in the config XML
216
                $skinconf = $skindir . '/config.xml';
217
                if (is_readable($skinconf)) {
218
                    if (($xml = @simplexml_load_file($skinconf)) !== false) {
219
                        $this->configxml = $xml;
220
                        $readin = true;
221
                    }
222
                }
223
                //Read in the CSS
224
                $skincss = $skindir . '/skin.css';
225
                if (is_readable($skincss)) {
226
                    if (($css = file_get_contents($skincss)) !== false) {
227
                        $this->css = $css;
228
                        $readin = true;
229
                    }
230
                }
231
            }
232
        }
233
234
        if ($readin) {
235
            $this->skinname = $skinvar;
236
            Util::setSessionVar('cilogon_skin', $skinvar);
237
        } else {
238
            Util::unsetSessionVar('cilogon_skin');
239
        }
240
241
        return $readin;
242
    }
243
244
    /**
245
     * readSkinFromDatabase
246
     *
247
     * This function reads in the skin config XML and CSS from the
248
     * database into the class variables $configxml and $css.
249
     *
250
     * @param string $skinvar The name of the skin as found by
251
     *        readSkinConfig().
252
     * @return bool True if at least one of config XML or CSS could
253
     *         be read from the database for the skin. False otherwise.
254
     */
255
    protected function readSkinFromDatabase($skinvar)
256
    {
257
        $readin = false; // Make sure we read in either XML or CSS (or both)
258
259
        if (strlen($skinvar) > 0) {
260
            $dsn = array(
261
                'phptype'  => 'mysqli',
262
                'username' => MYSQLI_USERNAME,
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\MYSQLI_USERNAME was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
263
                'password' => MYSQLI_PASSWORD,
0 ignored issues
show
Bug introduced by
The constant CILogon\Service\MYSQLI_PASSWORD was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
264
                'database' => 'ciloa2',
265
                'hostspec' => 'localhost'
266
            );
267
268
            $opts = array(
269
                'persistent'  => true,
270
                'portability' => DB_PORTABILITY_ALL
271
            );
272
273
            $db = DB::connect($dsn, $opts);
274
            if (!PEAR::isError($db)) {
275
                $data = $db->getRow(
276
                    'SELECT * from skins WHERE name = ?',
277
                    array($skinvar),
278
                    DB_FETCHMODE_ASSOC
279
                );
280
                if ((!DB::isError($data)) && (!empty($data))) {
281
                    // Read in the config XML
282
                    if (
283
                        (strlen(@$data['config']) > 0) &&
284
                        (($xml = @simplexml_load_string($data['config'])) !== false)
285
                    ) {
286
                        $this->configxml = $xml;
287
                        $readin = true;
288
                    }
289
                    //Read in the CSS
290
                    if (strlen(@$data['css']) > 0) {
291
                        $this->css = $data['css'];
292
                        $readin = true;
293
                    }
294
                }
295
                $db->disconnect();
296
            }
297
        }
298
299
        if ($readin) {
300
            $this->skinname = $skinvar;
301
            Util::setSessionVar('cilogon_skin', $skinvar);
302
        } else {
303
            Util::unsetSessionVar('cilogon_skin');
304
        }
305
306
        return $readin;
307
    }
308
309
    /**
310
     * readDefaultSkin
311
     *
312
     * If we don't read a skin from the filesystem or the database, then
313
     * read in the default "/skin/config.xml" file.
314
     *
315
     * @return bool True if the default config.xml file was read in.
316
     *         False otherwise.
317
     */
318
    protected function readDefaultSkin()
319
    {
320
        $readin = false;
321
322
        $this->skinname = '';
323
        $this->css = '';
324
        Util::unsetSessionVar('cilogon_skin');
325
326
        $skinconf = Util::getServerVar('DOCUMENT_ROOT') . '/skin/config.xml';
327
        if (is_readable($skinconf)) {
328
            if (($xml = @simplexml_load_file($skinconf)) !== false) {
329
                $this->configxml = $xml;
330
                $readin = true;
331
            }
332
        }
333
334
        return $readin;
335
    }
336
337
    /**
338
     * getConfigOption
339
     *
340
     * This method returns a SimpleXMLElement block corresponding to
341
     * the passed in arguments.  For example, to get the redlit list of
342
     * idps, call $idps = getConfigOption('idpredlit') and then
343
     * iterate over $idps with foreach($idps as $idp) { ... }.  To get
344
     * a single subblock value such as the initial lifetime number for
345
     * the PKCS12 download option, call $life =
346
     * (int)getConfigOption('pkcs12','initiallifetime','number'). Note
347
     * that you should explicitly cast the values to int, string,
348
     * float, etc., when you use them.
349
     *
350
     * @param mixed $args Variable number of parameters corresponding to XML
351
     *              blocks (and possible sub-blocks).
352
     * @return \SimpleXMLElement|null A SimpleXMLElement corresponding to the
353
     *         passed-in XML option, or 'null' if no such option exists.
354
     */
355
    public function getConfigOption(...$args)
356
    {
357
        $retval = null;
358
        $numargs = count($args);
359
        if ($numargs > 0) {
360
            $retval = $this->configxml;
361
        }
362
        for ($i = 0; $i < $numargs; $i++) {
363
            $argval = $args[$i];
364
            if (empty($retval->$argval)) {
365
                $retval = null;
366
                break;
367
            } else {
368
                $retval = $retval->$argval;
369
            }
370
        }
371
        return $retval;
372
    }
373
374
    /**
375
     * printSkinCSS
376
     *
377
     * Call this function in the HTML <head> block to print out the
378
     * <style> tag for the internal CSS of the skin. The CSS is minified
379
     * to remove whitespace. Note that you should call readSkinConfig
380
     * to set the contents of $css.
381
     */
382
    public function printSkinCSS()
383
    {
384
        if (strlen($this->css) > 0) {
385
            $cssmin = new CSSmin();
386
            $cssmin->removeImportantComments();
387
            $cssmin->setLineBreakPosition(255);
388
            $outputcss = $cssmin->run($this->css);
389
            echo "<style>$outputcss</style>";
390
        }
391
    }
392
393
    /**
394
     * hasGreenlitIdps
395
     *
396
     * This function checks for the presence of a <idpgreenlit> block
397
     * in the skin's config file.  There must be at least one <idp>
398
     * in the <idpgreenlit>.
399
     *
400
     * @return bool True if skin has a non-empty <idpgreenlit>.
401
     */
402
    public function hasGreenlitIdps()
403
    {
404
        $retval = false;  // Assume no <idpgreenlit> configured
405
        $idpgreenlit = $this->getConfigOption('idpgreenlit');
406
        if ((!is_null($idpgreenlit)) && (!empty($idpgreenlit->idp))) {
407
            $retval = true;
408
        }
409
        // REMOVE AFTER DATABASE UPDATE!!! 
410
        // For now, also check for older 'idpwhitelist' option
411
        if (!$retval) {
412
             $idpwhitelist = $this->getConfigOption('idpwhitelist');
413
             if ((!is_null($idpwhitelist)) && (!empty($idpwhitelist->idp))) {
414
                 $retval = true;
415
             }
416
        }
417
        return $retval;
418
    }
419
420
    /**
421
     * hasRedlitIdps
422
     *
423
     * This function checks for the presence of a <idpredlit> block
424
     * in the skin's config file.  There must be at least one <idp>
425
     * in the <idpredlit>.
426
     *
427
     * @return bool True if skin has a non-empty <idpredlit>.
428
     */
429
    public function hasRedlitIdps()
430
    {
431
        $retval = false;  // Assume no <idpredlit> configured
432
        $idpredlit = $this->getConfigOption('idpredlit');
433
        if ((!is_null($idpredlit)) && (!empty($idpredlit->idp))) {
434
            $retval = true;
435
        }
436
        // REMOVE AFTER DATABASE UPDATE!!! 
437
        // For now, also check for older 'idpblacklist' option
438
        if (!$retval) {
439
            $idpblacklist = $this->getConfigOption('idpblacklist');
440
            if ((!is_null($idpblacklist)) && (!empty($idpblacklist->idp))) {
441
                $retval = true;
442
            }
443
444
        }
445
        return $retval;
446
    }
447
448
    /**
449
     * idpGreenlit
450
     *
451
     * This method checks to see if a given entityId of an IdP
452
     * is greenlit. 'Greenlit' in this case means either (a) the
453
     * entityId is in the skin's <idpgreenlit> list or (b) the skin
454
     * doesn't have a <idpgreenlit> at all.  In the second case, all
455
     * IdPs are by default 'greenlit'.  If you want to find if an
456
     * IdP should be listed in the WAYF, use 'idpAvailable' which
457
     * checks the greenlit AND the redlit lists.
458
     *
459
     * @param string $entityId The entityId of an IdP to check for greenlit.
460
     * @return bool True if the given IdP entityId is in the skin's
461
     *         greenlit list (or if the skin doesn't have a greenlit list).
462
     */
463
    public function idpGreenlit($entityId)
464
    {
465
        $retval = true;  // Assume the entityId is 'greenlit'
466
        if ($this->hasGreenlitIdps()) {
467
            $idpgreenlit = $this->getConfigOption('idpgreenlit');
468
            // REMOVE AFTER DATABASE UPDATE!!!
469
            // For now, also check for older 'idpwhitelist' option
470
            if ((is_null($idpgreenlit)) || (empty($idpgreenlit->idp))) {
471
                $idpgreenlit = $this->getConfigOption('idpwhitelist');
472
            }
473
            $found = false;
474
            foreach ($idpgreenlit->idp as $greenidp) {
475
                if ($entityId == ((string)$greenidp)) {
476
                    $found = true;
477
                    break;
478
                }
479
            }
480
            if (!$found) {
481
                $retval = false;
482
            }
483
        }
484
        return $retval;
485
    }
486
487
    /**
488
     * idpRedlit
489
     *
490
     * This method checks to see if a given entityId of an IdP
491
     * appears in the skin's <idpredlit>.
492
     *
493
     * @param string $entityId The entityId of an IdP to check for
494
     *        redlit status.
495
     * @return bool True if the given IdP entityId is in the skin's
496
     *         redlit list.
497
     */
498
    public function idpRedlit($entityId)
499
    {
500
        $retval = false;  // Assume entityId is NOT in the idpredlit
501
        if ($this->hasRedlitIdps()) {
502
            $idpredlit = $this->getConfigOption('idpredlit');
503
            // REMOVE AFTER DATABASE UPDATE!
504
            // For now, also check for older 'idpwhitelist' option
505
            if ((is_null($idpredlit)) || (empty($idpredlit->idp))) {
506
                $idpredlit = $this->getConfigOption('idpblacklist');
507
            }
508
            foreach ($idpredlit->idp as $redidp) {
509
                if ($entityId == ((string)$redidp)) {
510
                    $retval = true;
511
                    break;
512
                }
513
            }
514
        }
515
        return $retval;
516
    }
517
518
    /**
519
     * idpAvailable
520
     *
521
     * This method combines idpGreenlit and idpRedlit to return
522
     * a 'yes/no' for if a given IdP should be made available for
523
     * selection in the WAYF.  It first checks to see if the IdP is
524
     * greenlit.  If not, it returns false. Otherwise, it then
525
     * checks if the IdP is redlit.  If not, it returns true.
526
     *
527
     * @param string $entityId The entityId of an IdP to check to see if it
528
     *        should be available in the WAYF.
529
     * @return bool True if the given IdP entityId is available to be
530
     *         selected in the WAYF.
531
     */
532
    public function idpAvailable($entityId)
533
    {
534
        $retval = false;   // Assume IdP is not available in the WAYF
535
        if (
536
            ($this->idpGreenlit($entityId)) &&
537
            (!$this->idpRedlit($entityId))
538
        ) {
539
            $retval = true;
540
        }
541
        return $retval;
542
    }
543
544
    /**
545
     * setMyProxyInfo
546
     *
547
     * This method sets the 'myproxyinfo' PHP session variable.  The
548
     * variable has the form 'info:key1=value1,key2=value2,...' and is
549
     * passed to the 'myproxy-logon' command as part of the username
550
     * when fetching a credential.  The MyProxy server will do extra
551
     * processing based on the content of this 'info:...' tag.  If the
552
     * skinname is not empty, that is added to the info tag.  Also,
553
     * the apache REMOTE_ADDR is added.  For other key=value pairs that
554
     * get added, see the code below.
555
     */
556
    public function setMyProxyInfo()
557
    {
558
        $infostr = '';
559
560
        // Add the skinname if available
561
        if (strlen($this->skinname) > 0) {
562
            $infostr .= 'cilogon_skin=' . $this->skinname;
563
        }
564
565
        // Add the REMOTE_ADDR
566
        $remoteaddr = Util::getServerVar('REMOTE_ADDR');
567
        if (strlen($remoteaddr) > 0) {
568
            $infostr .= (strlen($infostr) > 0 ? ',' : '') .
569
                        "remote_addr=$remoteaddr";
570
        }
571
572
        // Add eppn, eptid, open_id, and oidc if available
573
        // Note that these values are lowercase after an update to make
574
        // them the same as those used by the dbService. BUT, MyProxy
575
        // expects the old versions. So this array maps the new lowercase
576
        // versions back into the old ones.
577
        $mpid = array(
578
            'eppn' => 'ePPN',
579
            'eptid' => 'ePTID',
580
            'open_id' => 'openidID',
581
            'oidc' => 'oidcID'
582
        );
583
        foreach (array('eppn','eptid','open_id','oidc') as $id) {
584
            $sessvar = Util::getSessionVar($id);
585
            if (strlen($sessvar) > 0) {
586
                $infostr .= (strlen($infostr) > 0 ? ',' : '') .
587
                    $mpid[$id] . "=" . $sessvar;
588
            }
589
        }
590
591
        // Finally, set the 'myproxyinfo' PHP session variable
592
        if (strlen($infostr) > 0) {
593
            Util::setSessionVar('myproxyinfo', 'info:' . $infostr);
594
        } else {
595
            Util::unsetSessionVar('myproxyinfo');
596
        }
597
    }
598
599
    /**
600
     * hasPortalList
601
     *
602
     * This function checks for the presence of a <portallist> block in
603
     * the skin's config file.  There must be at least one <portal> in
604
     * the <portallist>.
605
     *
606
     * @return bool True if skin has a non-empty <portallist>.
607
     */
608
    public function hasPortalList()
609
    {
610
        $retval = false;  // Assume no <portallist> configured
611
        $portallist = $this->getConfigOption('portallist');
612
        if ((!is_null($portallist)) && (!empty($portallist->portal))) {
613
            $retval = true;
614
        }
615
        return $retval;
616
    }
617
618
    /**
619
     * inPortalList
620
     *
621
     * This function takes in a 'callback' URL of a portal passed to
622
     * the CILogon Delegate service.  It then looks through the list
623
     * of <portal> patterns in the skin's <portallist>.  If the
624
     * callback URL matches any of these patterns, true is returned.
625
     * This is used to hide the 'Site Name / Site URL / Service URL'
626
     * box on the delegation WAYF page, for example.
627
     *
628
     * @param string $portalurl A 'callback' URL of a portal accessing the
629
     *        delegate service.
630
     * @return bool True if the callback URL matches one of the patterns
631
     *         in the skin's <portallist>.  False otherwise.
632
     */
633
    public function inPortalList($portalurl)
634
    {
635
        $retval = false;  // Assume the portalurl not a listed <portal>
636
        if ($this->hasPortalList()) {
637
            $portallist = $this->getConfigOption('portallist');
638
            foreach ($portallist->portal as $portalmatch) {
639
                if (preg_match(((string)$portalmatch), $portalurl)) {
640
                    $retval = true;
641
                    break;
642
                }
643
            }
644
        }
645
        return $retval;
646
    }
647
648
    /**
649
     * getForceSkin
650
     *
651
     * The FORCE_SKIN_ARRAY contains 'uripattern'=>'skinname' pairs
652
     * corresponding to IdP entityIDs, portal callbackurls, or client_ids
653
     * that should have a particular skin force-applied. This function
654
     * looks in the FORCE_SKIN_ARRAY for a pattern-matched URI and returns
655
     * the corresponding skin name if found. If not found, empty
656
     * string is returned.
657
     *
658
     * @param string $uri A URI to search for in the FORCE_SKIN_ARRAY.
659
     * @return string The skin name for the URI, or empty string if not
660
     *         found.
661
     */
662
    protected function getForceSkin($uri)
663
    {
664
        $retval = '';  // Assume uri is not in FORCE_SKIN_ARRAY
665
666
        foreach ($this->forcearray as $key => $value) {
667
            if (preg_match($key, $uri)) {
668
                $retval = $value;
669
                break;
670
            }
671
        }
672
        return $retval;
673
    }
674
}
675