Completed
Pull Request — master (#21)
by
unknown
03:19
created

class.FlipPage.php (4 issues)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

1
<?php
2
/**
3
 * A WebPage class specific to this framework
4
 *
5
 * This file describes an abstraction for creating a webpage with JQuery, Bootstrap,
6
 * and other framework specific abilities
7
 *
8
 * PHP version 5 and 7
9
 *
10
 * @author Patrick Boyd / [email protected]
11
 * @copyright Copyright (c) 2015, Austin Artistic Reconstruction
12
 * @license http://www.apache.org/licenses/ Apache 2.0 License
13
 */
14
15
/**
16
 * We need the parent class
17
 */
18
require_once('class.WebPage.php');
19
20
define('JS_JQUERY', 0);
21
define('JS_JQUERY_UI', 1);
22
define('JS_BOOTSTRAP', 2);
23
define('JQUERY_VALIDATE', 3);
24
define('JQUERY_TOUCH', 4);
25
define('JS_TINYNAV', 5);
26
define('JS_BOOTSTRAP_FH', 6);
27
define('JS_BOOTSTRAP_SW', 7);
28
define('JS_DATATABLE', 8);
29
define('JS_CHART', 9);
30
define('JS_METISMENU', 10);
31
define('JS_BOOTBOX', 11);
32
define('JS_DATATABLE_ODATA', 12);
33
define('JS_CRYPTO_MD5_JS', 13);
34
define('JS_JCROP', 14);
35
define('JS_TYPEAHEAD', 15);
36
define('JS_CHEET', 16);
37
define('JS_FLIPSIDE', 20);
38
define('JS_LOGIN', 21);
39
40
define('CSS_JQUERY_UI', 0);
41
define('CSS_BOOTSTRAP', 1);
42
define('CSS_BOOTSTRAP_FH', 2);
43
define('CSS_BOOTSTRAP_SW', 3);
44
define('CSS_DATATABLE', 4);
45
define('CSS_JCROP', 5);
46
define('CSS_FONTAWESOME', 6);
47
48
global $jsArray;
49
$jsArray = array(
50
        JS_JQUERY => array(
51
            'no' => array(
52
                'no'  => '/js/common/jquery.js',
53
                'min' => '/js/common/jquery.min.js'
54
            ),
55
            'cdn' => array(
56
                'no'  => '//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.js',
57
                'min' => '//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js'
58
            )
59
        ),
60
        JS_JQUERY_UI => array(
61
            'no' => array(
62
                'no'  => '/js/common/jquery-ui.js',
63
                'min' => '/js/common/jquery-ui.min.js'
64
            ),
65
            'cdn' => array(
66
                'no'  => '//ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.js',
67
                'min' => '//ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/jquery-ui.min.js'
68
            )
69
        ),
70
        JS_BOOTSTRAP => array(
71
            'no' => array(
72
                'no'  => '/js/common/bootstrap.js',
73
                'min' => '/js/common/bootstrap.min.js'
74
            ),
75
            'cdn' => array(
76
                'no'  => '//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.js',
77
                'min' => '//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js'
78
            )
79
        ),
80
        JQUERY_VALIDATE => array(
81
            'no' => array(
82
                'no'  => '/js/common/jquery.validate.js',
83
                'min' => '/js/common/jquery.validate.min.js'
84
            ),
85
            'cdn' => array(
86
                'no'  => '//ajax.aspnetcdn.com/ajax/jquery.validate/1.13.1/jquery.validate.js',
87
                'min' => '//ajax.aspnetcdn.com/ajax/jquery.validate/1.13.1/jquery.validate.min.js'
88
            )
89
        ),
90
        JQUERY_TOUCH => array(
91
            'no' => array(
92
                'no'  => '/js/common/jquery.ui.touch-punch.min.js',
93
                'min' => '/js/common/jquery.ui.touch-punch.min.js'
94
            ),
95
            'cdn' => array(
96
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js',
97
                'min' => '//cdnjs.cloudflare.com/ajax/libs/jqueryui-touch-punch/0.2.3/jquery.ui.touch-punch.min.js'
98
            )
99
        ),
100
        JS_TINYNAV => array(
101
            'no' => array(
102
                'no'  => '/js/common/tinynav.js',
103
                'min' => '/js/common/tinynav.min.js'
104
            ),
105
            'cdn' => array(
106
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/TinyNav.js/1.2.0/tinynav.js',
107
                'min' => '//cdnjs.cloudflare.com/ajax/libs/TinyNav.js/1.2.0/tinynav.min.js'
108
            )
109
        ),
110
        JS_BOOTSTRAP_FH => array(
111
            'no' => array(
112
                'no'  => '/js/common/bootstrap-formhelpers.js',
113
                'min' => '/js/common/bootstrap-formhelpers.min.js'
114
            ),
115
            'cdn' => array(
116
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/bootstrap-formhelpers/2.3.0/js/bootstrap-formhelpers.js',
117
                'min' => '//cdnjs.cloudflare.com/ajax/libs/bootstrap-formhelpers/2.3.0/js/bootstrap-formhelpers.min.js'
118
            )
119
        ),
120
        JS_BOOTSTRAP_SW => array(
121
            'no' => array(
122
                'no'  => '/js/common/bootstrap-switch.js',
123
                'min' => '/js/common/bootstrap-switch.min.js'
124
            ),
125
            'cdn' => array(
126
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.2/js/bootstrap-switch.js',
127
                'min' => '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.2/js/bootstrap-switch.min.js'
128
            )
129
        ),
130
        JS_DATATABLE => array(
131
            'no' => array(
132
                'no'  => '/js/common/jquery.dataTables.js',
133
                'min' => '/js/common/jquery.dataTables.min.js'
134
            ),
135
            'cdn' => array(
136
                'no'  => '//cdn.datatables.net/1.10.7/js/jquery.dataTables.js',
137
                'min' => '//cdn.datatables.net/1.10.7/js/jquery.dataTables.min.js'
138
            )
139
        ),
140
        JS_CHART => array(
141
            'no' => array(
142
                'no'  => '/js/common/Chart.js',
143
                'min' => '/js/common/Chart.min.js'
144
            ),
145
            'cdn' => array(
146
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.js',
147
                'min' => '//cdnjs.cloudflare.com/ajax/libs/Chart.js/1.0.2/Chart.min.js'
148
            )
149
        ),
150
        JS_METISMENU => array(
151
            'no' => array(
152
                'no'  => '/js/common/metisMenu.js',
153
                'min' => '/js/common/metisMenu.min.js'
154
            ),
155
            'cdn' => array(
156
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/metisMenu/2.0.2/metisMenu.js',
157
                'min' => '//cdnjs.cloudflare.com/ajax/libs/metisMenu/2.0.2/metisMenu.min.js'
158
            )
159
        ),
160
        JS_BOOTBOX => array(
161
            'no' => array(
162
                'no'  => '/js/common/bootbox.js',
163
                'min' => '/js/common/bootbox.min.js'
164
            ),
165
            'cdn' => array(
166
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.3.0/bootbox.js',
167
                'min' => '//cdnjs.cloudflare.com/ajax/libs/bootbox.js/4.3.0/bootbox.min.js'
168
            )
169
        ),
170
        JS_DATATABLE_ODATA => array(
171
            'no' => array(
172
                'no'  => '/js/common/jquery.dataTables.odata.js',
173
                'min' => '/js/common/jquery.dataTables.odata.js',
174
            ),
175
            'cdn' => array(
176
                'no'  => '/js/common/jquery.dataTables.odata.js',
177
                'min' => '/js/common/jquery.dataTables.odata.js',
178
            )
179
        ),
180
        JS_CRYPTO_MD5_JS => array(
181
            'no' => array(
182
                'no'  => '/js/common/md5.js',
183
                'min' => '/js/common/md5.js',
184
            ),
185
            'cdn' => array(
186
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/md5.js',
187
                'min' => '//cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/md5.js',
188
            )
189
        ),
190
        JS_JCROP => array(
191
            'no' => array(
192
                'no'  => '/js/common/jquery.Jcrop.js',
193
                'min' => '/js/common/jquery.Jcrop.min.js'
194
            ),
195
            'cdn' => array(
196
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/jquery-jcrop/0.9.12/js/jquery.Jcrop.js',
197
                'min' => '//cdnjs.cloudflare.com/ajax/libs/jquery-jcrop/0.9.12/js/jquery.Jcrop.min.js'
198
            )
199
        ),
200
        JS_TYPEAHEAD => array(
201
            'no' => array(
202
                'no'  => '/js/common/typeahead.bundle.js',
203
                'min' => '/js/common/typeahead.bundle.min.js'
204
            ),
205
            'cdn' => array(
206
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.js',
207
                'min' => '//cdnjs.cloudflare.com/ajax/libs/typeahead.js/0.11.1/typeahead.bundle.min.js'
208
            )
209
        ),
210
        JS_CHEET => array(
211
            'no' => array(
212
                'no'  => '/js/common/cheet.js',
213
                'min' => '/js/common/cheer.min.js'
214
            ),
215
            'cdn' => array(
216
                'no'  => '//cdn.rawgit.com/namuol/cheet.js/master/cheet.min.js',
217
                'min' => '//cdn.rawgit.com/namuol/cheet.js/master/cheet.min.js'
218
            )
219
        ),
220
        JS_FLIPSIDE => array(
221
            'no' => array(
222
                'no'  => '/js/common/flipside.js',
223
                'min' => '/js/common/flipside.min.js'
224
            ),
225
            'cdn' => array(
226
                'no'  => '/js/common/flipside.js',
227
                'min' => '/js/common/flipside.min.js'
228
            )
229
        ),
230
        JS_LOGIN => array(
231
            'no' => array(
232
                'no'  => '/js/common/login.js',
233
                'min' => '/js/common/login.min.js'
234
            ),
235
            'cdn' => array(
236
                'no'  => '/js/common/login.js',
237
                'min' => '/js/common/login.min.js'
238
            )
239
        )
240
);
241
242
global $cssArray;
243
$cssArray = array(
244
    CSS_JQUERY_UI => array(
245
        'no' => array(
246
                'no'  => '/css/common/jquery-ui.css',
247
                'min' => '/css/common/jquery-ui.min.css'
248
            ),
249
            'cdn' => array(
250
                'no'  => '//ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.css',
251
                'min' => '//ajax.googleapis.com/ajax/libs/jqueryui/1.11.4/themes/smoothness/jquery-ui.min.css'
252
            )
253
    ),
254
    CSS_BOOTSTRAP => array(
255
            'no' => array(
256
                'no'  => '/css/common/bootstrap.css',
257
                'min' => '/css/common/bootstrap.min.css'
258
            ),
259
            'cdn' => array(
260
                'no'  => '//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.css',
261
                'min' => '//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css'
262
            )
263
    ),
264
    CSS_BOOTSTRAP_FH => array(
265
        'no' => array(
266
                'no'  => '/css/common/bootstrap-formhelpers.css',
267
                'min' => '/css/common/bootstrap-formhelpers.min.css'
268
            ),
269
            'cdn' => array(
270
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/bootstrap-formhelpers/2.3.0/css/bootstrap-formhelpers.css',
271
                'min' => '//cdnjs.cloudflare.com/ajax/libs/bootstrap-formhelpers/2.3.0/css/bootstrap-formhelpers.min.css'
272
            )
273
    ),
274
    CSS_BOOTSTRAP_SW => array(
275
            'no' => array(
276
                'no'  => '/css/common/bootstrap-switch.css',
277
                'min' => '/css/common/bootstrap-switch.min.css'
278
            ),
279
            'cdn' => array(
280
                'no'  => '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.2/css/bootstrap3/bootstrap-switch.css',
281
                'min' => '//cdnjs.cloudflare.com/ajax/libs/bootstrap-switch/3.3.2/css/bootstrap3/bootstrap-switch.min.css'
282
            )
283
    ),
284
    CSS_DATATABLE => array(
285
        'no' => array(
286
                'no'  => '/css/common/jquery.dataTables.css',
287
                'min' => '/css/common/jquery.dataTables.min.css'
288
            ),
289
            'cdn' => array(
290
                'no'  => '//cdn.datatables.net/1.10.7/css/jquery.dataTables.css',
291
                'min' => '//cdn.datatables.net/1.10.7/css/jquery.dataTables.min.css'
292
            )
293
    ),
294
    CSS_JCROP => array(
295
        'no'  => array(
296
            'no'  => '/css/common/jquery.Jcrop.css',
297
            'min' => '/css/common/jquery.Jcrop.min.css'
298
        ),
299
        'cdn' => array(
300
            'no'  => '//cdnjs.cloudflare.com/ajax/libs/jquery-jcrop/0.9.12/css/jquery.Jcrop.css',
301
            'min' => '//cdnjs.cloudflare.com/ajax/libs/jquery-jcrop/0.9.12/css/jquery.Jcrop.min.css'
302
        )
303
    ),
304
    CSS_FONTAWESOME => array(
305
        'no'  => array(
306
            'no'  => '/css/common/font-awesome.css',
307
            'min' => '/css/common/font-awesome.min.css'
308
        ),
309
        'cdn' => array(
310
            'no'  => '//maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.css',
311
            'min' => '//maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css'
312
        )
313
    )
314
);
315
316
/**
317
 * A framework specific webpage abstraction layer
318
 *
319
 * This class abstracts out some basic webpage creation with JQuery, Bootstrap, and other helpers
320
 */
321
class FlipPage extends WebPage
322
{
323
    /** The currently logged in user or null if no user is logged in */
324
    public $user;
325
    /** An array of links to put in the header */
326
    public $links;
327
    /** An array of notifications to draw on the page */
328
    public $notifications;
329
    /** Should we draw the header? */
330
    public $header;
331
    /** The login page URL */
332
    public $loginUrl;
333
    /** The logout page URL */
334
    public $logoutUrl;
335
    /** Should we use minified JS/CSS? */
336
    protected $minified = null;
337
    /** Should we use local JS/CSS or Content Delivery Networks? */
338
    protected $cdn = null;
339
    /** Draw the analytics scripts */
340
    protected $analytics = true;
341
    /** An instance of the Settings class */
342
    protected $settings;
343
344
    /**
345
     * Create a webpage with JQuery, Bootstrap, etc
346
     *
347
     * @param string $title The webpage title
348
     * @param boolean $header Draw the header bar?
349
     *
350
     * @SuppressWarnings("StaticAccess")
351
     */
352
    public function __construct($title, $header = true)
353
    {
354
        $this->settings = \Settings::getInstance();
355
        parent::__construct($title);
356
        $this->setupVars();
357
        $this->addWellKnownJS(JS_JQUERY, false);
358
        $this->addWellKnownJS(JS_FLIPSIDE, false);
359
        $this->addBootstrap();
360
        $this->header = $header;
361
        $this->links = array();
362
        $this->notifications = array();
363
        $this->aboutUrl = $this->settings->getGlobalSetting('about_url', 'https://www.burningflipside.com/about');
0 ignored issues
show
The property aboutUrl does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
364
        $this->eventUrl = $this->settings->getGlobalSetting('event_url', 'https://www.burningflipside.com/about/event');
0 ignored issues
show
The property eventUrl does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
365
        $this->aarUrl = $this->settings->getGlobalSetting('aar_url', 'https://www.burningflipside.com/about/aar');
0 ignored issues
show
The property aarUrl does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
366
        $this->privacyUrl = $this->settings->getGlobalSetting('privacy_url', 'https://www.burningflipside.com/about/privacy');
0 ignored issues
show
The property privacyUrl does not exist. Did you maybe forget to declare it?

In PHP it is possible to write to properties without declaring them. For example, the following is perfectly valid PHP code:

class MyClass { }

$x = new MyClass();
$x->foo = true;

Generally, it is a good practice to explictly declare properties to avoid accidental typos and provide IDE auto-completion:

class MyClass {
    public $foo;
}

$x = new MyClass();
$x->foo = true;
Loading history...
367
        $this->loginUrl = $this->settings->getGlobalSetting('login_url', 'login.php');
368
        $this->logoutUrl = $this->settings->getGlobalSetting('logout_url', 'logout.php');
369
        $this->user = FlipSession::getUser();
370
        $this->addAllLinks();
371
    }
372
373
    /**
374
     * Get the external site links for this page
375
     *
376
     */
377
    protected function getSites()
378
    {
379
        return $this->settings->getSiteLinks();
380
    }
381
382
    /**
383
     * Add the links to be used in the header
384
     *
385
     * @SuppressWarnings("Superglobals")
386
     *
387
     * @todo Consider pulling the about menu from the settings file or a DB
388
     */
389
    protected function addAllLinks()
390
    {
391
        if($this->user === false || $this->user === null)
392
        {
393
            if(isset($_SERVER['REQUEST_URI']) && strstr($_SERVER['REQUEST_URI'], 'logout.php') === false)
394
            {
395
                $this->addLink('Login', $this->loginUrl);
396
            }
397
        }
398
        else
399
        {
400
            $this->add_links();
401
            $this->addLink('Logout', $this->logoutUrl);
402
        }
403
        $aboutMenu = array(
404
            'Burning Flipside' => $this->eventUrl,
405
            'AAR, LLC' => $this->aarUrl,
406
            'Privacy Policy' => $this->privacyUrl
407
        );
408
        $this->addLink('About', $this->aboutUrl, $aboutMenu);
409
    }
410
411
    /**
412
     * Setup minified and cdn class varibles from defaults or the settings file
413
     */
414
    private function setupVars()
415
    {
416
        if($this->minified !== null && $this->cdn !== null)
417
        {
418
            return;
419
        }
420
        $this->minified = 'min';
421
        $this->cdn      = 'cdn';
422
        if($this->settings->getGlobalSetting('use_minified', true) == false)
423
        {
424
            $this->minified = 'no';
425
        }
426
        if($this->settings->getGlobalSetting('use_cdn', true) == false)
427
        {
428
            $this->cdn = 'no';
429
        }
430
    }
431
432
    /**
433
     * Add a JavaScript file from its src URI
434
     *
435
     * @param string $uri The webpath to the JavaScript file
436
     * @param boolean $async Can the JavaScript be loaded asynchronously?
437
     */
438
    public function addJSByURI($uri, $async = true)
439
    {
440
        $attributes = array('src'=>$uri, 'type'=>'text/javascript');
441
        if($async === true)
442
        {
443
            $attributes['async'] = true;
444
        }
445
        $jsTag = $this->createOpenTag('script', $attributes);
446
        $closeTag = $this->createCloseTag('script');
447
        $this->addHeadTag($jsTag);
448
        $this->addHeadTag($closeTag);
449
    }
450
451
    /**
452
     * Add a Cascading Style Sheet file from its src URI
453
     *
454
     * @param string $uri The webpath to the Cascading Style Sheet file
455
     * @param boolean $async Can the CSS be loaded asynchronously?
456
     */
457
    public function addCSSByURI($uri, $async = false)
458
    {
459
        $attributes = array('rel'=>'stylesheet', 'href'=>$uri, 'type'=>'text/css');
460
        if($async === true && $this->importSupport === true)
461
        {
462
            $attributes['rel'] = 'import';
463
        }
464
        $cssTag = $this->createOpenTag('link', $attributes, true);
465
        $this->addHeadTag($cssTag);
466
    }
467
468
    /**
469
     * Add a JavaScript file from a set of files known to the framework
470
     *
471
     * @param string $jsFileID the ID of the JS file
472
     * @param boolean $async Can the JS file be loaded asynchronously?
473
     */
474
    public function addWellKnownJS($jsFileID, $async = true)
475
    {
476
        global $jsArray;
477
        $this->setupVars();
478
        $src = $jsArray[$jsFileID][$this->cdn][$this->minified];
479
        $this->addJSByURI($src, $async);
480
    }
481
482
    /**
483
     * Add a CSS file from a set of files known to the framework
484
     *
485
     * @param string $cssFileID the ID of the CSS file
486
     * @param boolean $async Can the CSS file be loaded asynchronously?
487
     */
488
    public function addWellKnownCSS($cssFileID, $async = true)
489
    {
490
        global $cssArray;
491
        $this->setupVars();
492
        $src = $cssArray[$cssFileID][$this->cdn][$this->minified];
493
        $this->addCSSByURI($src, $async);
494
    }
495
496
    /**
497
     * Add files needed by the Bootstrap framework
498
     */
499
    private function addBootstrap()
500
    {
501
        $this->addWellKnownJS(JS_BOOTSTRAP, false);
502
        $this->addWellKnownCSS(CSS_BOOTSTRAP);
503
        $this->addWellKnownCSS(CSS_FONTAWESOME);
504
    }
505
506
    protected function getSiteLinksForHeader()
507
    {
508
        $sites = $this->getSites();
509
        $names = array_keys($sites);
510
        $ret = '';
511
        foreach($names as $name)
512
        {
513
            $ret .= '<li>'.$this->createLink($name, $sites[$name]).'</li>';
514
        }
515
        return $ret;
516
    }
517
518
    /**
519
     * Get the link for the HREF
520
     *
521
     * @return string The HREF for the dropdown
522
     */
523
    protected function getHrefForDropdown(&$link)
524
    {
525
        if(isset($link['_']))
526
        {
527
            $ret = $link['_'];
528
            unset($link['_']);
529
            return $ret;
530
        }
531
        return '#';
532
    }
533
534
    protected function getDropdown($link, $name)
535
    {
536
        $ret = '<li class="dropdown">';
537
        $href = $this->getHrefForDropdown($link);
538
        $ret .= '<a href="'.$href.'" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">'.$name.' <span class="caret"></span></a>';
539
        $ret .= '<ul class="dropdown-menu">';
540
        $subNames = array_keys($link);
541
        foreach($subNames as $subName)
542
        {
543
            $ret .= $this->getLinkByName($subName, $link);
544
        }
545
        $ret .= '</ul></li>';
546
        return $ret;
547
    }
548
549
    protected function getLinkByName($name, $links)
550
    {
551
        if(is_array($links[$name]))
552
        {
553
            return $this->getDropdown($links[$name], $name);
554
        }
555
        if($links[$name] === false)
556
        {
557
            return '<li>'.$name.'</li>';
558
        }
559
        return '<li>'.$this->createLink($name, $links[$name]).'</li>';
560
    }
561
562
    protected function getLinksMenus()
563
    {
564
        $names = array_keys($this->links);
565
        $ret = '';
566
        foreach($names as $name)
567
        {
568
            $ret .= $this->getLinkByName($name, $this->links);
569
        }
570
        return $ret;
571
    }
572
573
    /**
574
     * Draw the header for the page
575
     */
576
    protected function addHeader()
577
    {
578
        $sites = $this->getSiteLinksForHeader();
579
        $links = $this->getLinksMenus();
580
        $header = '<nav class="navbar navbar-default navbar-fixed-top">
581
                      <div class="container-fluid">
582
                          <!-- Brand and toggle get grouped for better mobile display -->
583
                          <div class="navbar-header">
584
                          <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse" aria-expanded="false">
585
                              <span class="sr-only">Toggle navigation</span>
586
                              <span class="icon-bar"></span>
587
                              <span class="icon-bar"></span>
588
                              <span class="icon-bar"></span>
589
                          </button>
590
                          <a class="navbar-brand" href="#">
591
                              <picture>
592
                                  <source srcset="/img/common/logo.svg" style="width: 30px; height:30px"/>
593
                                  <img alt="Burning Flipside" src="/img/common/logo.png" style="width: 30px; height:30px"/>
594
                              </picture>
595
                          </a>
596
                          </div>
597
                          <!-- Collect the nav links, forms, and other content for toggling -->
598
                          <div class="collapse navbar-collapse" id="navbar-collapse">
599
                              <ul id="site_nav" class="nav navbar-nav">
600
                              '.$sites.'
601
                              </ul>
602
                              <ul class="nav navbar-nav navbar-right">
603
                              '.$links.'
604
                              </ul>
605
                          </div>
606
                      </div>
607
                  </nav>';
608
        $this->body = $header.$this->body;
609
        $this->body_tags .= 'style="padding-top: 60px;"';
610
    }
611
612
    /** Notification that is green for success */
613
    const NOTIFICATION_SUCCESS = 'alert-success';
614
    /** Notification that is blue for infomrational messages */
615
    const NOTIFICATION_INFO    = 'alert-info';
616
    /** Notification that is yellow for warning */
617
    const NOTIFICATION_WARNING = 'alert-warning';
618
    /** Notification that is red for error */
619
    const NOTIFICATION_FAILED  = 'alert-danger';
620
621
    /**
622
     * Add a notification to the page
623
     *
624
     * @param string $message The message to show in the notifcation
625
     * @param string $severity The severity of the notifcation
626
     * @param boolean $dismissible Can the user dismiss the notificaton?
627
     */
628
    public function addNotification($message, $severity = self::NOTIFICATION_INFO, $dismissible = true)
629
    {
630
        array_push($this->notifications, array('msg'=>$message, 'sev'=>$severity, 'dismissible'=>$dismissible)); 
631
    }
632
633
    /**
634
     * Draw all notifications to the page
635
     */
636
    private function renderNotifications()
637
    {
638
        $count = count($this->notifications);
639
        if($count === 0)
640
        {
641
            return;
642
        }
643
        for($i = 0; $i < $count; $i++)
644
        {
645
            $class = 'alert '.$this->notifications[$i]['sev'];
646
            $button = '';
647
            if($this->notifications[$i]['dismissible'])
648
            {
649
                $class .= ' alert-dismissible';
650
                $button = '<button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>';
651
            }
652
            $prefix = '';
653
            switch($this->notifications[$i]['sev'])
654
            {
655
                case self::NOTIFICATION_INFO:
656
                    $prefix = '<strong>Notice:</strong> '; 
657
                    break;
658
                case self::NOTIFICATION_WARNING:
659
                    $prefix = '<strong>Warning!</strong> ';
660
                    break;
661
                case self::NOTIFICATION_FAILED:
662
                    $prefix = '<strong>Warning!</strong> ';
663
                    break;
664
            }
665
            $style = '';
666
            if(($i + 1) < count($this->notifications))
667
            {
668
                //Not the last notification, remove the end margin
669
                $style = 'style="margin: 0px;"';
670
            }
671
            $this->body = '
672
                <div class="'.$class.'" role="alert" '.$style.'>
673
                    '.$button.$prefix.$this->notifications[$i]['msg'].'
674
                </div>
675
            '.$this->body;
676
        }
677
    }
678
679
    /**
680
     * Add the no script block
681
     */
682
    private function addNoScript()
683
    {
684
        $this->body = '<noscript>
685
                <div class="alert alert-danger alert-dismissible" role="alert">
686
                    <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
687
                    <strong>Error!</strong> This site makes extensive use of JavaScript. Please enable JavaScript or this site will not function.
688
                </div>
689
            </noscript>'.$this->body;
690
    }
691
692
    /**
693
     * Add the analytics script block
694
     */
695
    private function addAnalyticsBlock()
696
    {
697
        if($this->analytics === false)
698
        {
699
            return;
700
        }
701
        $this->body = $this->body.'<script>
702
  (function(i,s,o,g,r,a,m){i[\'GoogleAnalyticsObject\']=r;i[r]=i[r]||function(){
703
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
704
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
705
  })(window,document,\'script\',\'//www.google-analytics.com/analytics.js\',\'ga\');
706
707
  ga(\'create\', \'UA-64901342-1\', \'auto\');
708
  ga(\'send\', \'pageview\');
709
710
</script>';
711
    }
712
713
    /**
714
     * Draw the page
715
     *
716
     * @param boolean $header Draw the header
717
     */
718
    public function printPage($header = true)
719
    {
720
        $this->renderNotifications();
721
        $this->addNoScript();
722
        $this->addAnalyticsBlock();
723
        if($this->header || $header)
724
        {
725
            $this->addHeader();
726
        }
727
        parent::printPage();
728
    }
729
730
    /**
731
     * Add a link to the header
732
     *
733
     * @param string $name The name of the link
734
     * @param boolean|string $url The URL to link to
735
     * @param boolean|array $submenu Any submenu items for the dropdown
736
     */
737
    public function addLink($name, $url = false, $submenu = false)
738
    {
739
        if(is_array($submenu))
740
        {
741
            $submenu['_'] = $url;
742
            $this->links[$name] = $submenu;
743
            return;
744
        }
745
        $this->links[$name] = $url;
746
    }
747
748
    /**
749
     * Add the login form to the page
750
     *
751
     * @SuppressWarnings("StaticAccess")
752
     */
753
    public function add_login_form()
754
    {
755
        $auth = \AuthProvider::getInstance();
756
        $authLinks = $auth->getSupplementaryLinks();
757
        $authLinksStr = '';
758
        $count = count($authLinks);
759
        for($i = 0; $i < $count; $i++)
760
        {
761
            $authLinksStr .= $authLinks[$i];
762
        }
763
        if($count > 0)
764
        {
765
            $authLinksStr = 'Sign in with '.$authLinksStr;
766
        }
767
        $this->body .= '<div class="modal fade" role="dialog" id="login-dialog" title="Login" aria-hidden="true">
768
                            <div class="modal-dialog">
769
                                <div class="modal-content">
770
                                    <div class="modal-header">
771
                                        <button type="button" class="close" data-dismiss="modal">
772
                                            <span aria-hidden="true">&times;</span>
773
                                            <span class="sr-only">Close</span>
774
                                        </button>
775
                                        <h4 class="modal-title">Login</h4>
776
                                    </div>
777
                                    <div class="modal-body">
778
                                        <form id="login_dialog_form" role="form">
779
                                            <input class="form-control" type="text" name="username" placeholder="Username or Email" required autofocus/>
780
                                            <input class="form-control" type="password" name="password" placeholder="Password" required/>
781
                                            <input type="hidden" name="return" value="'.$this->currentUrl().'"/>
782
                                            <button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
783
                                        </form>
784
                                        '.$authLinksStr.'
785
                                    </div>
786
                                </div>
787
                            </div>
788
                        </div>';
789
    }
790
791
    /**
792
     * Add additional links
793
     */
794
    public function add_links()
795
    {
796
    }
797
}
798
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
799