Completed
Push — master ( 922be9...285e65 )
by Nick
41s queued 31s
created

ALERT::email_exists()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 7
CRAP Score 3.0175

Importance

Changes 0
Metric Value
cc 3
eloc 10
nc 3
nop 1
dl 0
loc 15
ccs 7
cts 8
cp 0.875
crap 3.0175
rs 9.9332
c 0
b 0
f 0
1
<?php
2
3
/*
4
5
NO HTML IN THIS FILE!!
6
7
// Name: alert.php
8
// Author:  Richard Allan [email protected]
9
// Version: 0.5 beta
10
// Date: 6th Jan 2005
11
// Description:  This file contains ALERT class.
12
13
The ALERT class allows us to fetch and alter data about any email alert.
14
Functions here:
15
16
ALERT
17
18
    fetch_between($confirmed, $deleted, $start_date, $end_date)	Fetch summary data on alerts created between the dates.
19
    fetch($confirmed, $deleted)					Fetch all alert data from DB.
20
    add($details, $confirmation_email)				Add a new alert to the DB.
21
    send_confirmation_email($details)				Done after add()ing the alert.
22
    email_exists($email)						Checks if an alert exists with a certain email address.
23
    confirm($token)							Confirm a new alert in the DB
24
    delete($token)							Remove an existing alert from the DB
25
    id_exists()							Checks if an alert_id is valid.
26
27
To create a new alert do:
28
    $ALERT = new ALERT;
29
    $ALERT->add();
30
31
You can then access all the alert's variables with appropriately named functions, such as:
32
    $ALERT->email();
33
etc.
34
35
*/
36
37
// CLASS:  ALERT
38
39
class ALERT {
40
41
    public $token_checked = null;
42
    private $alert_id = "";
43
    public $email = "";
44
    public $criteria = "";		// Sets the terms that are used to produce the search results.
45
46
    private $db;
47
48 22
    public function __construct() {
49 22
        $this->db = new ParlDB;
50 22
    }
51
52
// FUNCTION: fetch_between
53
54 1
    public function fetch_between($confirmed, $deleted, $start_date, $end_date) {
55
      // Return summary data on all the alerts that were created between $start_date
56
      // and $end_date (inclusive) and whose confirmed and deleted values match the booleans
57
      // passed in $confirmed and $deleted
58 1
        $q = $this->db->query("SELECT   criteria, count(*) as cnt
59
            FROM     alerts
60
            WHERE    confirmed = :confirmed
61
            AND      deleted = :deleted
62
            AND      created >= :start_date
63
            AND      created <= :end_date
64
            GROUP BY criteria", array(
65 1
            ':confirmed' => $confirmed,
66 1
            ':deleted' => $deleted,
67 1
            ':start_date' => $start_date,
68 1
            ':end_date' => $end_date
69
            ));
70 1
        $data = array();
71 1
        foreach ($q as $row) {
72 1
            $contents = array('criteria' => $row['criteria'], 'count' => $row['cnt']);
73 1
            $data[] = $contents;
74
        }
75 1
        $data = array ('alerts' => $data);
76 1
        return $data;
77
    }
78
79
// FUNCTION: fetch
80
81 2
    public function fetch($confirmed, $deleted) {
82
        // Pass it an alert id and it will fetch data about alerts from the db
83
        // and put it all in the appropriate variables.
84
        // Normal usage is for $confirmed variable to be set to true
85
        // and $deleted variable to be set to false
86
        // so that only live confirmed alerts are chosen.
87
88
        // Look for this alert_id's details.
89 2
        $q = $this->db->query("SELECT alert_id,
90
                        email,
91
                        criteria,
92
                        registrationtoken,
93
                        deleted,
94
                        confirmed
95
                        FROM alerts
96 2
                        WHERE confirmed =" . $confirmed .
97 2
                        " AND deleted=" . $deleted .
98 2
                        ' ORDER BY email');
99
100 2
        $data = array();
101
102 2
        foreach ($q as $row) {
103
            $contents = array(
104 2
                'alert_id' => $row['alert_id'],
105 2
                'email' => $row['email'],
106 2
                'criteria' => $row['criteria'],
107 2
                'registrationtoken' => $row['registrationtoken'],
108 2
                'confirmed' => $row['confirmed'],
109 2
                'deleted' => $row['deleted']
110
            );
111 2
            $data[] = $contents;
112
        }
113 2
        $info = "Alert";
114 2
        $data = array ('info' => $info, 'data' => $data);
115
116 2
        return $data;
117
    }
118
119 3
    public function add($details, $confirmation_email=false, $instantly_confirm=true) {
120
121
        // Adds a new alert's info into the database.
122
        // Then calls another function to send them a confirmation email.
123
        // $details is an associative array of all the alert's details, of the form:
124
        // array (
125
        //		"email" => "[email protected]",
126
        //		"criteria"	=> "speaker:521",
127
        //		etc... using the same keys as the object variable names.
128
        // )
129
130 3
        $criteria = \MySociety\TheyWorkForYou\Utility\Alert::detailsToCriteria($details);
131
132 3
        $q = $this->db->query("SELECT * FROM alerts
133
            WHERE email = :email
134
            AND criteria = :criteria
135
            AND confirmed=1", array(
136 3
                ':email' => $details['email'],
137 3
                ':criteria' => $criteria
138 3
            ))->first();
139 3
        if ($q) {
140 2
            if ($q['deleted']) {
141 1
                $this->db->query("UPDATE alerts SET deleted=0
142
                    WHERE email = :email
143
                    AND criteria = :criteria
144
                    AND confirmed=1", array(
145 1
                        ':email' => $details['email'],
146 1
                        ':criteria' => $criteria
147
                    ));
148 1
                return 1;
149
            } else {
150 1
                return -2;
151
            }
152
        }
153
154 1
        $q = $this->db->query("INSERT INTO alerts (
155
                email, criteria, postcode, deleted, confirmed, created
156
            ) VALUES (
157
                :email,
158
                :criteria,
159
                :pc,
160
                '0', '0', NOW()
161
            )
162
        ", array(
163 1
            ':email' => $details['email'],
164 1
            ':criteria' => $criteria,
165 1
            ':pc' => $details['pc'],
166
            ));
167
168 1
        if ($q->success()) {
169
170
            // Get the alert id so that we can perform the updates for confirmation
171
172 1
            $this->alert_id = $q->insert_id();
173 1
            $this->criteria = $criteria;
174
175
            // We have to set the alert's registration token.
176
            // This will be sent to them via email, so we can confirm they exist.
177
            // The token will be the first 16 characters of a hash.
178
179
            // This gives a code for their email address which is then joined
180
            // to the timestamp so as to provide a unique ID for each alert.
181
182 1
            $token = substr( password_hash($details["email"] . microtime(), PASSWORD_BCRYPT), 29, 16 );
183
184
            // Full stops don't work well at the end of URLs in emails,
185
            // so replace them. We won't be doing anything clever with the hash
186
            // stuff, just need to match this token.
187
188 1
            $this->registrationtoken = strtr($token, '.', 'X');
0 ignored issues
show
Bug Best Practice introduced by
The property registrationtoken does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
189
190
            // Add that to the database.
191
192 1
            $r = $this->db->query("UPDATE alerts
193
                        SET registrationtoken = :registration_token
194
                        WHERE alert_id = :alert_id
195
                        ", array(
196 1
                            ':registration_token' => $this->registrationtoken,
197 1
                            ':alert_id' => $this->alert_id
198
                        ));
199
200 1
            if ($r->success()) {
201
                // Updated DB OK.
202
203 1
                if ($confirmation_email) {
204
                    // Right, send the email...
205
                    $success = $this->send_confirmation_email($details);
206
207
                    if ($success) {
208
                        // Email sent OK
209
                        return 1;
210
                    } else {
211
                        // Couldn't send the email.
212
                        return -1;
213
                    }
214 1
                } elseif ($instantly_confirm) {
215
                    // No confirmation email needed.
216 1
                    $this->db->query("UPDATE alerts
217
                        SET confirmed = '1'
218
                        WHERE alert_id = :alert_id
219
                        ", array(
220 1
                            ':alert_id' => $this->alert_id
221
                        ));
222 1
                    return 1;
223
                }
224
            } else {
225
                // Couldn't add the registration token to the DB.
226
                return -1;
227
            }
228
229
        } else {
230
            // Couldn't add the user's data to the DB.
231
            return -1;
232
        }
233
    }
234
235
// FUNCTION:  send_confirmation_email
236
237
    public function send_confirmation_email($details) {
238
239
        // After we've add()ed an alert we'll be sending them
240
        // a confirmation email with a link to confirm their address.
241
        // $details is the array we just sent to add(), and which it's
242
        // passed on to us here.
243
        // A brief check of the facts...
244
        if (!is_numeric($this->alert_id) ||
245
            !isset($details['email']) ||
246
            $details['email'] == '') {
247
            return false;
248
        }
249
250
        // We prefix the registration token with the alert's id and '-'.
251
        // Not for any particularly good reason, but we do.
252
253
        $urltoken = $this->alert_id . '-' . $this->registrationtoken;
254
255
        if ( isset($details['confirm_base']) && $details['confirm_base'] !== '' ) {
256
            $confirmurl = $details['confirm_base'] . $urltoken;
257
        } else {
258
            $confirmurl = 'https://' . DOMAIN . '/A/' . $urltoken;
259
        }
260
261
        // Arrays we need to send a templated email.
262
        $data = array (
263
            'to' 		=> $details['email'],
264
            'template' 	=> 'alert_confirmation'
265
        );
266
267
        $merge = array (
268
            'FIRSTNAME' 	=> 'THEY WORK FOR YOU',
269
            'LASTNAME' 		=> ' ALERT CONFIRMATION',
270
            'CONFIRMURL'	=> $confirmurl,
271
            'CRITERIA'	=> $this->criteria_pretty()
272
        );
273
274
        $success = send_template_email($data, $merge);
275
        if ($success) {
276
            return true;
277
        } else {
278
            return false;
279
        }
280
    }
281
282
    public function send_already_signedup_email($details) {
283
        $data = array (
284
            'to' 		=> $details['email'],
285
            'template' 	=> 'alert_already_signedup'
286
        );
287
288
        $criteria = \MySociety\TheyWorkForYou\Utility\Alert::detailsToCriteria($details);
289
        $this->criteria = $criteria;
290
291
        $merge = array (
292
            'FIRSTNAME' 	=> 'THEY WORK FOR YOU',
293
            'LASTNAME' 		=> ' ALERT ALREADY SIGNED UP',
294
            'CRITERIA'	=> $this->criteria_pretty()
295
        );
296
297
        $success = send_template_email($data, $merge);
298
        if ($success) {
299
            return true;
300
        } else {
301
            return false;
302
        }
303
    }
304
305 2
    public function fetch_by_mp($email, $pid) {
306 2
        $q = $this->db->query("SELECT alert_id FROM alerts
307
            WHERE confirmed AND NOT deleted
308
            AND email = :email
309
            AND criteria = :criteria", array(
310 2
                ':email' => $email,
311 2
                ':criteria' => 'speaker:' . $pid
312
            ));
313 2
        if ($q->rows() > 0) {
314 1
            return true;
315
        } else {
316 1
            return false;
317
        }
318
    }
319
320 2
    public function email_exists($email) {
321
        // Returns true if there's a user with this email address.
322
323 2
        if ($email != "") {
324 2
            $q = $this->db->query("SELECT alert_id FROM alerts
325
                WHERE email = :email", array(
326 2
                    ':email' => $email
327
                ));
328 2
            if ($q->rows() > 0) {
329 1
                return true;
330
            } else {
331 1
                return false;
332
            }
333
        } else {
334
            return false;
335
        }
336
337
    }
338
339 12
    public function check_token($token) {
340 12
        if (!is_null($this->token_checked))
341
            return $this->token_checked;
342
343 12
        $arg = strstr($token, '::') ? '::' : '-';
344 12
        $token_parts = explode($arg, $token);
345 12
        if (count($token_parts) != 2)
346 2
            return false;
347
348 10
        list($alert_id, $registrationtoken) = $token_parts;
349 10
        if (!is_numeric($alert_id) || !$registrationtoken)
350
            return false;
351
352 10
        $q = $this->db->query("SELECT alert_id, email, criteria
353
                        FROM alerts
354
                        WHERE alert_id = :alert_id
355
                        AND registrationtoken = :registration_token
356
                        ", array(
357 10
                            ':alert_id' => $alert_id,
358 10
                            ':registration_token' => $registrationtoken
359 10
                        ))->first();
360 10
        if (!$q) {
361 5
            $this->token_checked = false;
362
        } else {
363 5
            $this->token_checked = array(
364 5
                'id' => $q['alert_id'],
365 5
                'email' => $q['email'],
366 5
                'criteria' => $q['criteria'],
367
            );
368
        }
369
370 10
        return $this->token_checked;
371
    }
372
373
    public function fetch_by_token($confirmation) {
374
        $q = $this->db->query("SELECT alert_id, email, criteria
375
                        FROM alerts
376
                        WHERE registrationtoken = :registration_token
377
                        ", array(
378
                            ':registration_token' => $confirmation
379
                        )
380
                    )->first();
381
382
        if (!$q) {
383
            return false;
384
        } else {
385
            return array(
386
                'id' => $q['alert_id'],
387
                'email' => $q['email'],
388
                'criteria' => $q['criteria'],
389
            );
390
        }
391
    }
392
393
    // The user has clicked the link in their confirmation email
394
    // and the confirm page has passed the token from the URL to here.
395
    // If all goes well the alert will be confirmed.
396
    // The alert will be active when scripts run each day to send the actual emails.
397 2
    public function confirm($token) {
398 2
        if (!($alert = $this->check_token($token))) return false;
399 1
        $this->criteria = $alert['criteria'];
400 1
        $this->email = $alert['email'];
401 1
        $r = $this->db->query("UPDATE alerts SET confirmed = 1, deleted = 0 WHERE alert_id = :alert_id", array(
402 1
            ':alert_id' => $alert['id']
403
            ));
404
405 1
        return $r->success();
406
    }
407
408
    // The user has clicked the link in their delete confirmation email
409
    // and the deletion page has passed the token from the URL to here.
410
    // If all goes well the alert will be deleted.
411 2
    public function delete($token) {
412 2
        if (!($alert = $this->check_token($token))) return false;
413 1
        $r = $this->db->query("DELETE FROM alerts WHERE alert_id = :alert_id", array(
414 1
            ':alert_id' => $alert['id']
415
            ));
416
417 1
        return $r->success();
418
    }
419
420
    public function delete_all($token) {
421
        if (!($alert = $this->check_token($token))) return false;
422
        $r = $this->db->query("DELETE FROM alerts WHERE email = :email", array(
423
            ':email' => $alert['email']
424
            ));
425
426
        return $r->success();
427
    }
428
429 2
    public function suspend($token) {
430 2
        if (!($alert = $this->check_token($token))) return false;
431 1
        $r = $this->db->query("UPDATE alerts SET deleted = 2 WHERE alert_id = :alert_id", array(
432 1
            ':alert_id' => $alert['id']
433
            ));
434
435 1
        return $r->success();
436
    }
437
438 2
    public function resume($token) {
439 2
        if (!($alert = $this->check_token($token))) return false;
440 1
        $r = $this->db->query("UPDATE alerts SET deleted = 0 WHERE alert_id = :alert_id", array(
441 1
            ':alert_id' => $alert['id']
442
            ));
443
444 1
        return $r->success();
445
    }
446
447
    // Getters
448
    public function email() { return $this->email; }
449
    public function criteria() { return $this->criteria; }
450
    public function criteria_pretty($html = false) {
451
        $criteria = explode(' ',$this->criteria);
452
        $spokenby = array_values(\MySociety\TheyWorkForYou\Utility\Search::speakerNamesForIDs($this->criteria));
453
        $words = array();
454
        foreach ($criteria as $c) {
455
            if (!preg_match('#^speaker:(\d+)#',$c,$m)) {
456
                $words[] = $c;
457
            }
458
        }
459
        $criteria = '';
460
        if (count($words)) $criteria .= ($html?'<li>':'* ') . 'Mentions of [' . implode(' ', $words) . ']' . ($html?'</li>':'') . "\n";
461
        if ($spokenby) $criteria .= ($html?'<li>':'* ') . "Things by " . implode(' or ', $spokenby) . ($html?'</li>':'') . "\n";
0 ignored issues
show
Bug Best Practice introduced by
The expression $spokenby of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
462
        return $criteria;
463
    }
464
465
}
466