send_template_email()   F
last analyzed

Complexity

Conditions 18
Paths 309

Size

Total Lines 116
Code Lines 51

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 342

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 18
eloc 51
nc 309
nop 5
dl 0
loc 116
rs 2.6708
c 3
b 0
f 0
ccs 0
cts 53
cp 0
crap 342

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
/*
4
General utility functions v1.1 (well, it was).
5
6
*/
7
8
include_once INCLUDESPATH . '../../commonlib/phplib/email.php';
9
include_once INCLUDESPATH . '../../commonlib/phplib/datetime.php';
10
include_once INCLUDESPATH . '../../commonlib/phplib/validate.php';
11
use PHPMailer\PHPMailer\PHPMailer;
12
13
# Pass it a brief header word and some debug text and it'll be output.
14
# If TEXT is an array, call the user function, assuming it's a class.
15
function twfy_debug($header, $text = "") {
16
17
    // We set ?DEBUGTAG=n in the URL.
18
    // (DEBUGTAG is set in config.php).
19
    // n is a number from (currently) 1 to 4.
20
    // This sets what amount of debug information is shown.
21
    // For level '1' we show anything that is passed to this function
22
    // with a $header in $levels[1].
23
    // For level '2', anything with a $header in $levels[1] AND $levels[2].
24
    // Level '4' shows everything.
25
26 92
    $debug_level = get_http_var(DEBUGTAG);
27
    #$debug_level = 1;
28
29 92
    if ($debug_level != '') {
30
31
        // Set which level shows which types of debug info.
32
        $levels =  [
33
            1 =>  ['THEUSER', 'TIME', 'SQLERROR', 'PAGE', 'TEMPLATE', 'SEARCH', 'ALERTS', 'MP'],
34
            2 =>  ['SQL', 'EMAIL', 'WIKIPEDIA', 'hansardlist', 'debatelist', 'wranslist', 'whalllist'],
35
            3 =>  ['SQLRESULT'],
36
            // Higher than this: 'DATA', etc.
37
        ];
38
39
        // Store which headers we are allowed to show.
40
        $allowed_headers = [];
41
42
        if ($debug_level > count($levels)) {
43
            $max_level_to_show = count($levels);
44
        } else {
45
            $max_level_to_show = $debug_level;
46
        }
47
48
        for ($n = 1; $n <= $max_level_to_show; $n++) {
49
            $allowed_headers = array_merge($allowed_headers, $levels[$n]);
50
        }
51
52
        // If we can show this header, then, er, show it.
53
        if (in_array($header, $allowed_headers) || $debug_level >= 4) {
54
            if (is_array($text)) {
55
                $text = call_user_func($text);
56
            }
57
            print "<p><span style=\"color:#039;\"><strong>$header</strong></span> $text</p>\n";
58 92
        }
59
    }
60
}
61
62
function exception_handler($e) {
63
    trigger_error($e->getMessage(), E_USER_ERROR);
64
}
65
66
function error_handler($errno, $errmsg, $filename, $linenum, $vars) {
0 ignored issues
show
Unused Code introduced by
The parameter $vars is not used and could be removed. ( Ignorable by Annotation )

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

66
function error_handler($errno, $errmsg, $filename, $linenum, /** @scrutinizer ignore-unused */ $vars) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
67
    // Custom error-handling function.
68
    // Sends an email to BUGSLIST.
69
    global $PAGE;
70
71
    # Ignore errors we've asked to ignore
72
    if (error_reporting() == 0) {
73
        return;
74
    }
75
76
    // define an assoc array of error string
77
    // in reality the only entries we should
78
    // consider are E_WARNING, E_NOTICE, E_USER_ERROR,
79
    // E_USER_WARNING and E_USER_NOTICE
80
    # Commented out are ones that a user function cannot handle.
81
    $errortype =  [
82
        #E_ERROR            => "Error",
83
        E_WARNING           => "Warning",
84
        #E_PARSE            => "Parsing Error",
85
        E_NOTICE            => "Notice",
86
        #E_CORE_ERROR       => "Core Error",
87
        #E_CORE_WARNING     => "Core Warning",
88
        #E_COMPILE_ERROR    => "Compile Error",
89
        #E_COMPILE_WARNING  => "Compile Warning",
90
        E_USER_ERROR        => "User Error",
91
        E_USER_WARNING      => "User Warning",
92
        E_USER_NOTICE       => "User Notice",
93
        E_STRICT            => "Runtime Notice",
94
        # 5.3 introduced E_DEPRECATED
95
        8192                => 'Deprecated',
96
    ];
97
98
    $err = '';
99
    if (isset($_SERVER['REQUEST_URI'])) {
100
        $err .= "URL:\t\thttps://" . DOMAIN . $_SERVER['REQUEST_URI'] . "\n";
101
    } else {
102
        $err .= "URL:\t\tNone - running from command line?\n";
103
    }
104
    if (isset($_SERVER['HTTP_REFERER'])) {
105
        $err .= "Referer:\t" . $_SERVER['HTTP_REFERER'] . "\n";
106
    } else {
107
        $err .= "Referer:\tNone\n";
108
    }
109
    if (isset($_SERVER['HTTP_USER_AGENT'])) {
110
        $err .= "User-Agent:\t" . $_SERVER['HTTP_USER_AGENT'] . "\n";
111
    } else {
112
        $err .= "User-Agent:\tNone\n";
113
    }
114
    $err .= "Number:\t\t$errno\n";
115
    $err .= "Type:\t\t" . $errortype[$errno] . "\n";
116
    $err .= "Message:\t$errmsg\n";
117
    $err .= "File:\t\t$filename\n";
118
    $err .= "Line:\t\t$linenum\n";
119
    if (count($_POST)) {
120
        $err .= "_POST:";
121
        foreach ($_POST as $k => $v) {
122
            $err .= "\t\t$k => $v\n";
123
        }
124
    }
125
126
    // I'm not sure this bit is actually any use!
127
128
    // set of errors for which a var trace will be saved.
129
    //  $user_errors = array(E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE);
130
    //  if (in_array($errno, $user_errors)) {
131
    //      $err .= "Variables:\t" . serialize($vars) . "\n";
132
    //  }
133
134
135
    // Add the problematic line if possible.
136
    if (is_readable($filename)) {
137
        $source = file($filename);
138
        $err .= "\nSource:\n\n";
139
        // Show the line, plus prev and next, with line numbers.
140
        $err .= $linenum - 2 . " " . $source[$linenum - 3];
141
        $err .= $linenum - 1 . " " . $source[$linenum - 2];
142
        $err .= $linenum . " " . $source[$linenum - 1];
143
        $err .= $linenum + 1 . " " . $source[$linenum];
144
        $err .= $linenum + 2 . " " . $source[$linenum + 1];
145
    }
146
147
148
    // Will we need to exit after this error?
149
    $fatal_errors = [E_ERROR, E_USER_ERROR];
150
    if (in_array($errno, $fatal_errors)) {
151
        $fatal = true;
152
    } else {
153
        $fatal = false;
154
    }
155
156
    // Finally, display errors and stuff...
157
158
    if (DEVSITE || get_http_var(DEBUGTAG)) {
159
        // On a devsite we just display the problem.
160
        $errtxt = nl2br($err) . "\n";
161
        if (!strstr($errmsg, 'mysql_connect')) {
162
            $errtxt .= "<br><br>Backtrace:<br>" . nl2br(adodb_backtrace(false));
163
        }
164
        $message = [
165
            'title' => "Error",
166
            'text' => $errtxt,
167
        ];
168
        if (is_object($PAGE)) {
169
            $PAGE->error_message($message, $fatal);
170
        } else {
171
            vardump($message);
172
        }
173
174
    } else {
175
        // On live sites we display a nice message and email the problem.
176
177
        $message = [
178
            'title' => gettext("Sorry, an error has occurred"),
179
            'text' => gettext("We've been notified by email and will try to fix the problem soon!"),
180
        ];
181
182
        if (is_object($PAGE)) {
183
            $PAGE->error_message($message, $fatal);
184
        } else {
185
            header('HTTP/1.0 500 Internal Server Error');
186
            print "<p>Oops, sorry, an error has occurred!</p>\n";
187
        }
188
        if (!($errno & E_USER_NOTICE) && strpos($errmsg, 'pg_connect') === false && strpos($errmsg, 'mysql_connect') === false) {
189
            mail(BUGSLIST, "[TWFYBUG]: $errmsg", $err, "From: Bug <" . CONTACTEMAIL . ">\n" . "X-Mailer: PHP/" . phpversion());
190
        }
191
    }
192
193
    // Do we need to exit?
194
    if ($fatal) {
195
        exit(1);
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
196
    }
197
}
198
199
// Replacement for var_dump()
200
function vardump($blah) {
201
    print "<pre>\n";
202
    var_dump($blah);
203
    print "</pre>\n";
204
}
205
206
// pretty prints the backtrace, copied from http://uk.php.net/manual/en/function.debug-backtrace.php
207
function adodb_backtrace($print = true) {
208
    $s = '';
209
    if (PHPVERSION() >= 4.3) {
210
211
        $MAXSTRLEN = 64;
212
213
        $traceArr = debug_backtrace();
214
        array_shift($traceArr);
215
        $tabs = sizeof($traceArr) - 1;
216
        foreach ($traceArr as $arr) {
217
            for ($i = 0; $i < $tabs; $i++) {
218
                $s .= ' &nbsp; ';
219
            }
220
            $tabs -= 1;
221
            if (isset($arr['class'])) {
222
                $s .= $arr['class'] . '.';
223
            }
224
            $args = [];
225
            if (isset($arr['args'])) {
226
                foreach ($arr['args'] as $v) {
227
                    if (is_null($v)) {
228
                        $args[] = 'null';
229
                    } elseif (is_array($v)) {
230
                        $args[] = 'Array[' . sizeof($v) . ']';
231
                    } elseif (is_object($v)) {
232
                        $args[] = 'Object:' . get_class($v);
233
                    } elseif (is_bool($v)) {
234
                        $args[] = $v ? 'true' : 'false';
235
                    } else {
236
                        $v = (string) @$v;
237
                        $str = _htmlspecialchars(substr($v, 0, $MAXSTRLEN));
238
                        if (strlen($v) > $MAXSTRLEN) {
239
                            $str .= '...';
240
                        }
241
                        $args[] = $str;
242
                    }
243
                }
244
            }
245 92
246 92
            $s .= $arr['function'] . '(' . implode(', ', $args) . ')';
247 92
            //      $s .= sprintf("</font><font color=#808080 size=-1> # line %4d,".
248
            //            " file: <a href=\"file:/%s\">%s</a></font>",
249 92
            //        $arr['line'],$arr['file'],$arr['file']);
250
            $s .= "\n";
251
        }
252
        if ($print) {
253
            print $s;
254
        }
255
    }
256
257
    return $s;
258
}
259
260
// Returns the unixtime in microseconds.
261
function getmicrotime() {
262
    $mtime = microtime();
263
    $mtime = explode(" ", $mtime);
264
    $mtime = $mtime[1] + $mtime[0];
265
266
    return $mtime;
267
}
268
269
/* twfy_debug_timestamp
270
 * Output a timestamp since the page was started. */
271
$timestamp_last = $timestamp_start = getmicrotime();
272
function twfy_debug_timestamp($label = "") {
273
    global $timestamp_last, $timestamp_start;
274
    $t = getmicrotime();
275
    twfy_debug("TIME", sprintf(
276
        "%f msecs since start; %f msecs since last; %s",
277
        ($t - $timestamp_start) * 1000.0,
278
        ($t - $timestamp_last) * 1000.0,
279
        $label
280
    ));
281
    $timestamp_last = $t;
282
}
283
284
function format_timestamp($timestamp, $format) {
285 37
    // Pass it a MYSQL TIMESTAMP (YYYYMMDDHHMMSS) and a
286
    // PHP date format string (eg, "Y-m-d H:i:s")
287
    // and it returns a nicely formatted string according to requirements.
288
289
    // Because strtotime can't handle TIMESTAMPS.
290 37
291 37
    if (preg_match("/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/", $timestamp, $matches)) {
292 37
        [$string, $year, $month, $day, $hour, $min, $sec] = $matches;
293
294
        return strftime($format, mktime($hour, $min, $sec, $month, $day, $year));
295
    } else {
296
        return "";
297
    }
298
299
}
300 37
301
302
$format_date_months = ['', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'];
303
$format_date_months_short = ['', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
304
305
function format_date($date, $format) {
306
    global $format_date_months, $format_date_months_short;
307
    // Pass it a date (YYYY-MM-DD) and a
308
    // PHP date format string (eg, "Y-m-d H:i:s")
309
    // and it returns a nicely formatted string according to requirements.
310
311
    if (preg_match("/^(\d\d\d\d)-(\d\d?)-(\d\d?)$/", $date, $matches)) {
312
        [$string, $year, $month, $day] = $matches;
313
        if ($year < 1902) { # gmdate fns only go back to Dec. 1901
314
            if ($format == SHORTDATEFORMAT) {
315
                return ($day + 0) . ' ' . $format_date_months_short[$month + 0] . " $year";
316
            } else {
317
                return ($day + 0) . ' ' . $format_date_months[$month + 0] . " $year";
318
            }
319
        }
320
321
        return strftime($format, mktime(0, 0, 0, $month, $day, $year));
322
    } else {
323
        return "";
324
    }
325
326
}
327
328
329
function format_time($time, $format) {
330
    // Pass it a time (HH:MM:SS) and a
331
    // PHP date format string (eg, "H:i")
332
    // and it returns a nicely formatted string according to requirements.
333
334
    if (preg_match("/^(\d\d):(\d\d):(\d\d)$/", $time, $matches)) {
335
        [$string, $hour, $min, $sec] = $matches;
336
337
        return strftime($format, mktime($hour, $min, $sec));
338
    } else {
339
        return "";
340
    }
341
}
342
343
344
345
function relative_time($datetime) {
346
    // Pass it a 'YYYY-MM-DD HH:MM:SS' and it will return something
347
    // like "Two hours ago", "Last week", etc.
348
349
    // http://maniacalrage.net/projects/relative/
350
351
    if (!preg_match("/\d\d\d\d-\d\d-\d\d \d\d\:\d\d\:\d\d/", $datetime)) {
352
        return '';
353
    }
354
355
    $in_seconds = strtotime($datetime);
356
    $now = time();
357
358
    $diff   =  $now - $in_seconds;
359
    $months =  floor($diff / 2419200);
360
    $diff   -= $months * 2419200;
361
    $weeks  =  floor($diff / 604800);
362
    $diff   -= $weeks * 604800;
363
    $days   =  floor($diff / 86400);
364
    $diff   -= $days * 86400;
365
    $hours  =  floor($diff / 3600);
366
    $diff   -= $hours * 3600;
367
    $minutes = floor($diff / 60);
368
    $diff   -= $minutes * 60;
369
    $seconds = $diff;
370
371
372
    if ($months > 0) {
373
        // Over a month old, just show the actual date.
374
        $date = substr($datetime, 0, 10);
375
        return format_date($date, LONGDATEFORMAT);
376
377
    } else {
378
        $relative_date = '';
379
        if ($weeks > 0) {
380
            // Weeks and days
381
            $relative_date .= ($relative_date ? ', ' : '') . $weeks . ' week' . ($weeks > 1 ? 's' : '');
382
            $relative_date .= $days > 0 ? ($relative_date ? ', ' : '') . $days . ' day' . ($days > 1 ? 's' : '') : '';
383
        } elseif ($days > 0) {
384
            // days and hours
385
            $relative_date .= ($relative_date ? ', ' : '') . $days . ' day' . ($days > 1 ? 's' : '');
386
            $relative_date .= $hours > 0 ? ($relative_date ? ', ' : '') . $hours . ' hour' . ($hours > 1 ? 's' : '') : '';
387
        } elseif ($hours > 0) {
388
            // hours and minutes
389
            $relative_date .= ($relative_date ? ', ' : '') . $hours . ' hour' . ($hours > 1 ? 's' : '');
390
            $relative_date .= $minutes > 0 ? ($relative_date ? ', ' : '') . $minutes . ' minute' . ($minutes > 1 ? 's' : '') : '';
391
        } elseif ($minutes > 0) {
392
            // minutes only
393
            $relative_date .= ($relative_date ? ', ' : '') . $minutes . ' minute' . ($minutes > 1 ? 's' : '');
394
        } else {
395
            // seconds only
396
            $relative_date .= ($relative_date ? ', ' : '') . $seconds . ' second' . ($seconds > 1 ? 's' : '');
397
        }
398
    }
399
400 8
    // Return relative date and add proper verbiage
401
    return $relative_date . ' ago';
402 8
403
}
404
405
function parse_date($date) {
406
    return datetime_parse_local_date($date, time(), 'en', 'gb');
407
}
408
409
function slugify($text) {
410
    $text = preg_replace('#[^\w]+#', '-', $text);
411
    $text = trim($text, '-');
412
    $text = preg_replace('#-+#', '-', $text);
413
    $text = strtolower($text);
414
    return $text;
415 8
}
416
417
/* strip_tags_tospaces TEXT
418
 * Return a copy of TEXT in which certain block-level HTML tags have been
419 8
 * replaced by single spaces, and other HTML tags have been removed. */
420
function strip_tags_tospaces($text) {
421
    $text = preg_replace("#\<(p|br|div|td|tr|th|table)[^>]*\>#i", " ", $text);
422 8
423
    return strip_tags(trim($text));
424
}
425 8
426
function trim_characters($text, $start, $length, $url_length = 60) {
427
    // Pass it a string, a numeric start position and a numeric length.
428
    // If the start position is > 0, the string will be trimmed to start at the
429
    // nearest word boundary after (or at) that position.
430
    // If the string is then longer than $length, it will be trimmed to the nearest
431
    // word boundary below (or at) that length.
432
    // If either end is trimmed, ellipses will be added.
433
    // The modified string is then returned - its *maximum* length is $length.
434
    // HTML is always stripped (must be for trimming to prevent broken tags).
435
436
    $text = strip_tags_tospaces($text);
437
438 8
    // Split long strings up so they don't go too long.
439
    // Mainly for URLs which are displayed, but aren't links when trimmed.
440
    $text = preg_replace('/(\S{' . $url_length . '})/', "\$1 ", $text);
441 8
442
    // Otherwise the word boundary matching goes odd...
443
    $text = preg_replace("/[\n\r]/", " ", $text);
444 8
445 8
    // Trim start.
446
    if ($start > 0) {
447 8
        $text = substr($text, $start);
448
449
        // Word boundary.
450
        if (preg_match("/.+?\b(.*)/", $text, $matches)) {
451
            $text = $matches[1];
452 8
            // Strip spare space at the start.
453
            $text = preg_replace("/^\s/", '', $text);
454
        }
455 8
        $text = '...' . $text;
456
    }
457
458
    // Trim end.
459
    if (strlen($text) > $length) {
460
461
        // Allow space for ellipsis.
462
        $text = substr($text, 0, $length - 3);
463
464
        // Word boundary.
465
        if (preg_match("/(.*)\s.+/", $text, $matches)) {
466
            $text = $matches[1];
467
            // Strip spare space at the end.
468
            $text = preg_replace("/\s$/", '', $text);
469
        }
470 1
        // We don't want to use the HTML entity for an ellipsis (&#8230;), because then
471
        // it screws up when we subsequently use htmlentities() to print the returned
472 1
        // string!
473
        $text .= '...';
474
    }
475
476
    return $text;
477 1
}
478
479
/**
480
 * Filters user input to remove unwanted HTML tags etc
481
 */
482
function filter_user_input($text, $filter_type) {
483 1
    // We use this to filter any major user input, especially comments.
484
    // Gets rid of bad HTML, basically.
485
    // Uses iamcal.com's lib_filter class.
486
487
    // $filter_type is the level of filtering we want:
488
    //      'comment' allows <b> and <i> tags.
489
    //      'strict' strips all tags.
490
491
    global $filter;
492 1
493
    $text = trim($text);
494
495 1
    // Replace 3 or more newlines with just two newlines.
496
    //$text = preg_replace("/(\n){3,}/", "\n\n", $text);
497 1
498
    if ($filter_type == 'strict') {
499
        // No tags allowed at all!
500
        $filter->allowed =  [];
501
    } else {
502
        // Comment.
503
        // Only allowing <a href>, <b>, <strong>, <i> and <em>
504
        $filter->allowed =  [
505
            'a' => ['href'],
506
            'strong' => [],
507 3
            'em' => [],
508
            'b' => [],
509 3
            'i' => [],
510 3
        ];
511 3
        // turning this on means that stray angle brackets
512
        // are not turned in to tags
513 2
        $filter->always_make_tags = 0;
514 1
    }
515
516 1
    $text = $filter->go($text);
517
518 3
    return $text;
519 3
}
520 3
521 3
function prepare_comment_for_display($text) {
522 3
    // Makes any URLs into HTML links.
523
    // Turns \n's into <br>
524 3
525
    // Encode HTML entities.
526
    // Can't do htmlentities() because it'll turn the few tags we allow into &lt;
527
    // Must go before the URL stuff.
528
    $text = htmlentities_notags($text);
529
530
    $link_length = 60;
531 3
    $text = preg_replace_callback(
532
        "/(?<!\"|\/)((http(s?):\/\/)|(www\.))([a-zA-Z\d_.+,;:?%~\-\/#='*$!()&[\]]+)([a-zA-Z\d_?%~\-\/#='*$!&])/",
533
        function ($matches) use ($link_length) {
534
            if (strlen($matches[0]) > $link_length) {
535
                return '<a href="' . $matches[0] . '" rel="nofollow">' . substr($matches[0], 0, $link_length) . "...</a>";
536
            } else {
537
                return '<a href="' . $matches[0] . '" rel="nofollow">' . $matches[0] . '</a>';
538
            }
539
        },
540
        $text
541
    );
542
    $text = str_replace('<a href="www', '<a href="https://www', $text);
543
    $text = preg_replace("/([\w\.]+)(@)([\w\.\-]+)/i", "<a href=\"mailto:$0\">$0</a>", $text);
544
    $text = str_replace("\n", "<br>\n", $text);
545
546
    return $text;
547 3
}
548 3
549
function htmlentities_notags($text) {
550
    // If you want to do htmlentities() on some text that has HTML tags
551 3
    // in it, then you need this function.
552 3
553 3
    $tbl = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES, 'UTF-8');
554 3
555
    // You could encode extra stuff...
556
    //$tbl["“"] = "&quot;";
557 3
    //$tbl["”"] = "&quot;";
558
    //$tbl["…"] = "...";
559 3
    //$tbl["—"] = "-";
560
    //$tbl["»"] = "&raquo;";
561
    //$tbl["«"] = "&laquo;";
562
563
    // lib_filter will replace unmatched < and > with entities so
564
    // we abuse strtr's only replace once behaviour to not double
565
    // encode them. May not be robust.
566
    // This does mean if anyone actually wants to put &gt; or &lt;
567
    // in a comment they can't but that's a lot less likely than
568 8
    // < or > for less than and greater than.
569
    $tbl['&lt;'] = "&lt;";
570
    $tbl['&gt;'] = "&gt;";
571
572
    // Don't want to encode these things
573
    unset($tbl["<"]);
574
    unset($tbl[">"]);
575
    unset($tbl["'"]);
576
    unset($tbl['"']);
577
578
    # strtr "will *NOT* try to replace stuff that it has already worked on."
579
    $text = strtr($text, $tbl);
580
581
    return $text;
582
}
583
584
/*
585
 * PHP 5.4 changes the default encoding for htmlentities and htmlspecialchars
586
 * to be UTF-8, not using the php.ini character encoding until PHP 5.6. So
587
 * we have to wrap all uses of these two functions.
588
 */
589
function _htmlentities($s) {
590
    return htmlentities($s, ENT_COMPAT, 'UTF-8');
591
}
592
function _htmlspecialchars($s) {
593
    return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
594
}
595
596 4
function get_canonical_gid($gid) {
597 4
    $db = new ParlDB();
598
    $might_be_redirected = true;
599
    while ($might_be_redirected) {
600
        $q = $db->query("SELECT gid_to FROM gidredirect WHERE gid_from = :gid", [':gid' => $gid])->first();
601
        if ($q) {
602
            $gid = $q['gid_to'];
603
        } else {
604
            $might_be_redirected = false;
605
        }
606
    }
607
608
    return $gid;
609
}
610
611
612
function fix_gid_from_db($gid) {
613
    // The gids in the database are longer than we use in the site.
614
    // Feed this a gid from the db and it will be returned truncated.
615 1
616
    // $gid will be like 'uk.org.publicwhip/debate/2003-02-28.475.3'.
617
618
    $newgid = substr($gid, strrpos($gid, '/') + 1);
619
    return $newgid;
620
}
621
622
function gid_to_anchor($gid) {
623
    // For trimming gids to be used as #anchors in pages.
624
    // Extracted here so we keep it consistent.
625
    // The gid should already be truncated using fix_gid_from_db(), so it
626
    // will be like 2003-11-20.966.0
627
    // This function returns 966.0
628
629
    return substr($gid, (strpos($gid, '.') + 1));
630
}
631
632
function preg_replacement_quote($s) {
633
    // This returns $s but with every $ and \ backslash-escaped.
634
    // This is to create a string that can be safely used in a
635
    // preg_replace replacement string.  This function was suggested here:
636
    // http://www.procata.com/blog/archives/2005/11/13/two-preg_replace-escaping-gotchas/
637
    return preg_replace('/(\$|\\\\)(?=\d)/', '\\\\\1', $s);
638
}
639
640
function send_template_email($data, $merge, $bulk = false, $want_bounces = false, $lang = '') {
0 ignored issues
show
Unused Code introduced by
The parameter $lang is not used and could be removed. ( Ignorable by Annotation )

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

640
function send_template_email($data, $merge, $bulk = false, $want_bounces = false, /** @scrutinizer ignore-unused */ $lang = '') {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
641
    // We should have some email templates in INCLUDESPATH/easyparliament/templates/emails/.
642
643
    // $data is like:
644
    // array (
645
    //  'template'  => 'send_confirmation',
646
    //  'to'        => '[email protected]',
647
    //  'subject'   => 'Your confirmation email'
648
    // );
649
650
    // $merge is like:
651
    // array (
652
    //  'FIRSTNAME' => 'Phil',
653
    //  'LATNAME'   => 'Gyford'
654
    //  etc...
655
    // );
656
657
    // In $data, 'template' and 'to' are mandatory. 'template' is the
658
    // name of the file (when it has '.txt' added to it).
659
660
    // We'll get the text of the template and replace all the $merge
661
    // keys with their tokens. eg, if '{FIRSTNAME}' in the template will
662
    // be replaced with 'Phil'.
663
664
    // Additionally, the first line of a template may start with
665
    // 'Subject:'. Any text immediately following that, on the same line
666
    // will be the subject of the email (it will also have its tokens merged).
667
    // But this subject can be overridden by sending including a 'subject'
668
    // pair in $data.
669
670
    global $PAGE;
671
672
    if (!isset($data['to']) || $data['to'] == '') {
673
        $PAGE->error_message("We need an email address to send to.");
674
        return false;
675
    }
676
677
    $lang = '';
678
    if ($lang == 'cy' || LANGUAGE == 'cy') {
0 ignored issues
show
introduced by
The condition LANGUAGE == 'cy' is always false.
Loading history...
679
        $lang = 'cy/';
680
    }
681
682
    $filename = INCLUDESPATH . "easyparliament/templates/emails/$lang" . $data['template'] . ".txt";
683
    if (!file_exists($filename) && $lang == 'cy/') {
0 ignored issues
show
introduced by
The condition $lang == 'cy/' is always false.
Loading history...
684
        $filename = INCLUDESPATH . "easyparliament/templates/emails/" . $data['template'] . ".txt";
685
    }
686
687
    if (!file_exists($filename)) {
688
        $PAGE->error_message("Sorry, we could not find the email template '" . _htmlentities($data['template']) . "'.");
689
        return false;
690
    }
691
692
    // Get the text from the template.
693
    $handle = fopen($filename, "r");
694
    $emailtext = fread($handle, filesize($filename));
695
    fclose($handle);
696
697
    $filename = INCLUDESPATH . "easyparliament/templates/emails/$lang" . $data['template'] . ".html";
698
    if (!file_exists($filename) && $lang == 'cy/') {
0 ignored issues
show
introduced by
The condition $lang == 'cy/' is always false.
Loading history...
699
        $filename = INCLUDESPATH . "easyparliament/templates/emails/" . $data['template'] . ".html";
700
    }
701
702
    if (file_exists($filename)) {
703
        $htmltext = file_get_contents($filename);
704
    } else {
705
        $htmltext = '';
706
    }
707
708
    // See if there's a default subject in the template.
709
    $firstline = substr($emailtext, 0, strpos($emailtext, "\n"));
710
711
    // Work out what the subject line is.
712
    if (preg_match("/Subject:/", $firstline)) {
713
        if (isset($data['subject'])) {
714
            $subject = trim($data['subject']);
715
        } else {
716
            $subject = trim(substr($firstline, 8));
717
        }
718
719
        // Either way, remove this subject line from the template.
720
        $emailtext = substr($emailtext, strpos($emailtext, "\n"));
721
722
    } elseif (isset($data['subject'])) {
723
        $subject = $data['subject'];
724
    } else {
725
        $PAGE->error_message("We don't have a subject line for the email, so it wasn't sent.");
726
        return false;
727
    }
728
729
730
    // Now merge all the tokens from $merge into $emailtext...
731
    $search = [];
732
    $replace = [];
733
734
    foreach ($merge as $key => $val) {
735
        $search[] = '/{' . $key . '}/';
736
        $replace[] = preg_replacement_quote($val);
737
    }
738
739
    $emailtext = preg_replace($search, $replace, $emailtext);
740
    $htmltext = preg_replace($search, $replace, $htmltext);
741
742
    if ($lang == 'cy' || LANGUAGE == 'cy') {
0 ignored issues
show
introduced by
The condition LANGUAGE == 'cy' is always false.
Loading history...
743
        if (strpos(DOMAIN, 'www') !== false) {
744
            $repl = "https://" . str_replace('www.', 'cy.', DOMAIN);
745
        } else {
746
            $repl = "https://cy." . DOMAIN;
747
        }
748
        $emailtext = str_replace('https://' . DOMAIN, $repl, $emailtext);
749
        $htmltext = str_replace('https://' . DOMAIN, $repl, $htmltext);
750
    }
751
752
    // Send it!
753
    $success = send_email($data['to'], $subject, $emailtext, $bulk, 'twfy-DO-NOT-REPLY@' . EMAILDOMAIN, $want_bounces, $htmltext);
754
755
    return $success;
756
757
}
758
759
/* verp_envelope_sender RECIPIENT
760
 * Construct a VERP envelope sender for an email to RECIPIENT
761
 */
762
function twfy_verp_envelope_sender($recipient) {
763
    $envelope_sender = verp_envelope_sender($recipient, 'twfy', EMAILDOMAIN);
764
765
    return $envelope_sender;
766
}
767
768
function send_email($to, $subject, $message, $bulk = false, $from = '', $want_bounces = false, $html = '') {
769
    // Use this rather than PHP's mail() direct, so we can make alterations
770
    // easily to all the emails we send out from the site.
771
    // eg, we might want to add a .sig to everything here...
772
773
    if (!$from) {
774
        $from = CONTACTEMAIL;
775
    }
776
777
    $mail = new PHPMailer(true);
778
    $mail->isSMTP();
779
    $mail->SMTPAutoTLS = false;
780
    $mail->setFrom($from, 'TheyWorkForYou');
781
    $mail->addAddress($to);
782
    $mail->Subject = $subject;
783
    $mail->CharSet = 'utf-8';
784
    $mail->Host = OPTION_SMTP_SMARTHOST;
0 ignored issues
show
Bug introduced by
The constant OPTION_SMTP_SMARTHOST was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
785
    $mail->Port = OPTION_SMTP_PORT;
0 ignored issues
show
Bug introduced by
The constant OPTION_SMTP_PORT was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
786
787
    if ($html) {
788
        $mail->msgHTML($html, INCLUDESPATH . 'easyparliament/templates/emails');
789
        $mail->AltBody = $message;
790
    } else {
791
        $mail->Body = $message;
792
    }
793 92
794 92
    if ($bulk) {
795
        $mail->addCustomHeader('Precedence', 'bulk');
796
        $mail->addCustomHeader('Auto-Submitted', 'auto-generated');
797 92
    }
798
799
    if ($want_bounces) {
800 92
        $mail->Sender = twfy_verp_envelope_sender($to);
801
    }
802
803 92
    twfy_debug('EMAIL', "Sending email to $to with subject of '$subject'");
804
    try {
805
        $mail->send();
806
        return true;
807
    } catch (Exception $e) {
808 3
        return false;
809 3
    }
810
}
811 3
812
813
///////////////////////////////
814
// Cal's functions from
815
// http://www.iamcal.com/publish/article.php?id=13
816
817
// Call this with a key name to get a GET or POST variable.
818
function get_http_var($name, $default = '', $allow_array = false) {
819
    $val = $default;
820
    if (array_key_exists($name, $_POST)) {
821
        $val = $_POST[$name];
822
    }
823
    if (array_key_exists($name, $_GET)) {
824
        $val = $_GET[$name];
825
    }
826
    if (is_array($val) && !$allow_array) {
827
        $val = $val[0];
828
    }
829
    return $val;
830
}
831
832
// Call this with a key name to get a COOKIE variable.
833
function get_cookie_var($name, $default = '') {
834
    if (array_key_exists($name, $_COOKIE)) {
835
        return $_COOKIE[$name];
836
    }
837
    return $default;
838
}
839
///////////////////////////////
840
841
// Pass it an array of key names that should not be generated as
842
// hidden form variables. It then outputs hidden form variables
843
// based on the session_vars for this page.
844
function hidden_form_vars($omit = []) {
845
    global $DATA, $this_page;
846
847
    $session_vars = $DATA->page_metadata($this_page, "session_vars");
848
849
    foreach ($session_vars as $n => $key) {
850
        if (!in_array($key, $omit)) {
851
            print "<input type=\"hidden\" name=\"$key\" value=\"" . _htmlentities(get_http_var($key)) . "\">\n";
852
        }
853
    }
854
}
855
856
// Deprecated. Use hidden_form_vars, above, instead.
857
function hidden_vars($omit = []) {
858
    global $DATA;
859
860
    foreach ($args as $key => $val) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $args seems to be never defined.
Loading history...
861
        if (!in_array($key, $omit)) {
862
            print "<input type=\"hidden\" name=\"$key\" value=\"" . _htmlspecialchars($val) . "\">\n";
863
        }
864
    }
865
}
866
867
function make_ranking($rank) {
868
    $rank = $rank + 0;
869
870
    # 11th, 12th, 13th use "th" not "st", "nd", "rd"
871
    if (floor(($rank % 100) / 10) == 1) {
872
        return $rank . "th";
873
    }
874
    # 1st
875
    if ($rank % 10 == 1) {
876
        return $rank . "st";
877
    }
878
    # 2nd
879
    if ($rank % 10 == 2) {
880
        return $rank . "nd";
881
    }
882
    # 3rd
883 10
    if ($rank % 10 == 3) {
884 1
        return $rank . "rd";
885 1
    }
886
    # Everything else use th
887
888
    return $rank . "th";
889
}
890
891 9
function make_plural($word, $number) {
892 9
    if ($number == 1) {
893 9
        return $word;
894 9
    }
895
    return $word . "s";
896 9
}
897
898
# Can't have the entities in XML so replace all theones we currently have with numerical entities
899 9
# This is yucky. XXX
900
function entities_to_numbers($string) {
901 9
    $string = str_replace(
902
        ['&Ouml;', '&acirc;', '&uacute;', '&aacute;', '&iacute;', '&ocirc;', '&eacute;'],
903
        ['&#214;', '&#226;',  '&#250;',   '&#225;',   '&#237;',   '&#244;',  '&#233;'  ],
904
        $string
905 9
    );
906
    return $string;
907
}
908 9
909
function make_member_url($name, $const = '', $house = HOUSE_TYPE_COMMONS, $pid = null) {
910 3
911
    // Case for Elizabeth II
912
    if ($house == HOUSE_TYPE_ROYAL) {
913 9
        if (stripos($name, 'elizabeth') !== false) {
914
            return 'elizabeth_the_second';
915
        } elseif (stripos($name, 'charles') !== false) {
916
            return 'prince_charles';
917
        }
918
    }
919 14
920 2
    $s   = [' ', '&amp;', '&ocirc;',  '&Ouml;',  '&ouml;',   '&acirc;',  '&iacute;', '&aacute;', '&uacute;', '&eacute;', '&oacute;', '&Oacute;'];
921 2
    $s2  = [" ", "&",     "\xc3\xb4", "\xc3\96", "\xc3\xb6", "\xc3\xa5", "\xc3\xad", "\xc3\xa1", "\xc3\xba", "\xc3\xa9", "\xc3\xb3", "\xc3\x93"];
922 2
    $r   = ['_', 'and',   'o',        'o',       'o',        'a',        'i',        'a',        'u',        'e',        'o',        'o'];
923 2
    $name = preg_replace('#^the #', '', strtolower($name));
924 2
925 2
    $out = '';
926
927 12
    // Insert the Person ID if known.
928 1
    if ($pid !== null) {
929 1
        $out .= $pid . '/';
930
    }
931
932 11
    // Always inject the person's name
933 11
    $out .= urlencode(str_replace($s2, $r, str_replace($s, $r, $name)));
934 7
935
    // If there is a constituency, inject that too
936 11
    if ($const && $house == HOUSE_TYPE_COMMONS) {
937
        $out .= '/' . urlencode(str_replace($s2, $r, str_replace($s, $r, strtolower($const))));
938
    }
939
940
    return $out;
941
}
942
943
function member_full_name($house, $title, $given_name, $family_name, $lordofname) {
944
945
    switch ($house) {
946
        case HOUSE_TYPE_LORDS:
947
            $s = '';
948
            if (!$family_name) {
949
                $s = 'the ';
950
            }
951
            $s .= $title;
952
            if ($family_name) {
953
                $s .= ' ' . $family_name;
954
            }
955
            if ($lordofname) {
956
                $s .= ' of ' . $lordofname;
957
            }
958
            return $s;
959
960
        case HOUSE_TYPE_ROYAL:
961
            $s = "$given_name $family_name";
962
            return $s;
963
964
        default:
965
            $s = "$given_name $family_name";
966
            if ($title) {
967
                $s = $title . ' ' . $s;
968
            }
969
            return $s;
970
    }
971
972
}
973
974
function by_peer_name($a, $b) {
975
    if (!$a['family_name'] && !$b['family_name']) {
976
        return strcmp($a['lordofname'], $b['lordofname']);
977
    }
978
    if (!$a['family_name']) {
979
        return strcmp($a['lordofname'], $b['family_name']);
980
    }
981
    if (!$b['family_name']) {
982
        return strcmp($a['family_name'], $b['lordofname']);
983
    }
984
    if (strcmp($a['family_name'], $b['family_name'])) {
985
        return strcmp($a['family_name'], $b['family_name']);
986
    }
987
    return strcmp($a['lordofname'], $b['lordofname']);
988
}
989
990
function prettify_office($pos, $dept) {
991
    $lookup = [
992
        'Prime Minister, HM Treasury' => 'Prime Minister',
993
        'Secretary of State, Foreign & Commonwealth Office' => 'Foreign Secretary',
994
        'Secretary of State, Home Office' => 'Home Secretary',
995
        'Minister of State (Energy), Department of Trade and Industry'
996
            => 'Minister for energy, Department of Trade and Industry',
997
        'Minister of State (Pensions), Department for Work and Pensions'
998
            => 'Minister for pensions, Department for Work and Pensions',
999
        'Parliamentary Secretary to the Treasury, HM Treasury' => 'Chief Whip',
1000
        'The Parliamentary Secretary to the Treasury' => 'Chief Whip',
1001
        "Treasurer of Her Majesty's Household, HM Household" => "Deputy Chief Whip",
1002
        "The Treasurer of Her Majesty's Household, HM Household" => "Deputy Chief Whip",
1003
        'Comptroller, HM Household' => 'Government Whip',
1004
        'Vice Chamberlain, HM Household' => 'Government Whip',
1005
        "The Vice-Chamberlain of Her Majesty's Household" => 'Government Whip',
1006
        'Lords Commissioner, HM Treasury' => 'Government Whip',
1007
        "The Lord Commissioner of Her Majesty's Treasury" => 'Government Whip',
1008
        'Assistant Whip, HM Treasury' => 'Assistant Whip',
1009
        'Lords in Waiting, HM Household' => 'Government Whip',
1010
        'Lords in Waiting (HM Household)' => 'Government Whip',
1011
        'Baronesses in Waiting, HM Household' => 'Government Whip',
1012
    ];
1013
    if ($pos) { # Government post, or Chairman of Select Committee
1014
        $pretty = $pos;
1015
        if ($dept && $dept != 'No Department') {
1016
            $pretty .= ", $dept";
1017
        }
1018
        if (array_key_exists($pretty, $lookup)) {
1019
            $pretty = $lookup[$pretty];
1020
        }
1021
    } else { # Member of Select Committee
1022
        $pretty = "Member, $dept";
1023
    }
1024
    return $pretty;
1025
}
1026
1027
function major_summary($data, $echo = true) {
1028
    global $hansardmajors;
1029
    $html = '';
1030
    $db = new ParlDB();
1031
    $one_date = false;
1032
1033
    //if no printed majors passed, default to all
1034
    if (!isset($printed_majors)) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $printed_majors seems to never exist and therefore isset should always be false.
Loading history...
1035
        $printed_majors = [1, 2, 4, 3, 5, 101, 7]; # 8
1036
    }
1037
1038
    // single date?
1039
    if (isset($data['date'])) {
1040
        $one_date = true;
1041
    }
1042
1043
    // remove empty entries, so they don't produce errors
1044
    foreach (array_keys($hansardmajors) as $major) {
1045
        if (array_key_exists($major, $data)) {
1046
            if (count($data[$major]) == 0) {
1047
                unset($data[$major]);
1048
            }
1049
        }
1050
    }
1051
1052
    //work out the date text to be displaid
1053
    $daytext = [];
1054
    if (!$one_date) {
1055
        $todaystime = gmmktime(0, 0, 0, date('m'), date('d'), date('Y'));
0 ignored issues
show
Bug introduced by
date('m') of type string is incompatible with the type integer expected by parameter $month of gmmktime(). ( Ignorable by Annotation )

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

1055
        $todaystime = gmmktime(0, 0, 0, /** @scrutinizer ignore-type */ date('m'), date('d'), date('Y'));
Loading history...
Bug introduced by
date('Y') of type string is incompatible with the type integer expected by parameter $year of gmmktime(). ( Ignorable by Annotation )

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

1055
        $todaystime = gmmktime(0, 0, 0, date('m'), date('d'), /** @scrutinizer ignore-type */ date('Y'));
Loading history...
Bug introduced by
date('d') of type string is incompatible with the type integer expected by parameter $day of gmmktime(). ( Ignorable by Annotation )

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

1055
        $todaystime = gmmktime(0, 0, 0, date('m'), /** @scrutinizer ignore-type */ date('d'), date('Y'));
Loading history...
1056
        foreach ($data as $major => $array) {
1057
            if (!in_array('timestamp', $array)) {
1058
                $daytext[$major] = "The most recent ";
1059
            } elseif ($todaystime - $array['timestamp'] == 86400) {
1060
                $daytext[$major] = "Yesterday&rsquo;s";
1061
            } elseif ($todaystime - $array['timestamp'] <= (6 * 86400)) {
1062
                $daytext[$major] = gmdate('l', $array['timestamp']) . "&rsquo;s";
1063
            } else {
1064
                $daytext[$major] = "The most recent ";
1065
            }
1066
        }
1067
    }
1068
1069
    //build html
1070
    foreach ($printed_majors as $p_major) {
1071
        if (!array_key_exists($p_major, $data)) {
1072
            continue;
1073
        }
1074
1075
        if ($one_date) {
1076
            $date = $data['date'];
1077
        } else {
1078
            $date = $data[$p_major]['hdate'];
1079
        }
1080
        $q = $db->query('SELECT section_id, body, gid
1081
                FROM hansard, epobject
1082
                WHERE hansard.epobject_id = epobject.epobject_id '
1083
                . ($p_major == 4 ? 'AND subsection_id=0' : 'AND section_id=0') .
1084
                ' AND hdate = "' . $date . '"
1085
                AND major = ' . $p_major . '
1086
                ORDER BY hpos');
1087
        $out = '';
1088
        $LISTURL = new \MySociety\TheyWorkForYou\Url($hansardmajors[$p_major]['page_all']);
1089
        $current_sid = 0;
1090
        foreach ($q as $row) {
1091
            $gid = fix_gid_from_db($row['gid']);
1092
            $body = $row['body'];
1093
            $section_id = $row['section_id'];
1094
            //if (strstr($body, 'Chair]')) continue;
1095 20
            if ($p_major == 4 && !$section_id) {
1096 20
                if ($current_sid++) {
1097
                    $out .= '</ul>';
1098 20
                }
1099 20
                $out .= '<li>' . $body . '<ul>';
1100 20
            } else {
1101
                $LISTURL->insert([ 'id' => $gid ]);
1102 20
                $out .= '<li><a href="' . $LISTURL->generate() . '">';
1103 20
                $out .= $body . '</a>';
1104 20
            }
1105
        }
1106 20
        if ($out) {
1107
            $html .= _major_summary_title($p_major, $data, $LISTURL, $daytext);
1108 20
            $html .= '<ul class="hansard-day">';
1109 20
            $html .= $out;
1110 20
            $html .= '</ul>';
1111
        }
1112
    }
1113
    $html .= '</ul>';
1114
1115
    if ($echo) {
1116
        print $html;
1117
    } else {
1118
        return $html;
1119
    }
1120
}
1121
1122
function _major_summary_title($major, $data, $LISTURL, $daytext) {
1123
    global $hansardmajors;
1124
1125
    $return = '<h4>';
1126
    if (isset($daytext[$major])) {
1127
        $return .= $daytext[$major] . ' ';
1128
    }
1129
1130
    $return .= '<a href="';
1131
    if (isset($data[$major]['listurl'])) {
1132
        $return .= $data[$major]['listurl'];
1133
    } else {
1134
        $LISTURL->reset();
1135
        $return .= $LISTURL->generate();
1136
    }
1137
    $return .= '">' . $hansardmajors[$major]['title'] . '</a>';
1138
    if (isset($daytext[$major])) {
1139
        $return;
1140
    }
1141
    $return .= '</h4>';
1142
1143
    return $return;
1144
}
1145
1146
function score_to_strongly($dmpscore) {
1147
    $dmpdesc = "unknown about";
1148
    if ($dmpscore > 0.95 && $dmpscore <= 1.0) {
1149
        $dmpdesc = "consistently voted against";
1150
    } elseif ($dmpscore > 0.85) {
1151
        $dmpdesc = "almost always voted against";
1152
    } elseif ($dmpscore > 0.6) {
1153
        $dmpdesc = "generally voted against";
1154
    } elseif ($dmpscore > 0.4) {
1155
        $dmpdesc = "voted a mixture of for and against";
1156
    } elseif ($dmpscore > 0.15) {
1157
        $dmpdesc = "generally voted for";
1158
    } elseif ($dmpscore > 0.05) {
1159
        $dmpdesc = "almost always voted for";
1160
    } elseif ($dmpscore >= 0.0) {
1161
        $dmpdesc = "consistently voted for";
1162
    } elseif ($dmpscore == -1) {
1163
        $dmpdesc = "has never voted in a major vote about";
1164
    }
1165
    return $dmpdesc;
1166
}
1167
1168
function valid_url($url) {
1169
    $return = false;
1170
    if (preg_match("/^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?/i", $url)) {
1171
        $return = true;
1172
    }
1173
    return $return;
1174
}
1175
1176
function redirect($url, $code = 302) {
1177
    if (defined('TESTING')) {
1178
        print "Location: $url";
1179
    } else {
1180
        header("Location: $url", true, $code);
1181
    }
1182
    exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1183
}
1184
1185
function cache_version($file) {
1186
    static $version_hash = [];
1187
    $path = BASEDIR . "/$file";
1188
    if (is_file($path) && (!isset($version_hash[$file]) || DEVSITE)) {
1189
        $version_hash[$file] = stat($path)[9];
1190
        $file .= '?' . $version_hash[$file];
1191
    }
1192
    return WEBPATH . $file;
1193
}
1194