Completed
Push — master ( c9e75c...9f5802 )
by Patrick
12:31 queued 10:30
created

class.FlipPage.php (7 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
require_once('static.js_css.php');
20
21
/**
22
 * A framework specific webpage abstraction layer
23
 *
24
 * This class abstracts out some basic webpage creation with JQuery, Bootstrap, and other helpers
25
 */
26
class FlipPage extends WebPage
27
{
28
    /** The currently logged in user or null if no user is logged in */
29
    public $user;
30
    /** An array of links to put in the header */
31
    public $links;
32
    /** An array of notifications to draw on the page */
33
    public $notifications;
34
    /** Should we draw the header? */
35
    public $header;
36
    /** The login page URL */
37
    public $loginUrl;
38
    /** The logout page URL */
39
    public $logoutUrl;
40
    /** Should we use minified JS/CSS? */
41
    protected $minified = null;
42
    /** Should we use local JS/CSS or Content Delivery Networks? */
43
    protected $cdn = null;
44
    /** Draw the analytics scripts */
45
    protected $analytics = true;
46
    /** An instance of the Settings class */
47
    protected $settings;
48
49
    public $wwwUrl;
50
    public $wikiUrl;
51
    public $profilesUrl;
52
    public $secureUrl;
53
54
    /**
55
     * Create a webpage with JQuery, Bootstrap, etc
56
     *
57
     * @param string $title The webpage title
58
     * @param boolean $header Draw the header bar?
59
     *
60
     * @SuppressWarnings("StaticAccess")
61
     */
62
    public function __construct($title, $header = true)
63
    {
64
        $this->settings = \Settings::getInstance();
65
        parent::__construct($title);
66
        $this->setupVars();
67
        $this->addWellKnownJS(JS_JQUERY, false);
68
        $this->addWellKnownJS(JS_FLIPSIDE, false);
69
        $this->addBootstrap();
70
        $this->header = $header;
71
        $this->links = array();
72
        $this->notifications = array();
73
74
        $this->wwwUrl = $this->settings->getGlobalSetting('www_url', 'https://www.burningflipside.com');
75
76
        $this->wikiUrl = $this->settings->getGlobalSetting('wiki_url', 'https://wiki.burningflipside.com');
77
78
        $this->aboutUrl = $this->settings->getGlobalSetting('about_url', $this->wwwUrl.'/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...
79
        $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...
80
            'Burning Flipside' => $this->wwwUrl.'/about/event',
81
            'AAR, LLC' => $this->wwwUrl.'/organization/aar',
82
            'Privacy Policy' => $this->wwwUrl.'/about/privacy'
83
        ));
84
85
        $this->profilesUrl = $this->settings->getGlobalSetting('profiles_url', 'https://profiles.burningflipside.com/');
86
        $this->loginUrl = $this->settings->getGlobalSetting('login_url', $this->profilesUrl.'/login.php');
87
        $this->logoutUrl = $this->settings->getGlobalSetting('logout_url', $this->profilesUrl.'/logout.php');
88
        $this->registerUrl = $this->settings->getGlobalSetting('register_url', $this->profilesUrl.'/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...
89
        $this->resetUrl = $this->settings->getGlobalSetting('reset_url', $this->profilesUrl.'/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...
90
91
        $this->secureUrl = $this->settings->getGlobalSetting('secure_url', 'https://secure.burningflipside.com/');
92
93
        $this->user = FlipSession::getUser();
94
        $this->addAllLinks();
95
    }
96
97
    /**
98
     * Get the external site links for this page
99
     *
100
     */
101
    protected function getSites()
102
    {
103
        return $this->settings->getSiteLinks();
104
    }
105
106
    /**
107
     * Add the links to be used in the header
108
     *
109
     * @SuppressWarnings("Superglobals")
110
     *
111
     * @todo Consider pulling the about menu from the settings file or a DB
112
     */
113
    protected function addAllLinks()
114
    {
115 View Code Duplication
        if($this->user === false || $this->user === null)
0 ignored issues
show
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
116
        {
117
            if(isset($_SERVER['REQUEST_URI']) && strstr($_SERVER['REQUEST_URI'], 'logout.php') === false)
118
            {
119
                $this->addLink('Login', $this->loginUrl);
120
            }
121
        }
122
        else
123
        {
124
            $this->add_links();
125
            $this->addLink('Logout', $this->logoutUrl);
126
        }
127
        if($this->aboutUrl !== false)
128
        {
129
            if(!empty($this->aboutMenu))
130
            {
131
                $this->addLink('About', $this->aboutUrl, $this->aboutMenu);
132
            }
133
            else
134
            {
135
                $this->addLink('About', $this->aboutUrl);
136
            }
137
        }
138
    }
139
140
    /**
141
     * Setup minified and cdn class varibles from defaults or the settings file
142
     */
143
    private function setupVars()
144
    {
145
        if($this->minified !== null && $this->cdn !== null)
146
        {
147
            return;
148
        }
149
        $this->minified = 'min';
150
        $this->cdn      = 'cdn';
151
        if($this->settings->getGlobalSetting('use_minified', true) == false)
152
        {
153
            $this->minified = 'no';
154
        }
155
        if($this->settings->getGlobalSetting('use_cdn', true) == false)
156
        {
157
            $this->cdn = 'no';
158
        }
159
    }
160
161
    /**
162
     * Add a JavaScript file from its src URI
163
     *
164
     * @param string $uri The webpath to the JavaScript file
165
     * @param boolean $async Can the JavaScript be loaded asynchronously?
166
     */
167
    public function addJSByURI($uri, $async = true)
168
    {
169
        $attributes = array('src'=>$uri, 'type'=>'text/javascript');
170
        if($async === true)
171
        {
172
            $attributes['async'] = true;
173
        }
174
        $jsTag = $this->createOpenTag('script', $attributes);
175
        $closeTag = $this->createCloseTag('script');
176
        $this->addHeadTag($jsTag);
177
        $this->addHeadTag($closeTag);
178
    }
179
180
    /**
181
     * Add a Cascading Style Sheet file from its src URI
182
     *
183
     * @param string $uri The webpath to the Cascading Style Sheet file
184
     * @param boolean $async Can the CSS be loaded asynchronously?
185
     */
186
    public function addCSSByURI($uri, $async = false)
187
    {
188
        $attributes = array('rel'=>'stylesheet', 'href'=>$uri, 'type'=>'text/css');
189
        if($async === true && $this->importSupport === true)
190
        {
191
            $attributes['rel'] = 'import';
192
        }
193
        $cssTag = $this->createOpenTag('link', $attributes, true);
194
        $this->addHeadTag($cssTag);
195
    }
196
197
    /**
198
     * Add a JavaScript file from a set of files known to the framework
199
     *
200
     * @param string $jsFileID the ID of the JS file
201
     * @param boolean $async Can the JS file be loaded asynchronously?
202
     */
203
    public function addWellKnownJS($jsFileID, $async = false)
204
    {
205
        global $jsArray;
206
        $this->setupVars();
207
        $src = $jsArray[$jsFileID][$this->cdn][$this->minified];
208
        $this->addJSByURI($src, $async);
209
    }
210
211
    /**
212
     * Add a CSS file from a set of files known to the framework
213
     *
214
     * @param string $cssFileID the ID of the CSS file
215
     * @param boolean $async Can the CSS file be loaded asynchronously?
216
     */
217
    public function addWellKnownCSS($cssFileID, $async = false)
218
    {
219
        global $cssArray;
220
        $this->setupVars();
221
        $src = $cssArray[$cssFileID][$this->cdn][$this->minified];
222
        $this->addCSSByURI($src, $async);
223
    }
224
225
    /**
226
     * Add files needed by the Bootstrap framework
227
     */
228
    private function addBootstrap()
229
    {
230
        $this->addWellKnownJS(JS_BOOTSTRAP);
231
        $this->addWellKnownCSS(CSS_BOOTSTRAP);
232
        $this->addWellKnownCSS(CSS_FONTAWESOME);
233
234
    }
235
236
    protected function getSiteLinksForHeader()
237
    {
238
        $sites = $this->getSites();
239
        $names = array_keys($sites);
240
        $ret = '';
241
        foreach($names as $name)
242
        {
243
            $ret .= '<li class="nav-item">'.$this->createLink($name, $sites[$name], 'nav-link').'</li>';
0 ignored issues
show
'nav-link' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
244
        }
245
        return $ret;
246
    }
247
248
    /**
249
     * Get the link for the HREF
250
     *
251
     * @return string The HREF for the dropdown
252
     */
253
    protected function getHrefForDropdown(&$link)
254
    {
255
        if(isset($link['_']))
256
        {
257
            $ret = $link['_'];
258
            unset($link['_']);
259
            return $ret;
260
        }
261
        return '#';
262
    }
263
264
    protected function getDropdown($link, $name)
265
    {
266
        $ret = '<li class="nav-item dropdown">';
267
        $href = $this->getHrefForDropdown($link);
268
        $ret .= '<a href="'.$href.'" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">'.$name.' <span class="caret"></span></a>';
269
        $ret .= '<ul class="dropdown-menu dropdown-menu-right">';
270
        $subNames = array_keys($link);
271
        foreach($subNames as $subName)
272
        {
273
            $ret .= $this->getLinkByName($subName, $link);
274
        }
275
        $ret .= '</ul></li>';
276
        return $ret;
277
    }
278
279
    protected function getLinkByName($name, $links)
280
    {
281
        if(is_array($links[$name]))
282
        {
283
            return $this->getDropdown($links[$name], $name);
284
        }
285
        if($links[$name] === false)
286
        {
287
            return '<li class="nav-item">'.$name.'</li>';
288
        }
289
        return '<li class="nav-item">'.$this->createLink($name, $links[$name], 'nav-link').'</li>';
0 ignored issues
show
'nav-link' is of type string, but the function expects a boolean.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
290
    }
291
292
    protected function getLinksMenus()
293
    {
294
        $names = array_keys($this->links);
295
        $ret = '';
296
        foreach($names as $name)
297
        {
298
            $ret .= $this->getLinkByName($name, $this->links);
299
        }
300
        return $ret;
301
    }
302
303
    /**
304
     * Draw the header for the page
305
     */
306
    protected function addHeader()
307
    {
308
        $sites = $this->getSiteLinksForHeader();
309
        $links = $this->getLinksMenus();
310
        $header = '<nav class="navbar navbar-expand-lg navbar-light bg-light">
311
                      <a class="navbar-brand" href="#">
312
                          <picture>
313
                              <source srcset="/img/common/logo.svg" style="width: 30px; height:30px"/>
314
                              <img alt="Burning Flipside" src="/img/common/logo.png" style="width: 30px; height:30px"/>
315
                          </picture>
316
                      </a>
317
                      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
318
                          <span class="navbar-toggler-icon"></span>
319
                      </button>
320
                      <div class="collapse navbar-collapse" id="navbarSupportedContent">
321
                          <ul class="navbar-nav mr-auto">
322
                          '.$sites.'
323
                          </ul>
324
                          <ul class="navbar-nav navbar-right">
325
                          '.$links.'
326
                          </ul>
327
                      </div>
328
                  </nav>';
329
        $this->body = $header.$this->body;
330
        #$this->body_tags .= 'style="padding-top: 60px;"';
331
    }
332
333
    /** Notification that is green for success */
334
    const NOTIFICATION_SUCCESS = 'alert-success';
335
    /** Notification that is blue for infomrational messages */
336
    const NOTIFICATION_INFO    = 'alert-info';
337
    /** Notification that is yellow for warning */
338
    const NOTIFICATION_WARNING = 'alert-warning';
339
    /** Notification that is red for error */
340
    const NOTIFICATION_FAILED  = 'alert-danger';
341
342
    /**
343
     * Add a notification to the page
344
     *
345
     * @param string $message The message to show in the notifcation
346
     * @param string $severity The severity of the notifcation
347
     * @param boolean $dismissible Can the user dismiss the notificaton?
348
     */
349
    public function addNotification($message, $severity = self::NOTIFICATION_INFO, $dismissible = true)
350
    {
351
        array_push($this->notifications, array('msg'=>$message, 'sev'=>$severity, 'dismissible'=>$dismissible));
352
    }
353
354
    /**
355
     * Draw all notifications to the page
356
     */
357
    private function renderNotifications()
358
    {
359
        $count = count($this->notifications);
360
        if($count === 0)
361
        {
362
            return;
363
        }
364
        for($i = 0; $i < $count; $i++)
365
        {
366
            $class = 'alert '.$this->notifications[$i]['sev'];
367
            $button = '';
368
            if($this->notifications[$i]['dismissible'])
369
            {
370
                $class .= ' alert-dismissible';
371
                $button = '<button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>';
372
            }
373
            $prefix = '';
374
            switch($this->notifications[$i]['sev'])
375
            {
376
                case self::NOTIFICATION_INFO:
377
                    $prefix = '<strong>Notice:</strong> ';
378
                    break;
379
                case self::NOTIFICATION_WARNING:
380
                    $prefix = '<strong>Warning!</strong> ';
381
                    break;
382
                case self::NOTIFICATION_FAILED:
383
                    $prefix = '<strong>Warning!</strong> ';
384
                    break;
385
            }
386
            $style = '';
387
            if(($i + 1) < count($this->notifications))
388
            {
389
                //Not the last notification, remove the end margin
390
                $style = 'style="margin: 0px;"';
391
            }
392
            $this->body = '
393
                <div class="'.$class.'" role="alert" '.$style.'>
394
                    '.$button.$prefix.$this->notifications[$i]['msg'].'
395
                </div>
396
            '.$this->body;
397
        }
398
    }
399
400
    /**
401
     * Add the no script block
402
     */
403
    private function addNoScript()
404
    {
405
        $this->body = '<noscript>
406
                <div class="alert alert-danger alert-dismissible" role="alert">
407
                    <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
408
                    <strong>Error!</strong> This site makes extensive use of JavaScript. Please enable JavaScript or this site will not function.
409
                </div>
410
            </noscript>'.$this->body;
411
    }
412
413
    /**
414
     * Add the analytics script block
415
     */
416
    private function addAnalyticsBlock()
417
    {
418
        if($this->analytics === false)
419
        {
420
            return;
421
        }
422
        $this->body = $this->body.'<script>
423
  (function(i,s,o,g,r,a,m){i[\'GoogleAnalyticsObject\']=r;i[r]=i[r]||function(){
424
  (i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
425
  m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
426
  })(window,document,\'script\',\'//www.google-analytics.com/analytics.js\',\'ga\');
427
428
  ga(\'create\', \'UA-64901342-1\', \'auto\');
429
  ga(\'send\', \'pageview\');
430
431
</script>';
432
    }
433
434
    /**
435
     * Add some global js vars
436
     */
437
    private function addJSGlobals()
438
    {
439
      $this->body = $this->body.'<script>
440
var profilesUrl = \''.$this->profilesUrl.'\'
441
var loginUrl = \''.$this->loginUrl.'\'
442
var logoutUrl = \''.$this->logoutUrl.'\'
443
</script>';
444
    }
445
446
    /**
447
     * Draw the page
448
     *
449
     * @param boolean $header Draw the header
450
     */
451
    public function printPage($header = true)
452
    {
453
        $this->renderNotifications();
454
        $this->addNoScript();
455
        $this->addAnalyticsBlock();
456
        $this->addJSGlobals();
457
        if($this->header || $header)
458
        {
459
            $this->addHeader();
460
        }
461
        parent::printPage();
462
    }
463
464
    /**
465
     * Add a link to the header
466
     *
467
     * @param string $name The name of the link
468
     * @param boolean|string $url The URL to link to
469
     * @param boolean|array $submenu Any submenu items for the dropdown
470
     */
471
    public function addLink($name, $url = false, $submenu = false)
472
    {
473
        if(is_array($submenu))
474
        {
475
            $submenu['_'] = $url;
476
            $this->links[$name] = $submenu;
477
            return;
478
        }
479
        $this->links[$name] = $url;
480
    }
481
482
    /**
483
     * Add the login form to the page
484
     *
485
     * @SuppressWarnings("StaticAccess")
486
     */
487
    public function add_login_form()
488
    {
489
        $auth = \AuthProvider::getInstance();
490
        $authLinks = $auth->getSupplementaryLinks();
491
        $authLinksStr = '';
492
        $count = count($authLinks);
493
        for($i = 0; $i < $count; $i++)
494
        {
495
            $authLinksStr .= $authLinks[$i];
496
        }
497
        if($count > 0)
498
        {
499
            $authLinksStr = 'Sign in with '.$authLinksStr;
500
        }
501
        $this->body .= '<div class="modal fade" role="dialog" id="login-dialog" title="Login" aria-hidden="true">
502
                            <div class="modal-dialog" role="document">
503
                                <div class="modal-content">
504
                                    <div class="modal-header">
505
                                        <h4 class="modal-title">Login</h4>
506
                                        <button type="button" class="close" data-dismiss="modal">
507
                                            <span aria-hidden="true">&times;</span>
508
                                            <span class="sr-only">Close</span>
509
                                        </button>
510
                                    </div>
511
                                    <div class="modal-body">
512
                                        <form id="login_dialog_form" role="form">
513
                                            <input class="form-control" type="text" name="username" placeholder="Username or Email" required autofocus/>
514
                                            <input class="form-control" type="password" name="password" placeholder="Password" required/>
515
                                            <input type="hidden" name="return" value="'.$this->currentUrl().'"/>
516
                                            <button class="btn btn-lg btn-primary btn-block" type="submit">Login</button>
517
                                        </form>
518
                                        '.$authLinksStr.'
519
                                    </div>
520
                                </div>
521
                            </div>
522
                        </div>';
523
    }
524
525
    /**
526
     * Add additional links
527
     */
528
    public function add_links()
529
    {
530
    }
531
}
532
/* vim: set tabstop=4 shiftwidth=4 expandtab: */
533