Url::deviceLink()   F
last analyzed

Complexity

Conditions 17
Paths 5122

Size

Total Lines 72
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 41
dl 0
loc 72
rs 1.0499
c 0
b 0
f 0
cc 17
nc 5122
nop 7

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Url.php
4
 *
5
 * -Description-
6
 *
7
 * This program is free software: you can redistribute it and/or modify
8
 * it under the terms of the GNU General Public License as published by
9
 * the Free Software Foundation, either version 3 of the License, or
10
 * (at your option) any later version.
11
 *
12
 * This program is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
15
 * GNU General Public License for more details.
16
 *
17
 * You should have received a copy of the GNU General Public License
18
 * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19
 *
20
 * @link       https://www.librenms.org
21
 *
22
 * @copyright  2018 Tony Murray
23
 * @author     Tony Murray <[email protected]>
24
 */
25
26
namespace LibreNMS\Util;
27
28
use App\Models\Device;
29
use App\Models\Port;
30
use Carbon\Carbon;
31
use Carbon\CarbonImmutable;
32
use Illuminate\Support\Facades\Auth;
33
use Illuminate\Support\Str;
34
use LibreNMS\Config;
35
use Symfony\Component\HttpFoundation\ParameterBag;
36
37
class Url
38
{
39
    /**
40
     * @param  Device  $device
41
     * @param  string  $text
42
     * @param  array  $vars
43
     * @param  int  $start
44
     * @param  int  $end
45
     * @param  int  $escape_text
46
     * @param  int  $overlib
47
     * @return string
48
     */
49
    public static function deviceLink($device, $text = null, $vars = [], $start = 0, $end = 0, $escape_text = 1, $overlib = 1)
50
    {
51
        if (! $device instanceof Device || ! $device->hostname) {
0 ignored issues
show
introduced by
$device is always a sub-type of App\Models\Device.
Loading history...
52
            return '';
53
        }
54
55
        if (! $device->canAccess(Auth::user())) {
56
            return $device->displayName();
57
        }
58
59
        if (! $start) {
60
            $start = Carbon::now()->subDay()->timestamp;
61
        }
62
63
        if (! $end) {
64
            $end = Carbon::now()->timestamp;
65
        }
66
67
        if (! $text) {
68
            $text = $device->displayName();
69
        }
70
71
        if ($escape_text) {
72
            $text = htmlentities($text);
73
        }
74
75
        $class = self::deviceLinkDisplayClass($device);
76
        $graphs = Graph::getOverviewGraphsForDevice($device);
77
        $url = Url::deviceUrl($device, $vars);
78
79
        // beginning of overlib box contains large hostname followed by hardware & OS details
80
        $contents = '<div><span class="list-large">' . $device->displayName() . '</span>';
81
        if ($device->hardware) {
82
            $contents .= ' - ' . htmlentities($device->hardware);
83
        }
84
85
        if ($device->os) {
86
            $contents .= ' - ' . htmlentities(Config::getOsSetting($device->os, 'text'));
0 ignored issues
show
Bug introduced by
It seems like LibreNMS\Config::getOsSe...ng($device->os, 'text') can also be of type null; however, parameter $string of htmlentities() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

86
            $contents .= ' - ' . htmlentities(/** @scrutinizer ignore-type */ Config::getOsSetting($device->os, 'text'));
Loading history...
87
        }
88
89
        if ($device->version) {
90
            $contents .= ' ' . htmlentities($device->version);
91
        }
92
93
        if ($device->features) {
94
            $contents .= ' (' . htmlentities($device->features) . ')';
95
        }
96
97
        if ($device->location_id) {
98
            $contents .= ' - ' . htmlentities($device->location);
99
        }
100
101
        $contents .= '</div>';
102
103
        foreach ((array) $graphs as $entry) {
104
            $graph = isset($entry['graph']) ? $entry['graph'] : 'unknown';
105
            $graphhead = isset($entry['text']) ? $entry['text'] : 'unknown';
106
            $contents .= '<div class="overlib-box">';
107
            $contents .= '<span class="overlib-title">' . $graphhead . '</span><br />';
108
            $contents .= Url::minigraphImage($device, $start, $end, $graph);
0 ignored issues
show
Bug introduced by
It seems like $end can also be of type double and string; however, parameter $end of LibreNMS\Util\Url::minigraphImage() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

108
            $contents .= Url::minigraphImage($device, $start, /** @scrutinizer ignore-type */ $end, $graph);
Loading history...
Bug introduced by
It seems like $start can also be of type double and string; however, parameter $start of LibreNMS\Util\Url::minigraphImage() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

108
            $contents .= Url::minigraphImage($device, /** @scrutinizer ignore-type */ $start, $end, $graph);
Loading history...
109
            $contents .= Url::minigraphImage($device, Carbon::now()->subWeek()->timestamp, $end, $graph);
110
            $contents .= '</div>';
111
        }
112
113
        if ($overlib == 0) {
114
            $link = $contents;
115
        } else {
116
            $contents = self::escapeBothQuotes($contents);
117
            $link = Url::overlibLink($url, $text, $contents, $class);
118
        }
119
120
        return $link;
121
    }
122
123
    /**
124
     * @param  Port  $port
125
     * @param  string  $text
126
     * @param  string  $type
127
     * @param  bool  $overlib
128
     * @param  bool  $single_graph
129
     * @return mixed|string
130
     */
131
    public static function portLink($port, $text = null, $type = null, $overlib = true, $single_graph = false)
132
    {
133
        if ($port === null) {
134
            return $text;
135
        }
136
137
        $label = Rewrite::normalizeIfName($port->getLabel());
138
        if (! $text) {
139
            $text = $label;
140
        }
141
142
        $content = '<div class=list-large>' . addslashes(htmlentities($port->device->displayName() . ' - ' . $label)) . '</div>';
0 ignored issues
show
Bug introduced by
The method displayName() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

142
        $content = '<div class=list-large>' . addslashes(htmlentities($port->device->/** @scrutinizer ignore-call */ displayName() . ' - ' . $label)) . '</div>';

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
Bug introduced by
Are you sure $label of type array|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

142
        $content = '<div class=list-large>' . addslashes(htmlentities($port->device->displayName() . ' - ' . /** @scrutinizer ignore-type */ $label)) . '</div>';
Loading history...
143
        if ($description = $port->getDescription()) {
144
            $content .= addslashes(htmlentities($description)) . '<br />';
145
        }
146
147
        $content .= "<div style=\'width: 850px\'>";
148
        $graph_array = [
149
            'type' => $type ?: 'port_bits',
150
            'legend' => 'yes',
151
            'height' => 100,
152
            'width' => 340,
153
            'to' => Carbon::now()->timestamp,
154
            'from' => Carbon::now()->subDay()->timestamp,
155
            'id' => $port->port_id,
156
        ];
157
158
        $content .= self::graphTag($graph_array);
159
        if (! $single_graph) {
160
            $graph_array['from'] = Carbon::now()->subWeek()->timestamp;
161
            $content .= self::graphTag($graph_array);
162
            $graph_array['from'] = Carbon::now()->subMonth()->timestamp;
163
            $content .= self::graphTag($graph_array);
164
            $graph_array['from'] = Carbon::now()->subYear()->timestamp;
165
            $content .= self::graphTag($graph_array);
166
        }
167
168
        $content .= '</div>';
169
170
        if (! $overlib) {
171
            return $content;
172
        } elseif ($port->canAccess(Auth::user())) {
173
            return self::overlibLink(self::portUrl($port), $text, $content, self::portLinkDisplayClass($port));
174
        }
175
176
        return Rewrite::normalizeIfName($text);
177
    }
178
179
    /**
180
     * @param  \App\Models\Sensor  $sensor
181
     * @param  string  $text
182
     * @param  string  $type
183
     * @param  bool  $overlib
184
     * @param  bool  $single_graph
185
     * @return mixed|string
186
     */
187
    public static function sensorLink($sensor, $text = null, $type = null, $overlib = true, $single_graph = false)
188
    {
189
        $label = $sensor->sensor_descr;
190
        if (! $text) {
191
            $text = $label;
192
        }
193
194
        $content = '<div class=list-large>' . addslashes(htmlentities($sensor->device->displayName() . ' - ' . $label)) . '</div>';
0 ignored issues
show
Bug introduced by
The method displayName() does not exist on null. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

194
        $content = '<div class=list-large>' . addslashes(htmlentities($sensor->device->/** @scrutinizer ignore-call */ displayName() . ' - ' . $label)) . '</div>';

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
195
196
        $content .= "<div style=\'width: 850px\'>";
197
        $graph_array = [
198
            'type' => $type ?: 'sensor_' . $sensor->sensor_class,
199
            'legend' => 'yes',
200
            'height' => 100,
201
            'width' => 340,
202
            'to' => Carbon::now()->timestamp,
203
            'from' => Carbon::now()->subDay()->timestamp,
204
            'id' => $sensor->sensor_id,
205
        ];
206
207
        $content .= self::graphTag($graph_array);
208
        if (! $single_graph) {
209
            $graph_array['from'] = Carbon::now()->subWeek()->timestamp;
210
            $content .= self::graphTag($graph_array);
211
            $graph_array['from'] = Carbon::now()->subMonth()->timestamp;
212
            $content .= self::graphTag($graph_array);
213
            $graph_array['from'] = Carbon::now()->subYear()->timestamp;
214
            $content .= self::graphTag($graph_array);
215
        }
216
217
        $content .= '</div>';
218
219
        if (! $overlib) {
220
            return $content;
221
        }
222
223
        return self::overlibLink(self::sensorUrl($sensor), $text, $content, self::sensorLinkDisplayClass($sensor));
224
    }
225
226
    /**
227
     * @param  int|Device  $device
228
     * @param  array  $vars
229
     * @return string
230
     */
231
    public static function deviceUrl($device, $vars = [])
232
    {
233
        $routeParams = [is_numeric($device) ? $device : $device->device_id];
234
        if (isset($vars['tab'])) {
235
            $routeParams[] = $vars['tab'];
236
            unset($vars['tab']);
237
        }
238
239
        return route('device', $routeParams) . self::urlParams($vars);
240
    }
241
242
    public static function portUrl($port, $vars = [])
243
    {
244
        return self::generate(['page' => 'device', 'device' => $port->device_id, 'tab' => 'port', 'port' => $port->port_id], $vars);
245
    }
246
247
    public static function sensorUrl($sensor, $vars = [])
248
    {
249
        return self::generate(['page' => 'device', 'device' => $sensor->device_id, 'tab' => 'health', 'metric' => $sensor->sensor_class], $vars);
250
    }
251
252
    /**
253
     * @param  Port  $port
254
     * @return string
255
     */
256
    public static function portThumbnail($port)
257
    {
258
        $graph_array = [
259
            'port_id' => $port->port_id,
260
            'graph_type' => 'port_bits',
261
            'from' => Carbon::now()->subDay()->timestamp,
262
            'to' => Carbon::now()->timestamp,
263
            'width' => 150,
264
            'height' => 21,
265
        ];
266
267
        return self::portImage($graph_array);
268
    }
269
270
    /**
271
     * @param  Port  $port
272
     * @return string
273
     */
274
    public static function portErrorsThumbnail($port)
275
    {
276
        $graph_array = [
277
            'port_id' => $port->port_id,
278
            'graph_type' => 'port_errors',
279
            'from' => Carbon::now()->subDay()->timestamp,
280
            'to' => Carbon::now()->timestamp,
281
            'width' => 150,
282
            'height' => 21,
283
        ];
284
285
        return self::portImage($graph_array);
286
    }
287
288
    public static function portImage($args)
289
    {
290
        if (empty($args['bg'])) {
291
            $args['bg'] = 'FFFFFF00';
292
        }
293
294
        return '<img src="' . url('graph.php') . '?type=' . $args['graph_type'] . '&amp;id=' . $args['port_id'] . '&amp;from=' . $args['from'] . '&amp;to=' . $args['to'] . '&amp;width=' . $args['width'] . '&amp;height=' . $args['height'] . '&amp;bg=' . $args['bg'] . '">';
295
    }
296
297
    public static function generate($vars, $new_vars = [])
298
    {
299
        $vars = array_merge($vars, $new_vars);
300
301
        $url = url(Config::get('base_url', true) . $vars['page'] . '');
0 ignored issues
show
Bug introduced by
Are you sure LibreNMS\Config::get('base_url', true) of type mixed|true can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

301
        $url = url(/** @scrutinizer ignore-type */ Config::get('base_url', true) . $vars['page'] . '');
Loading history...
302
        unset($vars['page']);
303
304
        return $url . self::urlParams($vars);
305
    }
306
307
    /**
308
     * Generate url parameters to append to url
309
     * $prefix will only be prepended if there are parameters
310
     *
311
     * @param  array  $vars
312
     * @param  string  $prefix
313
     * @return string
314
     */
315
    private static function urlParams($vars, $prefix = '/')
316
    {
317
        $url = empty($vars) ? '' : $prefix;
318
        foreach ($vars as $var => $value) {
319
            if ($value == '0' || $value != '' && ! Str::contains($var, 'opt') && ! is_numeric($var)) {
320
                $url .= urlencode($var) . '=' . urlencode($value) . '/';
321
            }
322
        }
323
324
        return $url;
325
    }
326
327
    /**
328
     * @param  array  $args
329
     * @return string
330
     */
331
    public static function graphTag($args)
332
    {
333
        $urlargs = [];
334
        foreach ($args as $key => $arg) {
335
            $urlargs[] = $key . '=' . urlencode($arg);
336
        }
337
338
        return '<img src="' . url('graph.php') . '?' . implode('&amp;', $urlargs) . '" style="border:0;" />';
339
    }
340
341
    public static function graphPopup($args, $content = null, $link = null)
342
    {
343
        // Take $args and print day,week,month,year graphs in overlib, hovered over graph
344
        $original_from = $args['from'];
345
        $now = CarbonImmutable::now();
346
347
        $graph = $content ?: self::graphTag($args);
348
        $popup = '<div class=list-large>' . $args['popup_title'] . '</div>';
349
        $popup .= '<div style="width: 850px">';
350
        $args['width'] = 340;
351
        $args['height'] = 100;
352
        $args['legend'] = 'yes';
353
        $args['from'] = $now->subDay()->timestamp;
354
        $popup .= self::graphTag($args);
355
        $args['from'] = $now->subWeek()->timestamp;
356
        $popup .= self::graphTag($args);
357
        $args['from'] = $now->subMonth()->timestamp;
358
        $popup .= self::graphTag($args);
359
        $args['from'] = $now->subYear()->timestamp;
360
        $popup .= self::graphTag($args);
361
        $popup .= '</div>';
362
363
        $args['from'] = $original_from;
364
365
        $args['link'] = $link ?: self::generate($args, ['page' => 'graphs', 'height' => null, 'width' => null, 'bg' => null]);
366
367
        return self::overlibLink($args['link'], $graph, $popup, null);
368
    }
369
370
    public static function lazyGraphTag($args)
371
    {
372
        $urlargs = [];
373
374
        foreach ($args as $key => $arg) {
375
            $urlargs[] = $key . '=' . urlencode($arg);
376
        }
377
378
        $tag = '<img class="img-responsive" src="' . url('graph.php') . '?' . implode('&amp;', $urlargs) . '" style="border:0;"';
379
380
        if (Config::get('enable_lazy_load', true)) {
381
            return $tag . ' loading="lazy" />';
382
        }
383
384
        return $tag . ' />';
385
    }
386
387
    public static function overlibLink($url, $text, $contents, $class = null)
388
    {
389
        $contents = "<div class=\'overlib-contents\'>" . $contents . '</div>';
390
        $contents = str_replace('"', "\'", $contents);
391
        if ($class === null) {
392
            $output = '<a href="' . $url . '"';
393
        } else {
394
            $output = '<a class="' . $class . '" href="' . $url . '"';
395
        }
396
397
        if (Config::get('web_mouseover', true)) {
398
            $defaults = Config::get('overlib_defaults', ",FGCOLOR,'#ffffff', BGCOLOR, '#e5e5e5', BORDER, 5, CELLPAD, 4, CAPCOLOR, '#555555', TEXTCOLOR, '#3e3e3e'");
399
            $output .= " onmouseover=\"return overlib('$contents'$defaults,WRAP,HAUTO,VAUTO); \" onmouseout=\"return nd();\">";
400
        } else {
401
            $output .= '>';
402
        }
403
404
        $output .= $text . '</a>';
405
406
        return $output;
407
    }
408
409
    public static function overlibContent($graph_array, $text)
410
    {
411
        $overlib_content = '<div class=overlib><span class=overlib-text>' . $text . '</span><br />';
412
413
        $now = Carbon::now();
414
415
        foreach ([1, 7, 30, 365] as $days) {
416
            $graph_array['from'] = $now->subDays($days)->timestamp;
417
            $overlib_content .= self::escapeBothQuotes(self::graphTag($graph_array));
418
        }
419
420
        $overlib_content .= '</div>';
421
422
        return $overlib_content;
423
    }
424
425
    /**
426
     * Generate minigraph image url
427
     *
428
     * @param  Device  $device
429
     * @param  int  $start
430
     * @param  int  $end
431
     * @param  string  $type
432
     * @param  string  $legend
433
     * @param  int  $width
434
     * @param  int  $height
435
     * @param  string  $sep
436
     * @param  string  $class
437
     * @param  int  $absolute_size
438
     * @return string
439
     */
440
    public static function minigraphImage($device, $start, $end, $type, $legend = 'no', $width = 275, $height = 100, $sep = '&amp;', $class = 'minigraph-image', $absolute_size = 0)
441
    {
442
        $vars = ['device=' . $device->device_id, "from=$start", "to=$end", "width=$width", "height=$height", "type=$type", "legend=$legend", "absolute=$absolute_size"];
443
444
        return '<img class="' . $class . '" width="' . $width . '" height="' . $height . '" src="' . url('graph.php') . '?' . implode($sep, $vars) . '">';
445
    }
446
447
    /**
448
     * @param  Device  $device
449
     * @return string
450
     */
451
    private static function deviceLinkDisplayClass($device)
452
    {
453
        if ($device->disabled) {
454
            return 'list-device-disabled';
455
        }
456
457
        if ($device->ignore) {
458
            return $device->status ? 'list-device-ignored-up' : 'list-device-ignored';
459
        }
460
461
        return $device->status ? 'list-device' : 'list-device-down';
462
    }
463
464
    /**
465
     * Get html class for a port using ifAdminStatus and ifOperStatus
466
     *
467
     * @param  Port  $port
468
     * @return string
469
     */
470
    public static function portLinkDisplayClass($port)
471
    {
472
        if ($port->ifAdminStatus == 'down') {
473
            return 'interface-admindown';
474
        }
475
476
        if ($port->ifAdminStatus == 'up' && $port->ifOperStatus != 'up') {
477
            return 'interface-updown';
478
        }
479
480
        return 'interface-upup';
481
    }
482
483
    /**
484
     * Get html class for a sensor
485
     *
486
     * @param  \App\Models\Sensor  $sensor
487
     * @return string
488
     */
489
    public static function sensorLinkDisplayClass($sensor)
490
    {
491
        if ($sensor->sensor_current > $sensor->sensor_limit) {
492
            return 'sensor-high';
493
        }
494
495
        if ($sensor->sensor_current < $sensor->sensor_limit_low) {
496
            return 'sensor-low';
497
        }
498
499
        return 'sensor-ok';
500
    }
501
502
    /**
503
     * @param  string  $os
504
     * @param  string  $feature
505
     * @param  string  $icon
506
     * @param  string  $dir  directory to search in (images/os/ or images/logos)
507
     * @return string
508
     */
509
    public static function findOsImage($os, $feature, $icon = null, $dir = 'images/os/')
510
    {
511
        $possibilities = [$icon];
512
513
        if ($os) {
514
            if ($os == 'linux') {
515
                // first, prefer the first word of $feature
516
                $distro = Str::before(strtolower(trim($feature)), ' ');
517
                $possibilities[] = "$distro.svg";
518
                $possibilities[] = "$distro.png";
519
520
                // second, prefer the first two words of $feature (i.e. 'Red Hat' becomes 'redhat')
521
                if (strpos($feature, ' ') !== false) {
522
                    $distro = Str::replaceFirst(' ', '', strtolower(trim($feature)));
523
                    $distro = Str::before($distro, ' ');
524
                    $possibilities[] = "$distro.svg";
525
                    $possibilities[] = "$distro.png";
526
                }
527
            }
528
            $os_icon = Config::getOsSetting($os, 'icon', $os);
529
            $possibilities[] = "$os_icon.svg";
530
            $possibilities[] = "$os_icon.png";
531
        }
532
533
        foreach ($possibilities as $file) {
534
            if (is_file(Config::get('html_dir') . "/$dir" . $file)) {
535
                return $file;
536
            }
537
        }
538
539
        // fallback to the generic icon
540
        return 'generic.svg';
541
    }
542
543
    /**
544
     * parse a legacy path (one without ? or &)
545
     *
546
     * @param  string  $path
547
     * @return ParameterBag
548
     */
549
    public static function parseLegacyPath($path)
550
    {
551
        $parts = array_filter(explode('/', $path), function ($part) {
552
            return Str::contains($part, '=');
553
        });
554
555
        $vars = [];
556
        foreach ($parts as $part) {
557
            [$key, $value] = explode('=', $part);
558
            $vars[$key] = $value;
559
        }
560
561
        return new ParameterBag($vars);
562
    }
563
564
    private static function escapeBothQuotes($string)
565
    {
566
        return str_replace(["'", '"'], "\'", $string);
567
    }
568
}
569