Passed
Pull Request — master (#1700)
by Struan
04:10
created

send_template_email()   C

Complexity

Conditions 15
Paths 117

Size

Total Lines 106
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 240

Importance

Changes 2
Bugs 0 Features 0
Metric Value
cc 15
eloc 44
c 2
b 0
f 0
nc 117
nop 5
dl 0
loc 106
rs 5.775
ccs 0
cts 41
cp 0
crap 240

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 = array (
33
            1 => array ('THEUSER', 'TIME', 'SQLERROR', 'PAGE', 'TEMPLATE', 'SEARCH', 'ALERTS', 'MP'),
34
            2 => array ('SQL', 'EMAIL', 'WIKIPEDIA', 'hansardlist', 'debatelist', 'wranslist', 'whalllist'),
35
            3 => array ('SQLRESULT')
36
            // Higher than this: 'DATA', etc.
37
        );
38
39
        // Store which headers we are allowed to show.
40
        $allowed_headers = array();
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)) $text = call_user_func($text);
55
            print "<p><span style=\"color:#039;\"><strong>$header</strong></span> $text</p>\n";
56
        }
57
    }
58 92
}
59
60
function exception_handler($e) {
61
    trigger_error($e->getMessage(), E_USER_ERROR);
62
}
63
64
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

64
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...
65
    // Custom error-handling function.
66
    // Sends an email to BUGSLIST.
67
    global $PAGE;
68
69
    # Ignore errors we've asked to ignore
70
    if (error_reporting()==0) return;
71
72
   // define an assoc array of error string
73
   // in reality the only entries we should
74
   // consider are E_WARNING, E_NOTICE, E_USER_ERROR,
75
   // E_USER_WARNING and E_USER_NOTICE
76
   # Commented out are ones that a user function cannot handle.
77
    $errortype = array (
78
        #E_ERROR            => "Error",
79
        E_WARNING           => "Warning",
80
        #E_PARSE            => "Parsing Error",
81
        E_NOTICE            => "Notice",
82
        #E_CORE_ERROR       => "Core Error",
83
        #E_CORE_WARNING     => "Core Warning",
84
        #E_COMPILE_ERROR    => "Compile Error",
85
        #E_COMPILE_WARNING  => "Compile Warning",
86
        E_USER_ERROR        => "User Error",
87
        E_USER_WARNING      => "User Warning",
88
        E_USER_NOTICE       => "User Notice",
89
        E_STRICT            => "Runtime Notice",
90
        # 5.3 introduced E_DEPRECATED
91
        8192                => 'Deprecated',
92
    );
93
94
    $err = '';
95
    if (isset($_SERVER['REQUEST_URI'])) {
96
        $err .= "URL:\t\thttps://" . DOMAIN . $_SERVER['REQUEST_URI'] . "\n";
97
    } else {
98
        $err .= "URL:\t\tNone - running from command line?\n";
99
    }
100
    if (isset($_SERVER['HTTP_REFERER'])) {
101
        $err .= "Referer:\t" . $_SERVER['HTTP_REFERER'] . "\n";
102
    } else {
103
        $err .= "Referer:\tNone\n";
104
    }
105
    if (isset($_SERVER['HTTP_USER_AGENT'])) {
106
        $err .= "User-Agent:\t" . $_SERVER['HTTP_USER_AGENT'] . "\n";
107
    } else {
108
        $err .= "User-Agent:\tNone\n";
109
    }
110
    $err .= "Number:\t\t$errno\n";
111
    $err .= "Type:\t\t" . $errortype[$errno] . "\n";
112
    $err .= "Message:\t$errmsg\n";
113
    $err .= "File:\t\t$filename\n";
114
    $err .= "Line:\t\t$linenum\n";
115
    if (count($_POST)) {
116
        $err .= "_POST:";
117
        foreach ($_POST as $k => $v) {
118
            $err .= "\t\t$k => $v\n";
119
        }
120
    }
121
122
// I'm not sure this bit is actually any use!
123
124
    // set of errors for which a var trace will be saved.
125
//  $user_errors = array(E_USER_ERROR, E_USER_WARNING, E_USER_NOTICE);
126
//  if (in_array($errno, $user_errors)) {
127
//      $err .= "Variables:\t" . serialize($vars) . "\n";
128
//  }
129
130
131
    // Add the problematic line if possible.
132
    if (is_readable($filename)) {
133
        $source = file($filename);
134
        $err .= "\nSource:\n\n";
135
        // Show the line, plus prev and next, with line numbers.
136
        $err .= $linenum-2 . " " . $source[$linenum-3];
137
        $err .= $linenum-1 . " " . $source[$linenum-2];
138
        $err .= $linenum . " " . $source[$linenum-1];
139
        $err .= $linenum+1 . " " . $source[$linenum];
140
        $err .= $linenum+2 . " " . $source[$linenum+1];
141
    }
142
143
144
    // Will we need to exit after this error?
145
    $fatal_errors = array(E_ERROR, E_USER_ERROR);
146
    if (in_array($errno, $fatal_errors)) {
147
        $fatal = true;
148
    } else {
149
        $fatal = false;
150
    }
151
152
    // Finally, display errors and stuff...
153
154
    if (DEVSITE || get_http_var(DEBUGTAG)) {
155
        // On a devsite we just display the problem.
156
        $errtxt = nl2br($err) . "\n";
157
        if (!strstr($errmsg, 'mysql_connect')) {
158
            $errtxt .= "<br><br>Backtrace:<br>" . nl2br(adodb_backtrace(false));
159
        }
160
        $message = array(
161
            'title' => "Error",
162
            'text' => $errtxt
163
        );
164
        if (is_object($PAGE)) {
165
            $PAGE->error_message($message, $fatal);
166
        } else {
167
            vardump($message);
168
        }
169
170
    } else {
171
        // On live sites we display a nice message and email the problem.
172
173
        $message = array(
174
            'title' => gettext("Sorry, an error has occurred"),
175
            'text' => gettext("We've been notified by email and will try to fix the problem soon!")
176
        );
177
178
        if (is_object($PAGE)) {
179
            $PAGE->error_message($message, $fatal);
180
        } else {
181
            header('HTTP/1.0 500 Internal Server Error');
182
            print "<p>Oops, sorry, an error has occurred!</p>\n";
183
        }
184
        if (!($errno & E_USER_NOTICE) && strpos($errmsg, 'pg_connect')===false && strpos($errmsg, 'mysql_connect')===false) {
185
            mail(BUGSLIST, "[TWFYBUG]: $errmsg", $err, "From: Bug <" . CONTACTEMAIL . ">\n".  "X-Mailer: PHP/" . phpversion() );
186
        }
187
    }
188
189
    // Do we need to exit?
190
    if ($fatal) {
191
        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...
192
    }
193
}
194
195
// Replacement for var_dump()
196
function vardump($blah) {
197
    print "<pre>\n";
198
    var_dump($blah);
199
    print "</pre>\n";
200
}
201
202
// pretty prints the backtrace, copied from http://uk.php.net/manual/en/function.debug-backtrace.php
203
function adodb_backtrace($print=true)
204
{
205
  $s = '';
206
  if (PHPVERSION() >= 4.3) {
207
208
    $MAXSTRLEN = 64;
209
210
    $traceArr = debug_backtrace();
211
    array_shift($traceArr);
212
    $tabs = sizeof($traceArr)-1;
213
    foreach ($traceArr as $arr) {
214
      for ($i=0; $i < $tabs; $i++) $s .= ' &nbsp; ';
215
      $tabs -= 1;
216
      if (isset($arr['class'])) $s .= $arr['class'].'.';
217
      $args = array();
218
      if (isset($arr['args'])) foreach ($arr['args'] as $v) {
219
    if (is_null($v)) $args[] = 'null';
220
    elseif (is_array($v)) $args[] = 'Array['.sizeof($v).']';
221
    elseif (is_object($v)) $args[] = 'Object:'.get_class($v);
222
    elseif (is_bool($v)) $args[] = $v ? 'true' : 'false';
223
    else {
224
      $v = (string) @$v;
225
      $str = _htmlspecialchars(substr($v,0,$MAXSTRLEN));
226
      if (strlen($v) > $MAXSTRLEN) $str .= '...';
227
      $args[] = $str;
228
    }
229
      }
230
231
      $s .= $arr['function'].'('.implode(', ',$args).')';
232
      //      $s .= sprintf("</font><font color=#808080 size=-1> # line %4d,".
233
      //            " file: <a href=\"file:/%s\">%s</a></font>",
234
      //        $arr['line'],$arr['file'],$arr['file']);
235
      $s .= "\n";
236
    }
237
    if ($print) print $s;
238
  }
239
240
  return $s;
241
}
242
243
// Returns the unixtime in microseconds.
244
function getmicrotime() {
245 92
    $mtime = microtime();
246 92
    $mtime = explode(" ",$mtime);
247 92
    $mtime = $mtime[1] + $mtime[0];
248
249 92
    return $mtime;
250
}
251
252
/* twfy_debug_timestamp
253
 * Output a timestamp since the page was started. */
254
$timestamp_last = $timestamp_start = getmicrotime();
255
function twfy_debug_timestamp($label = "") {
256
    global $timestamp_last, $timestamp_start;
257
    $t = getmicrotime();
258
    twfy_debug("TIME", sprintf("%f msecs since start; %f msecs since last; %s",
259
            ($t - $timestamp_start)*1000.0, ($t - $timestamp_last)*1000.0, $label));
260
    $timestamp_last = $t;
261
}
262
263
function format_timestamp($timestamp, $format) {
264
    // Pass it a MYSQL TIMESTAMP (YYYYMMDDHHMMSS) and a
265
    // PHP date format string (eg, "Y-m-d H:i:s")
266
    // and it returns a nicely formatted string according to requirements.
267
268
    // Because strtotime can't handle TIMESTAMPS.
269
270
    if (preg_match("/^(\d\d\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)$/", $timestamp, $matches)) {
271
        list($string, $year, $month, $day, $hour, $min, $sec) = $matches;
272
273
        return strftime($format, mktime($hour, $min, $sec, $month, $day, $year));
274
    } else {
275
        return "";
276
    }
277
278
}
279
280
281
$format_date_months = array('', 'January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
282
$format_date_months_short = array('', 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
283
284
function format_date($date, $format) {
285 37
    global $format_date_months, $format_date_months_short;
286
    // Pass it a date (YYYY-MM-DD) and a
287
    // PHP date format string (eg, "Y-m-d H:i:s")
288
    // and it returns a nicely formatted string according to requirements.
289
290 37
    if (preg_match("/^(\d\d\d\d)-(\d\d?)-(\d\d?)$/", $date, $matches)) {
291 37
        list($string, $year, $month, $day) = $matches;
292 37
        if ($year < 1902) { # gmdate fns only go back to Dec. 1901
293
            if ($format == SHORTDATEFORMAT) {
294
                return ($day+0) . ' ' . $format_date_months_short[$month+0] . " $year";
295
            } else {
296
                return ($day+0) . ' ' . $format_date_months[$month+0] . " $year";
297
            }
298
        }
299
300 37
        return strftime($format, mktime(0, 0, 0, $month, $day, $year));
301
    } else {
302
        return "";
303
    }
304
305
}
306
307
308
function format_time($time, $format) {
309
    // Pass it a time (HH:MM:SS) and a
310
    // PHP date format string (eg, "H:i")
311
    // and it returns a nicely formatted string according to requirements.
312
313
    if (preg_match("/^(\d\d):(\d\d):(\d\d)$/", $time, $matches)) {
314
        list($string, $hour, $min, $sec) = $matches;
315
316
        return strftime($format, mktime($hour, $min, $sec));
317
    } else {
318
        return "";
319
    }
320
}
321
322
323
324
function relative_time($datetime) {
325
    // Pass it a 'YYYY-MM-DD HH:MM:SS' and it will return something
326
    // like "Two hours ago", "Last week", etc.
327
328
    // http://maniacalrage.net/projects/relative/
329
330
    if (!preg_match("/\d\d\d\d-\d\d-\d\d \d\d\:\d\d\:\d\d/", $datetime)) {
331
        return '';
332
    }
333
334
    $in_seconds = strtotime($datetime);
335
    $now = time();
336
337
    $diff   =  $now - $in_seconds;
338
    $months =  floor($diff/2419200);
339
    $diff   -= $months * 2419200;
340
    $weeks  =  floor($diff/604800);
341
    $diff   -= $weeks*604800;
342
    $days   =  floor($diff/86400);
343
    $diff   -= $days * 86400;
344
    $hours  =  floor($diff/3600);
345
    $diff   -= $hours * 3600;
346
    $minutes = floor($diff/60);
347
    $diff   -= $minutes * 60;
348
    $seconds = $diff;
349
350
351
    if ($months > 0) {
352
        // Over a month old, just show the actual date.
353
        $date = substr($datetime, 0, 10);
354
        return format_date($date, LONGDATEFORMAT);
355
356
    } else {
357
        $relative_date = '';
358
        if ($weeks > 0) {
359
            // Weeks and days
360
            $relative_date .= ($relative_date?', ':'').$weeks.' week'.($weeks>1?'s':'');
361
            $relative_date .= $days>0?($relative_date?', ':'').$days.' day'.($days>1?'s':''):'';
362
        } elseif ($days > 0) {
363
            // days and hours
364
            $relative_date .= ($relative_date?', ':'').$days.' day'.($days>1?'s':'');
365
            $relative_date .= $hours>0?($relative_date?', ':'').$hours.' hour'.($hours>1?'s':''):'';
366
        } elseif ($hours > 0) {
367
            // hours and minutes
368
            $relative_date .= ($relative_date?', ':'').$hours.' hour'.($hours>1?'s':'');
369
            $relative_date .= $minutes>0?($relative_date?', ':'').$minutes.' minute'.($minutes>1?'s':''):'';
370
        } elseif ($minutes > 0) {
371
            // minutes only
372
            $relative_date .= ($relative_date?', ':'').$minutes.' minute'.($minutes>1?'s':'');
373
        } else {
374
            // seconds only
375
            $relative_date .= ($relative_date?', ':'').$seconds.' second'.($seconds>1?'s':'');
376
        }
377
    }
378
379
    // Return relative date and add proper verbiage
380
    return $relative_date.' ago';
381
382
}
383
384
function parse_date($date) {
385
    return datetime_parse_local_date($date, time(), 'en', 'gb');
386
}
387
388
function slugify($text) {
389
    $text = preg_replace('#[^\w]+#', '-', $text);
390
    $text = trim($text, '-');
391
    $text = preg_replace('#-+#', '-', $text);
392
    $text = strtolower($text);
393
    return $text;
394
}
395
396
/* strip_tags_tospaces TEXT
397
 * Return a copy of TEXT in which certain block-level HTML tags have been
398
 * replaced by single spaces, and other HTML tags have been removed. */
399
function strip_tags_tospaces($text) {
400 8
    $text = preg_replace("#\<(p|br|div|td|tr|th|table)[^>]*\>#i", " ", $text);
401
402 8
    return strip_tags(trim($text));
403
}
404
405
function trim_characters($text, $start, $length, $url_length = 60) {
406
    // Pass it a string, a numeric start position and a numeric length.
407
    // If the start position is > 0, the string will be trimmed to start at the
408
    // nearest word boundary after (or at) that position.
409
    // If the string is then longer than $length, it will be trimmed to the nearest
410
    // word boundary below (or at) that length.
411
    // If either end is trimmed, ellipses will be added.
412
    // The modified string is then returned - its *maximum* length is $length.
413
    // HTML is always stripped (must be for trimming to prevent broken tags).
414
415 8
    $text = strip_tags_tospaces($text);
416
417
    // Split long strings up so they don't go too long.
418
    // Mainly for URLs which are displayed, but aren't links when trimmed.
419 8
    $text = preg_replace('/(\S{' . $url_length . '})/', "\$1 ", $text);
420
421
    // Otherwise the word boundary matching goes odd...
422 8
    $text = preg_replace("/[\n\r]/", " ", $text);
423
424
    // Trim start.
425 8
    if ($start > 0) {
426
        $text = substr($text, $start);
427
428
        // Word boundary.
429
        if (preg_match ("/.+?\b(.*)/", $text, $matches)) {
430
            $text = $matches[1];
431
            // Strip spare space at the start.
432
            $text = preg_replace ("/^\s/", '', $text);
433
        }
434
        $text = '...' . $text;
435
    }
436
437
    // Trim end.
438 8
    if (strlen($text) > $length) {
439
440
        // Allow space for ellipsis.
441 8
        $text = substr($text, 0, $length - 3);
442
443
        // Word boundary.
444 8
        if (preg_match ("/(.*)\s.+/", $text, $matches)) {
445 8
            $text = $matches[1];
446
            // Strip spare space at the end.
447 8
            $text = preg_replace ("/\s$/", '', $text);
448
        }
449
        // We don't want to use the HTML entity for an ellipsis (&#8230;), because then
450
        // it screws up when we subsequently use htmlentities() to print the returned
451
        // string!
452 8
        $text .= '...';
453
    }
454
455 8
    return $text;
456
}
457
458
/**
459
 * Filters user input to remove unwanted HTML tags etc
460
 */
461
function filter_user_input($text, $filter_type) {
462
    // We use this to filter any major user input, especially comments.
463
    // Gets rid of bad HTML, basically.
464
    // Uses iamcal.com's lib_filter class.
465
466
    // $filter_type is the level of filtering we want:
467
    //      'comment' allows <b> and <i> tags.
468
    //      'strict' strips all tags.
469
470 1
    global $filter;
471
472 1
    $text = trim($text);
473
474
    // Replace 3 or more newlines with just two newlines.
475
    //$text = preg_replace("/(\n){3,}/", "\n\n", $text);
476
477 1
    if ($filter_type == 'strict') {
478
        // No tags allowed at all!
479
        $filter->allowed = array ();
480
    } else {
481
        // Comment.
482
        // Only allowing <a href>, <b>, <strong>, <i> and <em>
483 1
        $filter->allowed = array (
484
            'a' => array('href'),
485
            'strong' => array(),
486
            'em' => array(),
487
            'b' => array(),
488
            'i' => array()
489
        );
490
        // turning this on means that stray angle brackets
491
        // are not turned in to tags
492 1
        $filter->always_make_tags = 0;
493
    }
494
495 1
    $text = $filter->go($text);
496
497 1
    return $text;
498
}
499
500
function prepare_comment_for_display($text) {
501
    // Makes any URLs into HTML links.
502
    // Turns \n's into <br>
503
504
    // Encode HTML entities.
505
    // Can't do htmlentities() because it'll turn the few tags we allow into &lt;
506
    // Must go before the URL stuff.
507 3
    $text = htmlentities_notags($text);
508
509 3
    $link_length = 60;
510 3
    $text = preg_replace_callback(
511 3
        "/(?<!\"|\/)((http(s?):\/\/)|(www\.))([a-zA-Z\d_.+,;:?%~\-\/#='*$!()&[\]]+)([a-zA-Z\d_?%~\-\/#='*$!&])/",
512
        function($matches) use ($link_length) {
513 2
            if (strlen($matches[0]) > $link_length) {
514 1
                return '<a href="' . $matches[0] . '" rel="nofollow">' . substr($matches[0], 0, $link_length) . "...</a>";
515
            } else {
516 1
                return '<a href="' . $matches[0] . '" rel="nofollow">' . $matches[0] . '</a>';
517
            }
518 3
        },
519 3
        $text);
520 3
    $text = str_replace('<a href="www', '<a href="http://www', $text);
521 3
    $text = preg_replace("/([\w\.]+)(@)([\w\.\-]+)/i", "<a href=\"mailto:$0\">$0</a>", $text);
522 3
    $text = str_replace("\n", "<br>\n", $text);
523
524 3
    return $text;
525
}
526
527
function htmlentities_notags($text) {
528
    // If you want to do htmlentities() on some text that has HTML tags
529
    // in it, then you need this function.
530
531 3
    $tbl = get_html_translation_table(HTML_ENTITIES, ENT_QUOTES, 'UTF-8');
532
533
    // You could encode extra stuff...
534
    //$tbl["“"] = "&quot;";
535
    //$tbl["”"] = "&quot;";
536
    //$tbl["…"] = "...";
537
    //$tbl["—"] = "-";
538
    //$tbl["»"] = "&raquo;";
539
    //$tbl["«"] = "&laquo;";
540
541
  // lib_filter will replace unmatched < and > with entities so
542
  // we abuse strtr's only replace once behaviour to not double
543
  // encode them. May not be robust.
544
  // This does mean if anyone actually wants to put &gt; or &lt;
545
  // in a comment they can't but that's a lot less likely than
546
  // < or > for less than and greater than.
547 3
  $tbl['&lt;'] = "&lt;";
548 3
  $tbl['&gt;'] = "&gt;";
549
550
    // Don't want to encode these things
551 3
    unset ($tbl["<"]);
552 3
    unset ($tbl[">"]);
553 3
    unset ($tbl["'"]);
554 3
    unset ($tbl['"']);
555
556
    # strtr "will *NOT* try to replace stuff that it has already worked on."
557 3
    $text = strtr($text, $tbl);
558
559 3
    return $text;
560
}
561
562
/*
563
 * PHP 5.4 changes the default encoding for htmlentities and htmlspecialchars
564
 * to be UTF-8, not using the php.ini character encoding until PHP 5.6. So
565
 * we have to wrap all uses of these two functions.
566
 */
567
function _htmlentities($s) {
568 8
    return htmlentities($s, ENT_COMPAT, 'UTF-8');
569
}
570
function _htmlspecialchars($s) {
571
    return htmlspecialchars($s, ENT_COMPAT, 'UTF-8');
572
}
573
574
function get_canonical_gid($gid) {
575
    $db = new ParlDB;
576
    $might_be_redirected = true;
577
    while ($might_be_redirected) {
578
        $q = $db->query("SELECT gid_to FROM gidredirect WHERE gid_from = :gid", array(':gid' => $gid))->first();
579
        if ($q) {
580
            $gid = $q['gid_to'];
581
        } else {
582
            $might_be_redirected = false;
583
        }
584
    }
585
586
    return $gid;
587
}
588
589
590
function fix_gid_from_db($gid) {
591
    // The gids in the database are longer than we use in the site.
592
    // Feed this a gid from the db and it will be returned truncated.
593
594
    // $gid will be like 'uk.org.publicwhip/debate/2003-02-28.475.3'.
595
596 4
    $newgid = substr($gid, strrpos($gid, '/')+1 );
597 4
    return $newgid;
598
}
599
600
function gid_to_anchor($gid) {
601
    // For trimming gids to be used as #anchors in pages.
602
    // Extracted here so we keep it consistent.
603
    // The gid should already be truncated using fix_gid_from_db(), so it
604
    // will be like 2003-11-20.966.0
605
    // This function returns 966.0
606
607
    return substr( $gid, (strpos($gid, '.') + 1) );
608
}
609
610
function preg_replacement_quote($s) {
611
    // This returns $s but with every $ and \ backslash-escaped.
612
    // This is to create a string that can be safely used in a
613
    // preg_replace replacement string.  This function was suggested here:
614
    // http://www.procata.com/blog/archives/2005/11/13/two-preg_replace-escaping-gotchas/
615 1
    return preg_replace('/(\$|\\\\)(?=\d)/', '\\\\\1', $s);
616
}
617
618
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

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

1013
        $todaystime = gmmktime(0, 0, 0, date('m'), /** @scrutinizer ignore-type */ 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

1013
        $todaystime = gmmktime(0, 0, 0, date('m'), date('d'), /** @scrutinizer ignore-type */ date('Y'));
Loading history...
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

1013
        $todaystime = gmmktime(0, 0, 0, /** @scrutinizer ignore-type */ date('m'), date('d'), date('Y'));
Loading history...
1014
        foreach ($data as $major => $array) {
1015
            if (!in_array('timestamp', $array)) $daytext[$major] = "The most recent ";
1016
            elseif ($todaystime - $array['timestamp'] == 86400) $daytext[$major] = "Yesterday&rsquo;s";
1017
            elseif ($todaystime - $array['timestamp'] <= (6 * 86400)) $daytext[$major] = gmdate('l', $array['timestamp']) . "&rsquo;s";
1018
            else $daytext[$major] = "The most recent ";
1019
        }
1020
    }
1021
1022
    //build html
1023
    foreach ($printed_majors as $p_major) {
1024
        if (!array_key_exists($p_major, $data))
1025
            continue;
1026
1027
        if ($one_date)
1028
            $date = $data['date'];
1029
        else
1030
            $date = $data[$p_major]['hdate'];
1031
        $q = $db->query('SELECT section_id, body, gid
1032
                FROM hansard, epobject
1033
                WHERE hansard.epobject_id = epobject.epobject_id '
1034
                . ($p_major == 4 ? 'AND subsection_id=0' : 'AND section_id=0') .
1035
                ' AND hdate = "' . $date . '"
1036
                AND major = ' . $p_major . '
1037
                ORDER BY hpos');
1038
        $out = '';
1039
        $LISTURL = new \MySociety\TheyWorkForYou\Url($hansardmajors[$p_major]['page_all']);
1040
        $current_sid = 0;
1041
        foreach ($q as $row) {
1042
            $gid = fix_gid_from_db($row['gid']);
1043
            $body = $row['body'];
1044
            $section_id = $row['section_id'];
1045
            //if (strstr($body, 'Chair]')) continue;
1046
            if ($p_major == 4 && !$section_id) {
1047
                if ($current_sid++) {
1048
                    $out .= '</ul>';
1049
                }
1050
                $out .= '<li>' . $body . '<ul>';
1051
            } else {
1052
                $LISTURL->insert( array( 'id' => $gid ) );
1053
                $out .= '<li><a href="'.$LISTURL->generate().'">';
1054
                $out .= $body . '</a>';
1055
            }
1056
        }
1057
        if ($out) {
1058
            $html .= _major_summary_title($p_major, $data, $LISTURL, $daytext);
1059
            $html .= '<ul class="hansard-day">';
1060
            $html .= $out;
1061
            $html .= '</ul>';
1062
        }
1063
    }
1064
    $html .= '</ul>';
1065
1066
    if ($echo) {
1067
        print $html;
1068
    } else {
1069
        return $html;
1070
    }
1071
}
1072
1073
function _major_summary_title($major, $data, $LISTURL, $daytext) {
1074
    global $hansardmajors;
1075
1076
    $return = '<h4>';
1077
    if (isset($daytext[$major])) {
1078
     $return .= $daytext[$major] . ' ';
1079
    }
1080
1081
    $return .= '<a href="';
1082
    if (isset($data[$major]['listurl']))
1083
        $return .= $data[$major]['listurl'];
1084
    else {
1085
        $LISTURL->reset();
1086
        $return .= $LISTURL->generate();
1087
    }
1088
    $return .= '">' . $hansardmajors[$major]['title'] . '</a>';
1089
    if (isset($daytext[$major])) $return;
1090
    $return .= '</h4>';
1091
1092
    return $return;
1093
}
1094
1095
function score_to_strongly($dmpscore) {
1096 20
    $dmpdesc = "unknown about";
1097 20
    if ($dmpscore > 0.95 && $dmpscore <= 1.0)
1098
        $dmpdesc = "consistently voted against";
1099 20
    elseif ($dmpscore > 0.85)
1100 20
        $dmpdesc = "almost always voted against";
1101 20
    elseif ($dmpscore > 0.6)
1102
        $dmpdesc = "generally voted against";
1103 20
    elseif ($dmpscore > 0.4)
1104 20
        $dmpdesc = "voted a mixture of for and against";
1105 20
    elseif ($dmpscore > 0.15)
1106
        $dmpdesc = "generally voted for";
1107 20
    elseif ($dmpscore > 0.05)
1108
        $dmpdesc = "almost always voted for";
1109 20
    elseif ($dmpscore >= 0.0)
1110 20
        $dmpdesc = "consistently voted for";
1111 20
    return $dmpdesc;
1112
}
1113
1114
function valid_url($url) {
1115
    $return = false;
1116
    if (preg_match("/^(http|https|ftp):\/\/(([A-Z0-9][A-Z0-9_-]*)(\.[A-Z0-9][A-Z0-9_-]*)+)(:(\d+))?/i", $url)) {
1117
        $return = true;
1118
    }
1119
    return $return;
1120
}
1121
1122
function redirect($url, $code=302) {
1123
    if (defined('TESTING')) {
1124
        print "Location: $url";
1125
    } else {
1126
        header("Location: $url", true, $code);
1127
    }
1128
    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...
1129
}
1130
1131
function cache_version($file) {
1132
    static $version_hash = array();
1133
    $path = BASEDIR . "/$file";
1134
    if (is_file($path) && (!isset($version_hash[$file]) || DEVSITE)) {
1135
        $version_hash[$file] = stat($path)[9];
1136
        $file .= '?' . $version_hash[$file];
1137
    }
1138
    return WEBPATH . $file;
1139
}
1140