Completed
Pull Request — master (#21)
by
unknown
02:37
created

class.FlipPage.php (5 issues)

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