Describe in a long comment what does Newsletter::sendToAll()
[platal.git] / include / newsletter.inc.php
index a51aeae..fa368f9 100644 (file)
@@ -1,6 +1,6 @@
 <?php
 /***************************************************************************
- *  Copyright (C) 2003-2011 Polytechnique.org                              *
+ *  Copyright (C) 2003-2014 Polytechnique.org                              *
  *  http://opensource.polytechnique.org/                                   *
  *                                                                         *
  *  This program is free software; you can redistribute it and/or modify   *
@@ -37,13 +37,12 @@ class NewsLetter
     public $cats;  // List of all categories for this NL
     public $criteria;  // PlFlagSet of allowed filters for recipient selection
 
-    protected $custom_css = false;
-
     // Base name to use instead of the group short name for NLs without a custom CSS
     const FORMAT_DEFAULT_GROUP = 'default';
 
     // Diminutif of X.net groups with a specific NL view
     const GROUP_XORG = 'Polytechnique.org';
+    const GROUP_COMMUNITY = 'Annonces';
     const GROUP_AX = 'AX';
     const GROUP_EP = 'Ecole';
     const GROUP_FX = 'FX';
@@ -59,7 +58,7 @@ class NewsLetter
     {
         // Load NL data
         $res = XDB::query('SELECT  nls.group_id, g.diminutif AS group_name,
-                                   nls.name AS nl_name, nls.custom_css, nls.criteria
+                                   nls.name AS nl_name, nls.criteria
                              FROM  newsletters AS nls
                         LEFT JOIN  groups AS g ON (nls.group_id = g.id)
                             WHERE  nls.id = {?}',
@@ -73,7 +72,6 @@ class NewsLetter
         $this->group_id = $data['group_id'];
         $this->group = $data['group_name'];
         $this->name = $data['nl_name'];
-        $this->custom_css = $data['custom_css'];
         $this->criteria = new PlFlagSet($data['criteria']);
 
         // Load the categories
@@ -108,7 +106,7 @@ class NewsLetter
      */
     public static function getAll($sort = 'id', $order = 'ASC')
     {
-        $res = XDB::fetchAllAssoc('SELECT  n.id, g.nom AS group_name, n.name, n.custom_css, n.criteria, g.diminutif AS group_link
+        $res = XDB::fetchAllAssoc('SELECT  n.id, g.nom AS group_name, n.name, n.criteria, g.diminutif AS group_link
                                      FROM  newsletters AS n
                                INNER JOIN  groups      AS g ON (n.group_id = g.id)
                                  ORDER BY  ' . $sort . ' ' . $order);
@@ -502,8 +500,11 @@ class NewsLetter
      */
     public function maySubmit($user = null)
     {
-        // Submission of new articles is only enabled for the X.org NL (and forbidden when viewing issues on X.net)
-        return ($this->group == self::GROUP_XORG && !isset($GLOBALS['IS_XNET_SITE']));
+        // Submission of new articles is only enabled for the X.org NL and the
+        // community letter (and forbidden when viewing issues on X.net)
+        return (
+            ($this->group == self::GROUP_XORG || $this->group == self::GROUP_COMMUNITY)
+            && !isset($GLOBALS['IS_XNET_SITE']));
     }
 
     // }}}
@@ -513,7 +514,7 @@ class NewsLetter
      */
     public function cssFile()
     {
-        if ($this->custom_css) {
+        if ($this->hasCustomCss()) {
             $base = $this->group;
         } else {
             $base = self::FORMAT_DEFAULT_GROUP;
@@ -525,7 +526,7 @@ class NewsLetter
      */
     public function tplFile()
     {
-        if ($this->custom_css) {
+        if ($this->hasCustomCss()) {
             $base = $this->group;
         } else {
             $base = self::FORMAT_DEFAULT_GROUP;
@@ -548,6 +549,8 @@ class NewsLetter
         switch ($this->group) {
         case self::GROUP_XORG:
             return 'nl';
+        case self::GROUP_COMMUNITY:
+            return 'comletter';
         case self::GROUP_AX:
             return 'ax';
         case self::GROUP_EP:
@@ -574,6 +577,8 @@ class NewsLetter
         switch ($this->group) {
         case self::GROUP_XORG:
             return 'admin/newsletter';
+        case self::GROUP_COMMUNITY:
+            return 'comletter/admin';
         case self::GROUP_AX:
             return 'ax/admin';
         case self::GROUP_EP:
@@ -600,6 +605,8 @@ class NewsLetter
         switch ($this->group) {
         case self::GROUP_XORG:
             return 'stat/newsletter';
+        case self::GROUP_COMMUNITY:
+            return 'comletter/stat';
         case self::GROUP_AX:
             return 'ax/stat';
         case self::GROUP_EP:
@@ -612,6 +619,26 @@ class NewsLetter
         }
     }
 
+    /** Get a full URL to a newsletter
+     */
+    public function fullUrl()
+    {
+        switch ($this->group) {
+        case self::GROUP_XORG:
+            return 'https://www.polytechnique.org/nl';
+        case self::GROUP_COMMUNITY:
+            return 'https://www.polytechnique.org/comletter';
+        case self::GROUP_AX:
+            return 'https://www.polytechnique.org/ax';
+        case self::GROUP_EP:
+            return 'https://www.polytechnique.org/epletter';
+        case self::GROUP_FX:
+            return 'https://www.polytechnique.org/fxletter';
+        default:
+            return 'http://www.polytechnique.net/' . $this->group . '/nl';
+        }
+    }
+
     /** Get links for nl pages.
      */
     public function adminLinks()
@@ -640,13 +667,23 @@ class NewsLetter
 
     public function hasCustomCss()
     {
-        return $this->custom_css;
+        switch ($this->group) {
+          case self::GROUP_XORG:
+          case self::GROUP_COMMUNITY:
+          case self::GROUP_AX:
+          case self::GROUP_EP:
+          case self::GROUP_FX:
+            return true;
+          default:
+            return false;
+        }
     }
 
     public function canSyncWithGroup()
     {
         switch ($this->group) {
           case self::GROUP_XORG:
+          case self::GROUP_COMMUNITY:
           case self::GROUP_AX:
           case self::GROUP_EP:
           case self::GROUP_FX:
@@ -1214,6 +1251,14 @@ class NLIssue
      */
     public function sendTo($user, $hash = null)
     {
+        global $globals;
+
+        // Don't send email to users without an address
+        // Note: this would never happen when using sendToAll
+        if (!$user->bestEmail()) {
+            return;
+        }
+
         $this->fetchArticles();
 
         if (is_null($hash)) {
@@ -1241,6 +1286,21 @@ class NLIssue
         if (!empty($this->reply_to)) {
             $mailer->addHeader('Reply-To', $this->reply_to);
         }
+
+        // Add mailing list headers
+        // Note: "Precedence: bulk" is known to cause issues on some clients
+        $mailer->addHeader('Precedence', 'list');
+        // RFC 2919 header
+        $mailer->addHeader('List-Id', $this->nl->group .
+            ' <' . $this->nl->group . '.newsletter.' . $globals->mail->domain . '>');
+        // RFC 2369 headers
+        $listurl = $this->nl->fullUrl();
+        $mailer->addHeader('List-Unsubscribe', '<' . $listurl . '/out/nohash/' . $this->id . '>');
+        $mailer->addHeader('List-Subscribe', '<' . $listurl. '/in/nohash/' . $this->id . '>');
+        $mailer->addHeader('List-Archive', '<' . $listurl . '>');
+        $mailer->addHeader('List-Help', '<' . $listurl . '>');
+        $mailer->addHeader('List-Owner', '<mailto:support@' . $globals->mail->domain . '>');
+
         $mailer->sendTo($user);
     }
 
@@ -1278,11 +1338,16 @@ class NLIssue
                        WHERE  id = {?}',
                        $this->id);
 
+        // Every minute, select BATCH_SIZE users who:
+        // * are subscribed to the newsletter
+        // * have not yet been mailed this issue of the newsletter
+        // * have a valid email address
+        // ... and send them the current issue.
+        // Once a mail is sent, newsletter_ins is updated to prevent selecting again the same user a minute later.
         $ufc = new PFC_And($this->getRecipientsUFC(), new UFC_NLSubscribed($this->nl->id, $this->id), new UFC_HasValidEmail());
         $uf = new UserFilter($ufc, array(new UFO_IsAdmin(true), new UFO_Uid()));
         $limit = new PlLimit(self::BATCH_SIZE);
         $global_sent = array();
-
         while (true) {
             $sent = array();
             $users = $uf->getUsers($limit);
@@ -1291,6 +1356,14 @@ class NLIssue
             }
             foreach ($users as $user) {
                 if (array_key_exists($user->id(), $global_sent)) {
+                    // Such a condition may happen if an user:
+                    // 1. was mailed the issue,
+                    // 2. unsubscribed the newsletter,
+                    // 3. subscribed again before the sending was done.
+                    // Such a case is reported by mail to people who monitor the website.
+                    // If you are reading this comment because of such a mail and the lines above explain what happened,
+                    // you only need to reset the state of the issue to "pending".
+                    // A cron script will then restart the mailing from where it stopped and only the problematic user will reveive the issue twice.
                     Platal::page()->kill('Sending the same newsletter issue ' . $this->id . ' to user ' . $user->id() . ' twice, something must be wrong.');
                 }
                 $sent[] = $user->id();
@@ -1501,5 +1574,5 @@ function format_text($input, $format, $indent = 0, $width = 68)
 
 // }}}
 
-// vim:set et sw=4 sts=4 sws=4 enc=utf-8:
+// vim:set et sw=4 sts=4 sws=4 fenc=utf-8:
 ?>