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

change_locale()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 1
dl 0
loc 7
rs 10
1
<?php
2
/* 
3
 * Name: alertmailer.php
4
 * Description: Mailer for email alerts
5
 * $Id: alertmailer.php,v 1.34 2009-06-23 10:11:10 matthew Exp $
6
 */
7
8
function mlog($message) {
9
    print $message;
10
}
11
12
include_once '../www/includes/easyparliament/init.php';
13
ini_set('memory_limit', -1);
14
include_once INCLUDESPATH . 'easyparliament/member.php';
15
16
$global_start = getmicrotime();
17
$db = new ParlDB;
18
19
# Get current value of latest batch
20
$q = $db->query('SELECT max(indexbatch_id) as max_batch_id FROM indexbatch')->first();
21
$max_batch_id = $q['max_batch_id'];
22
mlog("max_batch_id: " . $max_batch_id . "\n");
23
24
# Last sent is timestamp of last alerts gone out.
25
# Last batch is the search index batch number last alert went out to.
26
if (is_file('alerts-lastsent')) {
27
    $lastsent = file('alerts-lastsent');
28
} else {
29
    $lastsent = array('', 0);
30
}
31
32
$lastupdated = trim($lastsent[0]);
33
if (!$lastupdated) {
34
    $lastupdated = strtotime('00:00 today');
35
}
36
$lastbatch = trim($lastsent[1]);
37
if (!$lastbatch) {
38
    $lastbatch = 0;
39
}
40
mlog("lastupdated: $lastupdated lastbatch: $lastbatch\n");
41
42
# Construct query fragment to select search index batches which
43
# have been made since last time we ran
44
$batch_query_fragment = "";
45
for ($i = $lastbatch + 1; $i <= $max_batch_id; $i++) {
46
    $batch_query_fragment .= "batch:$i ";
47
}
48
$batch_query_fragment = trim($batch_query_fragment);
49
mlog("batch_query_fragment: " . $batch_query_fragment . "\n");
50
51
if (!$batch_query_fragment) {
52
    mlog("No new batches since last run - nothing to run over!");
53
    exit;
54
}
55
56
# For testing purposes, specify nomail on command line to not send out emails
57
$nomail = false;
58
$onlyemail = '';
59
$fromemail = '';
60
$fromflag = false;
61
$toemail = '';
62
$template = 'alert_mailout';
63
for ($k = 1; $k < $argc; $k++) {
64
    if ($argv[$k] == '--nomail') {
65
        $nomail = true;
66
    }
67
    if (preg_match('#^--only=(.*)$#', $argv[$k], $m)) {
68
        $onlyemail = $m[1];
69
    }
70
    if (preg_match('#^--from=(.*)$#', $argv[$k], $m)) {
71
        $fromemail = $m[1];
72
    }
73
    if (preg_match('#^--to=(.*)$#', $argv[$k], $m)) {
74
        $toemail = $m[1];
75
    }
76
    if (preg_match('#^--template=(.*)$#', $argv[$k], $m)) {
77
        $template = $m[1];
78
        # Tee hee
79
        $template = "../../../../../../../../../../home/twfy-live/email-alert-templates/alert_mailout_$template";
80
    }
81
}
82
83
if (DEVSITE) {
84
    $nomail = true;
85
}
86
87
if ($nomail) {
88
    mlog("NOT SENDING EMAIL\n");
89
}
90
if (($fromemail && $onlyemail) || ($toemail && $onlyemail)) {
91
    mlog("Can't have both from/to and only!\n");
92
    exit;
93
}
94
95
$active = 0;
96
$queries = 0;
97
$unregistered = 0;
98
$registered = 0;
99
$sentemails = 0;
100
101
$LIVEALERTS = new ALERT;
102
103
$current = array('email' => '', 'token' => '', 'lang' => '');
104
$email_text = '';
105
$html_text = '';
106
$globalsuccess = 1;
107
108
# Fetch all confirmed, non-deleted alerts
109
$confirmed = 1; $deleted = 0;
110
$alertdata = $LIVEALERTS->fetch($confirmed, $deleted);
111
$alertdata = $alertdata['data'];
112
113
$DEBATELIST = new DEBATELIST; # Nothing debate specific, but has to be one of them
114
115
$sects = array(
116
    1 => gettext('Commons debate'),
117
    2 => gettext('Westminster Hall debate'),
118
    3 => gettext('Written Answer'),
119
    4 => gettext('Written Ministerial Statement'),
120
    5 => gettext('Northern Ireland Assembly debate'),
121
    6 => gettext('Public Bill committee'),
122
    7 => gettext('Scottish Parliament debate'),
123
    8 => gettext('Scottish Parliament written answer'),
124
    9 => gettext('London Mayoral question'),
125
    10 => gettext('Senedd debate'),
126
    11 => gettext('Senedd debate'),
127
    101 => gettext('Lords debate'),
128
    'F' => gettext('event'),
129
    'V' => gettext('vote'),
130
);
131
$sects_plural = array(
132
    1 => gettext('Commons debates'),
133
    2 => gettext('Westminster Hall debates'),
134
    3 => gettext('Written Answers'),
135
    4 => gettext('Written Ministerial Statements'),
136
    5 => gettext('Northern Ireland Assembly debates'),
137
    6 => gettext('Public Bill committees'),
138
    7 => gettext('Scottish Parliament debates'),
139
    8 => gettext('Scottish Parliament written answers'),
140
    9 => gettext('London Mayoral questions'),
141
    10 => gettext('Senedd debates'),
142
    11 => gettext('Senedd debates'),
143
    101 => gettext('Lords debate'),
144
    'F' => gettext('event'),
145
    'V' => gettext('vote'),
146
);
147
$sects_gid = array(
148
    1 => 'debate',
149
    2 => 'westminhall',
150
    3 => 'wrans',
151
    4 => 'wms',
152
    5 => 'ni',
153
    6 => 'pbc',
154
    7 => 'sp',
155
    8 => 'spwa',
156
    9 => 'london-mayors-questions',
157
    10 => 'senedd/en',
158
    11 => 'senedd/cy',
159
    101 => 'lords',
160
    'F' => 'calendar',
161
);
162
$sects_search = array(
163
    1 => 'debate',
164
    2 => 'westminhall',
165
    3 => 'wrans',
166
    4 => 'wms',
167
    5 => 'ni',
168
    6 => 'pbc',
169
    7 => 'sp',
170
    8 => 'spwrans',
171
    9 => 'lmqs',
172
    10 => 'wales',
173
    11 => 'wales',
174
    101 => 'lords',
175
    'F' => 'future',
176
);
177
178
$domain = '';
179
$outof = count($alertdata);
180
$start_time = time();
181
foreach ($alertdata as $alertitem) {
182
    $active++;
183
    $email = $alertitem['email'];
184
    if ($onlyemail && $email != $onlyemail) {
185
        continue;
186
    }
187
    if ($fromemail && strcasecmp($email, $fromemail) > 0) {
188
        $fromflag = true;
189
    }
190
    if ($fromemail && !$fromflag) {
191
        continue;
192
    }
193
    if ($toemail && strcasecmp($email, $toemail) > 0) {
194
        continue;
195
    }
196
    $criteria_raw = $alertitem['criteria'];
197
    if (preg_match('#\bOR\b#', $criteria_raw)) {
198
        $criteria_raw = "($criteria_raw)";
199
    }
200
    $criteria_batch = $criteria_raw . " " . $batch_query_fragment;
201
202
    $lang = $alertitem['lang'];
203
    if (!$domain) {
204
        if ($lang == 'cy') {
205
            $domain = 'cy.theyworkforyou.com';
206
        } else {
207
            $domain = 'www.theyworkforyou.com';
208
        }
209
    }
210
211
    if ($email != $current['email']) {
212
        if ($email_text) {
213
            write_and_send_email($current, $email_text, $html_text, $template);
214
        }
215
        $current['email'] = $email;
216
        $current['token'] = $alertitem['alert_id'] . '-' . $alertitem['registrationtoken'];
217
        $current['lang'] = $lang;
218
        $email_text = '';
219
        $html_text = '';
220
        $q = $db->query('SELECT user_id FROM users WHERE email = :email', array(
221
            ':email' => $email
222
            ))->first();
223
        if ($q) {
224
            $user_id = $q['user_id'];
225
            $registered++;
226
        } else {
227
            $user_id = 0;
228
            $unregistered++;
229
        }
230
        mlog("\nEMAIL: $email, uid $user_id; memory usage : " . memory_get_usage() . "\n");
231
    }
232
233
    mlog("  ALERT $active/$outof QUERY $queries : Xapian query '$criteria_batch'");
234
    $start = getmicrotime();
235
    $SEARCHENGINE = new SEARCHENGINE($criteria_batch, $lang);
236
    #mlog("query_remade: " . $SEARCHENGINE->query_remade() . "\n");
237
    $args = array(
238
        's' => $criteria_raw, # Note: use raw here for URLs, whereas search engine has batch
239
        'threshold' => $lastupdated, # Return everything added since last time this script was run
240
        'o' => 'c',
241
        'num' => 1000, // this is limited to 1000 in hansardlist.php anyway
242
        'pop' => 1,
243
        'e' => 1 # Don't escape ampersands
244
    );
245
    $data = $DEBATELIST->_get_data_by_search($args);
246
    $total_results = $data['info']['total_results'];
247
    $queries++;
248
    mlog(", hits " . $total_results . ", time " . (getmicrotime()-$start) . "\n");
249
250
    # Divisions
251
    if (preg_match('#^speaker:(\d+)$#', $criteria_raw, $m)) {
252
        $pid = $m[1];
253
        $q = $db->query('SELECT * FROM persondivisionvotes pdv JOIN divisions USING(division_id)
254
            WHERE person_id=:person_id AND pdv.lastupdate >= :time', array(
255
                'person_id' => $pid,
256
                ':time' => date('Y-m-d H:i:s', $lastupdated),
0 ignored issues
show
Bug introduced by
It seems like $lastupdated can also be of type string; however, parameter $timestamp of date() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

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

256
                ':time' => date('Y-m-d H:i:s', /** @scrutinizer ignore-type */ $lastupdated),
Loading history...
257
            ));
258
        foreach ($q as $row) {
259
            # Skip other-language divisions if needed, set locale
260
            if (strpos($row['division_id'], '-cy-')) {
261
                if ($lang == 'en') {
262
                    continue;
263
                }
264
                change_locale('cy');
265
            } elseif (strpos($row['division_id'], '-en-')) {
266
                if ($lang == 'cy') {
267
                    continue;
268
                }
269
                change_locale('en');
270
            } else {
271
                change_locale('en');
272
            }
273
274
            $vote = $row['vote'];
275
            $num = $row['division_number'];
276
            $teller = '';
277
            if (strpos($vote, 'tell') !== false) {
278
                $teller = ', ' . gettext('as a teller');
279
                $vote = str_replace('tell', '', $vote);
280
            }
281
            if ($vote == 'absent') {
282
                continue;
283
            }
284
            if ($vote == 'aye' && $row['yes_text']) {
285
                $text = "Voted ($vote$teller) " . $row['yes_text'];
286
            } elseif ($vote == 'no' && $row['no_text']) {
287
                $text = "Voted ($vote$teller) " . $row['no_text'];
288
            } elseif ($vote == 'aye') {
289
                $text = gettext("Voted aye") . $teller;
290
            } elseif ($vote == 'no') {
291
                $text = gettext("Voted no") . $teller;
292
            } elseif ($vote == 'both') {
293
                $text = gettext("Voted abstain") . $teller;
294
            } else {
295
                $text = sprintf(gettext("Voted %s"), $vote) . $teller;
296
            }
297
            $text .= " " . sprintf(gettext("(division #%s; result was <b>%s</b> aye, <b>%s</b> no)"), $num, $row['yes_total'], $row['no_total']);
298
            $data['rows'][] = [
299
                'parent' => [
300
                    'body' => $row['division_title'],
301
                ],
302
                'extract' => $text,
303
                'listurl' => '/divisions/' . $row['division_id'],
304
                'major' => 'V',
305
                'hdate' => $row['division_date'],
306
                'hpos' => $row['division_number'],
307
            ];
308
        }
309
    }
310
311
    if (isset($data['rows']) && count($data['rows']) > 0) {
312
        usort($data['rows'], 'sort_by_stuff'); # Sort results into order, by major, then date, then hpos
313
        $o = array(); $major = 0; $count = array(); $total = 0;
314
        $any_content = false;
315
        foreach ($data['rows'] as $row) {
316
            if ($major !== $row['major']) {
317
                $count[$major] = $total; $total = 0;
318
                $major = $row['major'];
319
                $o[$major] = ['text' => '', 'html' => ''];
320
                $k = 3;
321
            }
322
            #mlog($row['major'] . " " . $row['gid'] ."\n");
323
324
            if (isset($sects_gid[$major])) {
325
                $q = $db->query('SELECT gid_from FROM gidredirect WHERE gid_to = :gid_to', array(
326
                    ':gid_to' => 'uk.org.publicwhip/' . $sects_gid[$major] . '/' . $row['gid']
327
                    ));
328
                if ($q->rows() > 0) {
329
                    continue;
330
                }
331
            }
332
333
            if ($major == 11) {
334
                change_locale('cy');
335
            } else {
336
                change_locale('en');
337
            }
338
339
            --$k;
340
            if ($major == 'V' || $k >= 0) {
341
                $any_content = true;
342
                $parentbody = text_html_to_email($row['parent']['body']);
343
                $body_text = text_html_to_email($row['extract']);
344
                $body_html = $row['extract'];
345
                if (isset($row['speaker']) && count($row['speaker'])) {
346
                    $body_text = $row['speaker']['name'] . ': ' . $body_text;
347
                    $body_html = '<strong style="font-weight: 900;">' . $row['speaker']['name'] . '</strong>: ' . $body_html;
348
                }
349
                $body_html = '<p style="font-size: 16px;">' . $body_html . '</p>';
350
351
                $body_text = wordwrap($body_text, 72);
352
                $o[$major]['text'] .= $parentbody . ' (' . format_date($row['hdate'], SHORTDATEFORMAT) . ")\nhttps://$domain" . $row['listurl'] . "\n";
353
                $o[$major]['text'] .= $body_text . "\n\n";
354
                $o[$major]['html'] .= '<a href="https://' . $domain . $row['listurl'] . '"><h2 style="line-height: 1.2; font-size: 17px; font-weight: 900;">' . $parentbody . '</h2></a> <span style="margin: 16px 0 0 0; display: block; font-size: 16px;">' . format_date($row['hdate'], SHORTDATEFORMAT) . '</span>';
355
                $o[$major]['html'] .= $body_html . "\n\n";
356
            }
357
            $total++;
358
        }
359
        $count[$major] = $total;
360
361
        if ($any_content) {
362
            # Add data to email_text/html_text
363
            $desc = trim(html_entity_decode($data['searchdescription']));
364
            $desc = trim(preg_replace(['#\(B\d+( OR B\d+)*\)#', '#B\d+( OR B\d+)*#'], '', $desc));
365
            foreach ($o as $major => $body) {
366
                if ($body['text']) {
367
                    $heading_text = $desc . ' : ' . $count[$major] . ' ' . ngettext($sects[$major], $sects_plural[$major], $count[$major]);
368
                    $heading_html = $desc . ' : <strong>' . $count[$major] . '</strong> ' . ngettext($sects[$major], $sects_plural[$major], $count[$major]);
369
370
                    $email_text .= "$heading_text\n" . str_repeat('=', strlen($heading_text)) . "\n\n";
371
                    if ($count[$major] > 3 && $major != 'V') {
372
                        $url = "https://$domain/search/?s=" . urlencode($criteria_raw) . "+section:" . $sects_search[$major] . "&o=d";
373
                        $email_text .= gettext('There are more results than we have shown here.') . ' ' . gettext('See more') . ":\n$url\n\n";
374
                    }
375
                    $email_text .= $body['text'];
376
377
                    $html_text .= '<hr style="height:2px;border-width:0; background-color: #f7f6f5; margin: 30px 0;">';
378
                    $html_text .= '<p style="font-size:16px;">' . $heading_html . '</p>';
379
                    if ($count[$major] > 3 && $major != 'V') {
380
                        $html_text .= '<p style="font-size:16px;">' . gettext('There are more results than we have shown here.') . ' <a href="' . $url . '">' . gettext('See more') . '</a></p>';
381
                    }
382
                    $html_text .= $body['html'];
383
                }
384
            }
385
        }
386
    }
387
}
388
if ($email_text) {
389
    write_and_send_email($current, $email_text, $html_text, $template);
390
}
391
392
mlog("\n");
393
394
$sss = "Active alerts: $active\nEmail lookups: $registered registered, $unregistered unregistered\nQuery lookups: $queries\nSent emails: $sentemails\n";
395
if ($globalsuccess) {
396
    $sss .= 'Everything went swimmingly, in ';
397
} else {
398
    $sss .= 'Something went wrong! Total time: ';
399
}
400
$sss .= (getmicrotime() - $global_start) . "\n\n";
401
mlog($sss);
402
if (!$nomail && !$onlyemail) {
403
    $fp = fopen('alerts-lastsent', 'w');
404
    fwrite($fp, time() . "\n");
405
    fwrite($fp, $max_batch_id);
406
    fclose($fp);
407
    mail(ALERT_STATS_EMAILS, 'Email alert statistics', $sss, 'From: Email Alerts <[email protected]>');
408
}
409
mlog(date('r') . "\n");
410
411
function _sort($a, $b) {
412
    if ($a > $b) {
413
        return 1;
414
    } elseif ($a < $b) {
415
        return -1;
416
    }
417
    return 0;
418
}
419
420
function sort_by_stuff($a, $b) {
421
    # Always have votes first.
422
    if ($a['major'] == 'V' && $b['major'] != 'V') {
423
        return -1;
424
    } elseif ($b['major'] == 'V' && $a['major'] != 'V') {
425
        return 1;
426
    }
427
428
    # Always have future business second..
429
    if ($a['major'] == 'F' && $b['major'] != 'F') {
430
        return -1;
431
    } elseif ($b['major'] == 'F' && $a['major'] != 'F') {
432
        return 1;
433
    }
434
435
    # Otherwise sort firstly by major number (so Commons before NI before SP before Lords)
436
    if ($ret = _sort($a['major'], $b['major'])) {
437
        return $ret;
438
    }
439
440
    # Then by date (most recent first for everything except future, which is the opposite)
441
    if ($a['major'] == 'F') {
442
        if ($ret = _sort($a['hdate'], $b['hdate'])) {
443
            return $ret;
444
        }
445
    } else {
446
        if ($ret = _sort($b['hdate'], $a['hdate'])) {
447
            return $ret;
448
        }
449
    }
450
451
    # Lastly by speech position within a debate.
452
    if ($a['hpos'] == $b['hpos']) {
453
        return 0;
454
    }
455
    return ($a['hpos'] > $b['hpos']) ? 1 : -1;
456
}
457
458
function write_and_send_email($current, $text, $html, $template) {
459
    global $globalsuccess, $sentemails, $nomail, $start_time, $domain;
460
461
    $text .= '====================';
462
    $sentemails++;
463
    mlog("SEND $sentemails : Sending email to $current[email] ... ");
464
    $d = array('to' => $current['email'], 'template' => $template);
465
    $m = array(
466
        'DATA' => $text,
467
        '_HTML_' => $html,
468
        'MANAGE' => "https://$domain/D/" . $current['token'],
469
    );
470
    if (!$nomail) {
471
        $success = send_template_email($d, $m, true, true, $current['lang']); # true = "Precedence: bulk", want bounces
472
        mlog("sent ... ");
473
        # sleep if time between sending mails is less than a certain number of seconds on average
474
        # 0.25 is number of seconds per mail not to be quicker than
475
        if (((time() - $start_time) / $sentemails) < 0.25) {
476
            mlog("pausing ... ");
477
            sleep(1);
478
        }
479
    } else {
480
        mlog($text);
481
        $success = 1;
482
    }
483
    mlog("done\n");
484
    if (!$success) {
485
        $globalsuccess = 0;
486
    }
487
}
488
489
function text_html_to_email($s) {
490
    $s = preg_replace('#</?(i|b|small)>#', '', $s);
491
    $s = preg_replace('#</?span[^>]*>#', '*', $s);
492
    $s = str_replace(
493
        array('&#163;', '&#8211;', '&#8212;', '&#8217;', '<br>'),
494
        array("\xa3", '-', '-', "'", "\n"),
495
        $s
496
    );
497
    return $s;
498
}
499
500
# Switch the language to that of the data/alert
501
function change_locale($lang) {
502
    if ($lang == 'cy') {
503
        setlocale(LC_ALL, 'cy_GB.UTF-8');
504
        putenv('LC_ALL=cy_GB.UTF-8');
505
    } else {
506
        setlocale(LC_ALL, 'en_GB.UTF-8');
507
        putenv('LC_ALL=en_GB.UTF-8');
508
    }
509
}
510