Passed
Push — master ( d8531e...e5f489 )
by Matthew
04:14
created

ALERT::add()   B

Complexity

Conditions 8
Paths 8

Size

Total Lines 116
Code Lines 47

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 32
CRAP Score 8.2518

Importance

Changes 0
Metric Value
cc 8
eloc 47
c 0
b 0
f 0
nc 8
nop 3
dl 0
loc 116
rs 7.9119
ccs 32
cts 38
cp 0.8421
crap 8.2518

How to fix   Long Method   

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
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
                        lang,
94
                        deleted,
95
                        confirmed
96
                        FROM alerts
97 2
                        WHERE confirmed =" . $confirmed .
98 2
                        " AND deleted=" . $deleted .
99 2
                        ' ORDER BY email');
100
101 2
        $data = $q->fetchAll();
102 2
        $info = "Alert";
103 2
        $data = array ('info' => $info, 'data' => $data);
104
105 2
        return $data;
106
    }
107
108 3
    public function add($details, $confirmation_email=false, $instantly_confirm=true) {
109
110
        // Adds a new alert's info into the database.
111
        // Then calls another function to send them a confirmation email.
112
        // $details is an associative array of all the alert's details, of the form:
113
        // array (
114
        //		"email" => "[email protected]",
115
        //		"criteria"	=> "speaker:521",
116
        //		etc... using the same keys as the object variable names.
117
        // )
118
119 3
        $criteria = \MySociety\TheyWorkForYou\Utility\Alert::detailsToCriteria($details);
120
121 3
        $q = $this->db->query("SELECT * FROM alerts
122
            WHERE email = :email
123
            AND criteria = :criteria
124
            AND confirmed=1", array(
125 3
                ':email' => $details['email'],
126 3
                ':criteria' => $criteria
127 3
            ))->first();
128 3
        if ($q) {
129 2
            if ($q['deleted']) {
130 1
                $this->db->query("UPDATE alerts SET deleted=0
131
                    WHERE email = :email
132
                    AND criteria = :criteria
133
                    AND confirmed=1", array(
134 1
                        ':email' => $details['email'],
135 1
                        ':criteria' => $criteria
136
                    ));
137 1
                return 1;
138
            } else {
139 1
                return -2;
140
            }
141
        }
142
143 1
        $q = $this->db->query("INSERT INTO alerts (
144
                email, criteria, postcode, lang, deleted, confirmed, created
145
            ) VALUES (
146
                :email,
147
                :criteria,
148
                :pc,
149
                :lang,
150
                '0', '0', NOW()
151
            )
152
        ", array(
153 1
            ':email' => $details['email'],
154 1
            ':criteria' => $criteria,
155 1
            ':pc' => $details['pc'],
156
            ':lang' => LANGUAGE,
157
            ));
158
159 1
        if ($q->success()) {
160
161
            // Get the alert id so that we can perform the updates for confirmation
162
163 1
            $this->alert_id = $q->insert_id();
164 1
            $this->criteria = $criteria;
165
166
            // We have to set the alert's registration token.
167
            // This will be sent to them via email, so we can confirm they exist.
168
            // The token will be the first 16 characters of a hash.
169
170
            // This gives a code for their email address which is then joined
171
            // to the timestamp so as to provide a unique ID for each alert.
172
173 1
            $token = substr( password_hash($details["email"] . microtime(), PASSWORD_BCRYPT), 29, 16 );
174
175
            // Full stops don't work well at the end of URLs in emails, so
176
            // replace them. And double slash would be treated as single and
177
            // not work either. We won't be doing anything clever with the hash
178
            // stuff, just need to match this token.
179 1
            $token = strtr($token, './', 'Xx');
180 1
            $this->registrationtoken = $token;
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...
181
182
            // Add that to the database.
183
184 1
            $r = $this->db->query("UPDATE alerts
185
                        SET registrationtoken = :registration_token
186
                        WHERE alert_id = :alert_id
187
                        ", array(
188 1
                            ':registration_token' => $this->registrationtoken,
189 1
                            ':alert_id' => $this->alert_id
190
                        ));
191
192 1
            if ($r->success()) {
193
                // Updated DB OK.
194
195 1
                if ($confirmation_email) {
196
                    // Right, send the email...
197
                    $success = $this->send_confirmation_email($details);
198
199
                    if ($success) {
200
                        // Email sent OK
201
                        return 1;
202
                    } else {
203
                        // Couldn't send the email.
204
                        return -1;
205
                    }
206 1
                } elseif ($instantly_confirm) {
207
                    // No confirmation email needed.
208 1
                    $this->db->query("UPDATE alerts
209
                        SET confirmed = '1'
210
                        WHERE alert_id = :alert_id
211
                        ", array(
212 1
                            ':alert_id' => $this->alert_id
213
                        ));
214 1
                    return 1;
215
                }
216
            } else {
217
                // Couldn't add the registration token to the DB.
218
                return -1;
219
            }
220
221
        } else {
222
            // Couldn't add the user's data to the DB.
223
            return -1;
224
        }
225
    }
226
227
// FUNCTION:  send_confirmation_email
228
229
    public function send_confirmation_email($details) {
230
231
        if (in_array($details['email'], ALERT_NO_EMAIL)) {
0 ignored issues
show
Bug introduced by
The constant ALERT_NO_EMAIL was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
232
            return false;
233
        }
234
235
        // After we've add()ed an alert we'll be sending them
236
        // a confirmation email with a link to confirm their address.
237
        // $details is the array we just sent to add(), and which it's
238
        // passed on to us here.
239
        // A brief check of the facts...
240
        if (!is_numeric($this->alert_id) ||
241
            !isset($details['email']) ||
242
            $details['email'] == '') {
243
            return false;
244
        }
245
246
        // We prefix the registration token with the alert's id and '-'.
247
        // Not for any particularly good reason, but we do.
248
249
        $urltoken = $this->alert_id . '-' . $this->registrationtoken;
250
251
        if ( isset($details['confirm_base']) && $details['confirm_base'] !== '' ) {
252
            $confirmurl = $details['confirm_base'] . $urltoken;
253
        } else {
254
            $confirmurl = 'https://' . DOMAIN . '/A/' . $urltoken;
255
        }
256
257
        // Arrays we need to send a templated email.
258
        $data = array (
259
            'to' 		=> $details['email'],
260
            'template' 	=> 'alert_confirmation'
261
        );
262
263
        $merge = array (
264
            'CONFIRMURL'	=> $confirmurl,
265
            'CRITERIA'	=> $this->criteria_pretty()
266
        );
267
268
        $success = send_template_email($data, $merge);
269
        if ($success) {
270
            return true;
271
        } else {
272
            return false;
273
        }
274
    }
275
276
    public function send_already_signedup_email($details) {
277
        $data = array (
278
            'to' 		=> $details['email'],
279
            'template' 	=> 'alert_already_signedup'
280
        );
281
282
        $criteria = \MySociety\TheyWorkForYou\Utility\Alert::detailsToCriteria($details);
283
        $this->criteria = $criteria;
284
285
        $merge = array (
286
            'CRITERIA'	=> $this->criteria_pretty()
287
        );
288
289
        $success = send_template_email($data, $merge);
290
        if ($success) {
291
            return true;
292
        } else {
293
            return false;
294
        }
295
    }
296
297 2
    public function fetch_by_mp($email, $pid) {
298 2
        $q = $this->db->query("SELECT alert_id FROM alerts
299
            WHERE confirmed AND NOT deleted
300
            AND email = :email
301
            AND criteria = :criteria", array(
302 2
                ':email' => $email,
303 2
                ':criteria' => 'speaker:' . $pid
304
            ));
305 2
        if ($q->rows() > 0) {
306 1
            return true;
307
        } else {
308 1
            return false;
309
        }
310
    }
311
312 2
    public function email_exists($email) {
313
        // Returns true if there's a user with this email address.
314
315 2
        if ($email != "") {
316 2
            $q = $this->db->query("SELECT alert_id FROM alerts
317
                WHERE email = :email", array(
318 2
                    ':email' => $email
319
                ));
320 2
            if ($q->rows() > 0) {
321 1
                return true;
322
            } else {
323 1
                return false;
324
            }
325
        } else {
326
            return false;
327
        }
328
329
    }
330
331 12
    public function check_token($token) {
332 12
        if (!is_null($this->token_checked)) {
333
            return $this->token_checked;
334
        }
335
336 12
        $arg = strstr($token, '::') ? '::' : '-';
337 12
        $token_parts = explode($arg, $token);
338 12
        if (count($token_parts) != 2) {
339 2
            return false;
340
        }
341
342 10
        list($alert_id, $registrationtoken) = $token_parts;
343 10
        if (!is_numeric($alert_id) || !$registrationtoken) {
344
            return false;
345
        }
346
347 10
        $q = $this->db->query("SELECT alert_id, email, criteria
348
                        FROM alerts
349
                        WHERE alert_id = :alert_id
350
                        AND registrationtoken = :registration_token
351
                        ", array(
352 10
                            ':alert_id' => $alert_id,
353 10
                            ':registration_token' => $registrationtoken
354 10
                        ))->first();
355 10
        if (!$q) {
356 5
            $this->token_checked = false;
357
        } else {
358 5
            $this->token_checked = array(
359 5
                'id' => $q['alert_id'],
360 5
                'email' => $q['email'],
361 5
                'criteria' => $q['criteria'],
362
            );
363
        }
364
365 10
        return $this->token_checked;
366
    }
367
368
    public function fetch_by_token($confirmation) {
369
        $q = $this->db->query("SELECT alert_id, email, criteria
370
                        FROM alerts
371
                        WHERE registrationtoken = :registration_token
372
                        ", array(
373
                            ':registration_token' => $confirmation
374
                        )
375
                    )->first();
376
377
        if (!$q) {
378
            return false;
379
        } else {
380
            return array(
381
                'id' => $q['alert_id'],
382
                'email' => $q['email'],
383
                'criteria' => $q['criteria'],
384
            );
385
        }
386
    }
387
388
    // The user has clicked the link in their confirmation email
389
    // and the confirm page has passed the token from the URL to here.
390
    // If all goes well the alert will be confirmed.
391
    // The alert will be active when scripts run each day to send the actual emails.
392 2
    public function confirm($token) {
393 2
        if (!($alert = $this->check_token($token))) {
394 1
            return false;
395
        }
396 1
        $this->criteria = $alert['criteria'];
397 1
        $this->email = $alert['email'];
398 1
        $r = $this->db->query("UPDATE alerts SET confirmed = 1, deleted = 0 WHERE alert_id = :alert_id", array(
399 1
            ':alert_id' => $alert['id']
400
            ));
401
402 1
        return $r->success();
403
    }
404
405
    // The user has clicked the link in their delete confirmation email
406
    // and the deletion page has passed the token from the URL to here.
407
    // If all goes well the alert will be deleted.
408 2
    public function delete($token) {
409 2
        if (!($alert = $this->check_token($token))) {
410 1
            return false;
411
        }
412 1
        $r = $this->db->query("DELETE FROM alerts WHERE alert_id = :alert_id", array(
413 1
            ':alert_id' => $alert['id']
414
            ));
415
416 1
        return $r->success();
417
    }
418
419
    public function delete_all($token) {
420
        if (!($alert = $this->check_token($token))) {
421
            return false;
422
        }
423
        $r = $this->db->query("DELETE FROM alerts WHERE email = :email", array(
424
            ':email' => $alert['email']
425
            ));
426
427
        return $r->success();
428
    }
429
430 2
    public function suspend($token) {
431 2
        if (!($alert = $this->check_token($token))) {
432 1
            return false;
433
        }
434 1
        $r = $this->db->query("UPDATE alerts SET deleted = 2 WHERE alert_id = :alert_id", array(
435 1
            ':alert_id' => $alert['id']
436
            ));
437
438 1
        return $r->success();
439
    }
440
441 2
    public function resume($token) {
442 2
        if (!($alert = $this->check_token($token))) {
443 1
            return false;
444
        }
445 1
        $r = $this->db->query("UPDATE alerts SET deleted = 0 WHERE alert_id = :alert_id", array(
446 1
            ':alert_id' => $alert['id']
447
            ));
448
449 1
        return $r->success();
450
    }
451
452
    // Getters
453
    public function email() { return $this->email; }
454
    public function criteria() { return $this->criteria; }
455
    public function criteria_pretty($html = false) {
456
        $criteria = explode(' ',$this->criteria);
457
        $spokenby = array_values(\MySociety\TheyWorkForYou\Utility\Search::speakerNamesForIDs($this->criteria));
458
        $words = array();
459
        foreach ($criteria as $c) {
460
            if (!preg_match('#^speaker:(\d+)#',$c,$m)) {
461
                $words[] = $c;
462
            }
463
        }
464
        $criteria = '';
465
        if (count($words)) {
466
            $criteria .= ($html?'<li>':'* ') . sprintf(gettext('Mentions of [%s]'), implode(' ', $words)) . ($html?'</li>':'') . "\n";
467
        }
468
        if ($spokenby) {
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...
469
            $criteria .= ($html?'<li>':'* ') . sprintf(gettext("Things by %s"), implode(' or ', $spokenby)) . ($html?'</li>':'') . "\n";
470
        }
471
        return $criteria;
472
    }
473
474
}
475