source: trunk/scripts/send-newsletter @ 1184

Revision 1170, 14.2 KB checked in by bogdan2412, 3 months ago (diff)

Some more fixes from live.

  • Made finfo only return the mime type (no charset information), since most of our code expects this behaviour. Changed check-attachments script so that it optionally checks that the stored mimetypes agree with the stored files. This helped discover a bug in the attach-endline-fix which was skiping files that were small enough that mime was not recognizing them as plain text.
  • Fixed a bug with the textblock edit button not displaying the correct edit link for pages with round or task security.
  • Replaced deprecated functions from mailing code base.
  • Added an 'admin' tab to the top navigation bar.
  • Fixed random unwanted characters in common/round.php
  • Property svn:eol-style set to native
  • Property svn:executable set to *
Line 
1#! /usr/bin/env php
2<?php
3
4require_once(dirname($argv[0]) . "/utilities.php");
5require_once(IA_ROOT_DIR . 'common/external_libs/class.phpmailer.php');
6require_once(IA_ROOT_DIR."common/db/user.php");
7require_once(IA_ROOT_DIR."common/db/textblock.php");
8require_once(IA_ROOT_DIR."common/user.php");
9require_once(IA_ROOT_DIR."common/rating.php");
10require_once(IA_ROOT_DIR."common/email.php");
11require_once(IA_ROOT_DIR."common/newsletter.php");
12require_once(IA_ROOT_DIR."www/url.php");
13db_connect();
14
15// Configuration section.
16
17define("IA_NEWSLETTER_FROM", "newsletter@infoarena.ro");
18define("IA_NEWSLETTER_FROM_NAME", "Newsletter Infoarena");
19// how many emails to send at once (with no delay)
20define("IA_BURST_LENGTH", 25);
21// pause (milliseconds i.e. 10^-3) between bursts.
22define("IA_BURST_DELAY", 500);
23// place where we store newsletter logs
24define("IA_NEWSLETTER_LOGDIR", IA_ROOT_DIR."scripts/newsletter-logs/");
25// application to use for previewing the HTML newsletter
26define("IA_NEWSLETTER_PREVIEW_APP", "elinks");
27
28// Create PHPMailer object from newsletter textblock, and for given recipient.
29function phpmail_create($textblock, $recipient) {
30    log_assert_valid(user_validate($recipient));
31    log_assert_valid(textblock_validate($textblock));
32    $phpmail = new PHPMailer();
33    phpmail_config($phpmail);
34    $phpmail->Subject = newsletter_subject($textblock, $recipient);
35    $phpmail->AltBody = newsletter_body_alternate($textblock, $recipient);
36    $phpmail->Body = newsletter_body_html($textblock, $recipient);
37    $phpmail->AddAddress($recipient['email'], $recipient['full_name']);
38    return $phpmail;
39}
40
41// Configure a PHPMailer object.
42function phpmail_config(&$phpmail) {
43    if (IA_SMTP_ENABLED) {
44        $phpmail->IsSMTP();
45        $phpmail->Host = IA_SMTP_HOST;
46        $phpmail->Port = IA_SMTP_PORT;
47    } else {
48        // Use PHP mail(...) function.
49        $phpmail->IsMail();
50    }
51    $phpmail->From = IA_NEWSLETTER_FROM;
52    $phpmail->FromName = IA_NEWSLETTER_FROM_NAME;
53    $phpmail->CharSet = "UTF-8";
54}
55
56// return array with valid subscriber list identifiers
57function list_valid_ids() {
58    $valid = array(
59        'all', 'admins', 'rated', 'test', 'review'
60    );
61    return $valid;
62}
63
64function list_get($list_id) {
65    switch ($list_id) {
66        case 'all':
67            // all subscribers that agree to receive newsletters
68            $query = "SELECT * FROM ia_user WHERE newsletter = 1";
69            return db_fetch_all($query);
70
71        case 'admins':
72            // all administrators
73            $query = "SELECT * FROM ia_user
74                      WHERE 'admin' = security_level AND newsletter = 1
75                      ORDER BY full_name";
76            return db_fetch_all($query);
77
78        case 'rated':
79            // all rated users that agree to receive newsletters
80            $query = "SELECT * FROM ia_user
81                      WHERE 0 < rating_cache AND newsletter = 1
82                      ORDER BY rating_cache DESC";
83            return db_fetch_all($query);
84
85        case 'review':
86            // a small set of users. meant for reviewing
87            $usernames = array('wickedman', 'domino', 'silviug', 'Cosmin');
88            $query = "SELECT * FROM ia_user
89                      WHERE username IN ('".join("', '", $usernames)."')
90                      ORDER BY full_name";
91            return db_fetch_all($query);
92
93        case 'test':
94            // test the newsletter in various email clients
95            $usernames = array('gmail_test', 'yahoo_test', 'hotmail_test',
96                    'k_test');
97            $query = "SELECT * FROM ia_user
98                      WHERE username IN ('".join("', '", $usernames)."')
99                      ORDER BY full_name";
100            return db_fetch_all($query);
101
102        default:
103            log_error("Invalid list id!");
104    }
105}
106
107// Preview email for a given recipient
108// When recipient is null, no tags are replaced
109function preview_email($textblock, $recipient, $use_external_app = true) {
110    log_assert_valid(user_validate($recipient));
111    log_assert_valid(textblock_validate($textblock));
112    $phpmail = phpmail_create($textblock, $recipient);
113    if (function_exists('sys_get_temp_dir')) {
114        $temp_dir = sys_get_temp_dir();
115    } else {
116        $temp_dir = '/tmp';
117    }
118    $temp_filename = tempnam($temp_dir, 'ia-newsletter-preview-');
119    file_put_contents($temp_filename, $phpmail->Body);
120
121    echo "\n\n===  e-mail ========================================\n";
122    echo "From   : {$phpmail->From}\n";
123    if ($recipient) {
124        echo "To     : ".$recipient['email']."\n";
125    }
126    echo "Subject: {$phpmail->Subject}\n";
127    echo "---  alternate text --------------------------------\n";
128    echo wordwrap($phpmail->AltBody, IA_EMAIL_WORDRAP)."\n";
129    echo "--- /alternate text --------------------------------\n";
130    echo "=== /e-mail ========================================\n\n";
131
132    // warn users if word-wrapping is needed
133
134    if (wordwrap($phpmail->AltBody, IA_EMAIL_WORDRAP) != $phpmail->AltBody) {
135        log_print("WARNING: Please word-wrap the text to ".IA_EMAIL_WORDRAP
136                  ." characters per line!\n");
137    }
138
139    // display HTML body in preview app
140
141    if ($use_external_app) {
142        system(IA_NEWSLETTER_PREVIEW_APP." ".$temp_filename);
143        unlink($temp_filename);
144    }
145}
146
147// Format recipient to display it nicely on screen / log
148function recipient_str($recipient) {
149    log_assert_valid(user_validate($recipient));
150    return '<'.$recipient['email'].'> ['.$recipient['username']
151            .'] '.$recipient['full_name'];
152}
153
154// each newsletter has its own log
155// put $msg into $page_name's log
156function nlog($newsletter_id, $msg) {
157    $fd = fopen(IA_NEWSLETTER_LOGDIR.$newsletter_id, "a");
158    log_assert($fd);
159    fputs($fd, $msg."\n");
160    fclose($fd);
161}
162
163// Tells whether a newsletter log exists
164function nlog_exists($newsletter_id) {
165    $fname = IA_NEWSLETTER_LOGDIR.$newsletter_id;
166    return file_exists($fname);
167}
168
169// Display newsletter log
170function nlog_view($newsletter_id) {
171    $fname = IA_NEWSLETTER_LOGDIR.$newsletter_id;
172    log_assert(file_exists($fname));
173    $buffer = file_get_contents($fname);
174    echo $buffer;
175}
176
177function log_putch($char) {
178    log_assert(1 == strlen($char));
179    static $lcount = 0;
180
181    $lcount++;
182    echo $char;
183    if (0 == $lcount % 79) {
184        echo "\n";
185    }
186}
187
188/// entry point
189// ---------------------------------------------------------------------
190
191// read page name
192if (2 != $argc) {
193    log_error("usage: ./send-newsletter wiki-page-name");
194}
195$page_name = $argv[1];
196
197// validate $page_name
198$prefix = 'newsletter/';
199if ($prefix != substr($page_name, 0, strlen($prefix))) {
200    log_error("Newsletter page names should start with $prefix");
201}
202$textblock = textblock_get_revision($page_name);
203if (!$textblock) {
204    log_error("Invalid page name!");
205}
206$newsletter_id = substr($page_name, strlen($prefix));
207
208// small preview before entering main menu
209preview_email($textblock, newsletter_anonymous_user(), false);
210
211// check log
212// It is possible that script was interrupted so warn user to resume
213if (nlog_exists($newsletter_id)) {
214    log_print("WARNING: This newsletter already has a log! It is "
215              ."possible that a prior mass mail process failed.\n");
216    if (read_bool("Would you like to inspect the log?", true)) {
217        nlog_view($newsletter_id);
218    }
219}
220
221// main menu
222while (true) {
223    $cmd = read_line("newsletter>");
224    $elems = explode(" ", $cmd);
225    if (1 < count($elems)) {
226        $cmd = $elems[0];
227        array_shift($elems);
228        $param = join(" ", $elems);
229    }
230    else {
231        $param = null;
232    }
233
234    switch ($cmd) {
235        case "quit":
236            // guess what?
237            log_print("Bye!");
238            die();
239            break;
240
241        case "preview":
242            // preview email as it would be received by a user
243            if ($param) {
244                $user = user_get_by_username($param);
245                if (!$user) {
246                    log_print("No such user!");
247                    break;
248                }
249            }
250            else {
251                $user = newsletter_anonymous_user();
252            }
253            preview_email($textblock, $user);
254            break;
255
256        case "count":
257            // count subscribers in a given list
258            if (!$param) {
259                log_print('Forgot to specify subscriber list');
260                break;
261            }
262            if (!in_array($param, list_valid_ids())) {
263                log_print("No such list!");
264            }
265            $list = list_get($param);
266            log_print(count($list)." subscribers in this list\n");
267            if (!$param) {
268                log_print('Forgot to specify subscriber list');
269                break;
270            }
271            if (!in_array($param, list_valid_ids())) {
272                log_print("No such list!");
273            }
274            $list = list_get($param);
275            $i = 0;
276            foreach ($list as $recipient) {
277                if (!is_valid_email($recipient['email'])) {
278                    $status = 'INVALID';
279                }
280                else {
281                    $status = 'ok     ';
282                }
283                log_print($i."\t".$status."\t".recipient_str($recipient));
284                $i++;
285            }
286            log_print("\n".count($list)." subscribers in this list\n");
287            break;
288
289        case "list":
290            // list subscribers in a given list
291            if (!$param) {
292                log_print('Forgot to specify subscriber list');
293                break;
294            }
295            if (!in_array($param, list_valid_ids())) {
296                log_print("No such list!");
297            }
298            $list = list_get($param);
299            $i = 0;
300            foreach ($list as $recipient) {
301                if (!is_valid_email($recipient['email'])) {
302                    $status = 'INVALID';
303                }
304                else {
305                    $status = 'ok     ';
306                }
307                log_print($i."\t".$status."\t".recipient_str($recipient));
308                $i++;
309            }
310            log_print("\n".count($list)." subscribers in this list\n");
311            break;
312
313        case "log":
314            // view newsletter log
315            if (nlog_exists($newsletter_id)) {
316                nlog_view($newsletter_id);
317            }
318            else {
319                log_print("Newsletter has no log.");
320            }
321            break;
322
323        case "send":
324            // read list
325            $list_id = read_line('Subscriber list?');
326            if (!in_array($list_id, list_valid_ids())) {
327                log_print("No such list!");
328                break;
329            }
330            $list = list_get($list_id);
331            log_print("\n".count($list)." subscribers in this list\n");
332
333            // resume sending emails?
334            $skip = read_line("Enter next recipient-index to send email to "
335                              ."(0 means from the beginning):", 0);
336            log_assert(is_numeric($skip) && (0 <= $skip)
337                       && ($skip <= count($list)), "Invalid recipient index");
338            $skip = (int)$skip;
339            if (0 < $skip) {
340                $left = count($list) - $skip;
341                log_print("Skipping {$skip} recipients. There are {$left} "
342                          ."left.");
343                log_print("Last skipped recipient: "
344                          .recipient_str($list[$skip - 1]));
345                log_print("Next recipient is: ".recipient_str($list[$skip]));
346            }
347
348            echo "\n\n";
349
350            // confirm
351            if (!read_bool("This is the final warning! "
352                           ."Should I start sending emails?", false)) {
353                log_print("Aborted by user");
354                break;
355            }
356
357            // start sending letters
358            $i = $skip - 1;
359            $count_ok = 0;
360            $count_error = 0;
361            $used_destinations = array();
362            foreach ($list as $recipient) {
363                // skip some recipients
364                if (0 < $skip) {
365                    $skip--;
366                    continue;
367                }
368
369                $i++;
370                log_assert_valid(user_validate($recipient));
371
372                // log invalid email addresses
373                if (!is_valid_email($recipient['email'])) {
374                    nlog($newsletter_id,
375                         $i."\tINVALID\t".recipient_str($recipient));
376                    log_putch('i');
377                    $count_error++;
378                    continue;
379                }
380
381                // avoid emailing the same destination, even for
382                // different accounts
383                if (isset($used_destinations[$recipient['email']])) {
384                    nlog($newsletter_id,
385                         $i."\tDUPLICATE\t".recipient_str($recipient));
386                    log_putch('D');
387                    $count_error++;
388                    continue;
389                } else {
390                    $used_destinations[$recipient['email']] = true;
391                }
392
393                // send email
394                $phpmail = phpmail_create($textblock, $recipient);
395                $success = $phpmail->Send();
396
397                if ($success) {
398                    log_putch('.');
399                    nlog($newsletter_id, $i."\tok\t".recipient_str($recipient));
400                    $count_ok++;
401                }
402                else {
403                    log_putch('e');
404                    nlog($newsletter_id, $i."\tERROR\t".
405                            recipient_str($recipient)."\t".
406                            $phpmail->ErrorInfo);
407                    $count_error++;
408                }
409
410                // take a break from time to time
411                if (0 == ($i + 1) % IA_BURST_LENGTH) {
412                    usleep(IA_BURST_DELAY * 1000);
413                }
414            }
415
416            log_putch("\n");
417            log_print("\nTask completed!");
418            log_print("{$count_ok} ok; {$count_error} errors; ".count($list)
419                      ." total (total includes skipped)");
420            break;
421
422        default:
423            log_print("Invalid command");
424            echo <<<EOS
425
426Valid commands:
427quit
428preview [<username>]
429list <list-id>
430count <list-id>
431send
432log
433EOS;
434            // valid subscriber lists
435            echo "\nValid subscriber lists: ".join(', ', list_valid_ids())
436                 ."\n\n\n";
437
438    }
439}
440
441?>
Note: See TracBrowser for help on using the repository browser.