Passed
Pull Request — master (#1700)
by Struan
04:10
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
        // After we've add()ed an alert we'll be sending them
232
        // a confirmation email with a link to confirm their address.
233
        // $details is the array we just sent to add(), and which it's
234
        // passed on to us here.
235
        // A brief check of the facts...
236
        if (!is_numeric($this->alert_id) ||
237
            !isset($details['email']) ||
238
            $details['email'] == '') {
239
            return false;
240
        }
241
242
        // We prefix the registration token with the alert's id and '-'.
243
        // Not for any particularly good reason, but we do.
244
245
        $urltoken = $this->alert_id . '-' . $this->registrationtoken;
246
247
        if ( isset($details['confirm_base']) && $details['confirm_base'] !== '' ) {
248
            $confirmurl = $details['confirm_base'] . $urltoken;
249
        } else {
250
            $confirmurl = 'https://' . DOMAIN . '/A/' . $urltoken;
251
        }
252
253
        // Arrays we need to send a templated email.
254
        $data = array (
255
            'to' 		=> $details['email'],
256
            'template' 	=> 'alert_confirmation'
257
        );
258
259
        $merge = array (
260
            'CONFIRMURL'	=> $confirmurl,
261
            'CRITERIA'	=> $this->criteria_pretty()
262
        );
263
264
        $success = send_template_email($data, $merge);
265
        if ($success) {
266
            return true;
267
        } else {
268
            return false;
269
        }
270
    }
271
272
    public function send_already_signedup_email($details) {
273
        $data = array (
274
            'to' 		=> $details['email'],
275
            'template' 	=> 'alert_already_signedup'
276
        );
277
278
        $criteria = \MySociety\TheyWorkForYou\Utility\Alert::detailsToCriteria($details);
279
        $this->criteria = $criteria;
280
281
        $merge = array (
282
            'CRITERIA'	=> $this->criteria_pretty()
283
        );
284
285
        $success = send_template_email($data, $merge);
286
        if ($success) {
287
            return true;
288
        } else {
289
            return false;
290
        }
291
    }
292
293 2
    public function fetch_by_mp($email, $pid) {
294 2
        $q = $this->db->query("SELECT alert_id FROM alerts
295
            WHERE confirmed AND NOT deleted
296
            AND email = :email
297
            AND criteria = :criteria", array(
298 2
                ':email' => $email,
299 2
                ':criteria' => 'speaker:' . $pid
300
            ));
301 2
        if ($q->rows() > 0) {
302 1
            return true;
303
        } else {
304 1
            return false;
305
        }
306
    }
307
308 2
    public function email_exists($email) {
309
        // Returns true if there's a user with this email address.
310
311 2
        if ($email != "") {
312 2
            $q = $this->db->query("SELECT alert_id FROM alerts
313
                WHERE email = :email", array(
314 2
                    ':email' => $email
315
                ));
316 2
            if ($q->rows() > 0) {
317 1
                return true;
318
            } else {
319 1
                return false;
320
            }
321
        } else {
322
            return false;
323
        }
324
325
    }
326
327 12
    public function check_token($token) {
328 12
        if (!is_null($this->token_checked)) {
329
            return $this->token_checked;
330
        }
331
332 12
        $arg = strstr($token, '::') ? '::' : '-';
333 12
        $token_parts = explode($arg, $token);
334 12
        if (count($token_parts) != 2) {
335 2
            return false;
336
        }
337
338 10
        list($alert_id, $registrationtoken) = $token_parts;
339 10
        if (!is_numeric($alert_id) || !$registrationtoken) {
340
            return false;
341
        }
342
343 10
        $q = $this->db->query("SELECT alert_id, email, criteria
344
                        FROM alerts
345
                        WHERE alert_id = :alert_id
346
                        AND registrationtoken = :registration_token
347
                        ", array(
348 10
                            ':alert_id' => $alert_id,
349 10
                            ':registration_token' => $registrationtoken
350 10
                        ))->first();
351 10
        if (!$q) {
352 5
            $this->token_checked = false;
353
        } else {
354 5
            $this->token_checked = array(
355 5
                'id' => $q['alert_id'],
356 5
                'email' => $q['email'],
357 5
                'criteria' => $q['criteria'],
358
            );
359
        }
360
361 10
        return $this->token_checked;
362
    }
363
364
    public function fetch_by_token($confirmation) {
365
        $q = $this->db->query("SELECT alert_id, email, criteria
366
                        FROM alerts
367
                        WHERE registrationtoken = :registration_token
368
                        ", array(
369
                            ':registration_token' => $confirmation
370
                        )
371
                    )->first();
372
373
        if (!$q) {
374
            return false;
375
        } else {
376
            return array(
377
                'id' => $q['alert_id'],
378
                'email' => $q['email'],
379
                'criteria' => $q['criteria'],
380
            );
381
        }
382
    }
383
384
    // The user has clicked the link in their confirmation email
385
    // and the confirm page has passed the token from the URL to here.
386
    // If all goes well the alert will be confirmed.
387
    // The alert will be active when scripts run each day to send the actual emails.
388 2
    public function confirm($token) {
389 2
        if (!($alert = $this->check_token($token))) {
390 1
            return false;
391
        }
392 1
        $this->criteria = $alert['criteria'];
393 1
        $this->email = $alert['email'];
394 1
        $r = $this->db->query("UPDATE alerts SET confirmed = 1, deleted = 0 WHERE alert_id = :alert_id", array(
395 1
            ':alert_id' => $alert['id']
396
            ));
397
398 1
        return $r->success();
399
    }
400
401
    // The user has clicked the link in their delete confirmation email
402
    // and the deletion page has passed the token from the URL to here.
403
    // If all goes well the alert will be deleted.
404 2
    public function delete($token) {
405 2
        if (!($alert = $this->check_token($token))) {
406 1
            return false;
407
        }
408 1
        $r = $this->db->query("DELETE FROM alerts WHERE alert_id = :alert_id", array(
409 1
            ':alert_id' => $alert['id']
410
            ));
411
412 1
        return $r->success();
413
    }
414
415
    public function delete_all($token) {
416
        if (!($alert = $this->check_token($token))) {
417
            return false;
418
        }
419
        $r = $this->db->query("DELETE FROM alerts WHERE email = :email", array(
420
            ':email' => $alert['email']
421
            ));
422
423
        return $r->success();
424
    }
425
426 2
    public function suspend($token) {
427 2
        if (!($alert = $this->check_token($token))) {
428 1
            return false;
429
        }
430 1
        $r = $this->db->query("UPDATE alerts SET deleted = 2 WHERE alert_id = :alert_id", array(
431 1
            ':alert_id' => $alert['id']
432
            ));
433
434 1
        return $r->success();
435
    }
436
437 2
    public function resume($token) {
438 2
        if (!($alert = $this->check_token($token))) {
439 1
            return false;
440
        }
441 1
        $r = $this->db->query("UPDATE alerts SET deleted = 0 WHERE alert_id = :alert_id", array(
442 1
            ':alert_id' => $alert['id']
443
            ));
444
445 1
        return $r->success();
446
    }
447
448
    // Getters
449
    public function email() { return $this->email; }
450
    public function criteria() { return $this->criteria; }
451
    public function criteria_pretty($html = false) {
452
        $criteria = explode(' ',$this->criteria);
453
        $spokenby = array_values(\MySociety\TheyWorkForYou\Utility\Search::speakerNamesForIDs($this->criteria));
454
        $words = array();
455
        foreach ($criteria as $c) {
456
            if (!preg_match('#^speaker:(\d+)#',$c,$m)) {
457
                $words[] = $c;
458
            }
459
        }
460
        $criteria = '';
461
        if (count($words)) {
462
            $criteria .= ($html?'<li>':'* ') . sprintf(gettext('Mentions of [%s]'), implode(' ', $words)) . ($html?'</li>':'') . "\n";
463
        }
464
        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...
465
            $criteria .= ($html?'<li>':'* ') . sprintf(gettext("Things by %s"), implode(' or ', $spokenby)) . ($html?'</li>':'') . "\n";
466
        }
467
        return $criteria;
468
    }
469
470
}
471