Outputs iso-8859-1 in addresses list as excel cannot open utf-8 csv properly.
[platal.git] / include / newsletter.inc.php
index 2faf993..7f1975c 100644 (file)
@@ -105,15 +105,13 @@ class NewsLetter
     /** Retrieve all newsletters
      * @return An array of $id => NewsLetter objects
      */
-    public static function getAll()
+    public static function getAll($sort = 'id', $order = 'ASC')
     {
-        $res = XDB::query('SELECT  id
-                             FROM  newsletters');
-        $nls = array();
-        foreach ($res->fetchColumn() as $id) {
-            $nls[$id] = new NewsLetter($id);
-        }
-        return $nls;
+        $res = XDB::fetchAllAssoc('SELECT  n.id, g.nom AS group_name, n.name, n.custom_css, 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);
+        return $res;
     }
 
     // }}}
@@ -366,6 +364,21 @@ class NewsLetter
         }
     }
 
+    /** Subscribe a batch of users to a newsletter.
+     * This skips 'maySubscribe' test.
+     *
+     * @p $user_ids Array of user IDs to subscribe to the newsletter.
+     */
+    public function bulkSubscribe($user_ids)
+    {
+        // TODO: use a 'bulkMaySubscribe'.
+        XDB::execute('INSERT IGNORE INTO  newsletter_ins (nlid, uid, last, hash)
+                                  SELECT  {?}, a.uid, NULL, NULL
+                                    FROM  accounts AS a
+                                   WHERE  a.uid IN {?}',
+                     $this->id, $user_ids);
+    }
+
     /** Retrieve subscription state of a user
      * @p $user Target user; if null, use current user.
      * @return Boolean: true if the user has subscribed to the NL.
@@ -392,6 +405,20 @@ class NewsLetter
                                    WHERE  nlid = {?}', $this->id);
     }
 
+    /** Get the count of subscribers with non valid redirection.
+     */
+    public function lostSubscriberCount()
+    {
+        return XDB::fetchOneCell('SELECT  COUNT(DISTINCT(n.uid))
+                                    FROM  newsletter_ins         AS n
+                              INNER JOIN  accounts               AS a ON (n.uid = a.uid)
+                              INNER JOIN  account_types          AS t ON (t.type = a.type)
+                               LEFT JOIN  email_redirect_account AS r ON (r.uid = a.uid AND r.flags = \'active\' AND r.broken_level < 3
+                                                                          AND r.type != \'imap\' AND r.type != \'homonym\')
+                                   WHERE  n.nlid = {?} AND r.redirect IS NULL AND a.state = \'active\' AND FIND_IN_SET(\'mail\', t.perms)',
+                                 $this->id);
+    }
+
     /** Get the number of subscribers to the NL whose last received mailing was $last.
      * @p $last ID of the issue for which subscribers should be counted.
      * @return Number of subscribers
@@ -501,10 +528,14 @@ class NewsLetter
     /** Get the prefix leading to the page for this NL
      * Only X.org / AX / X groups may be seen on X.org.
      */
-    public function prefix($enforce_xnet=true)
+    public function prefix($enforce_xnet=true, $with_group=true)
     {
         if (!empty($GLOBALS['IS_XNET_SITE'])) {
-            return $this->group . '/nl';
+            if ($with_group) {
+                return $this->group . '/nl';
+            } else {
+                return 'nl';
+            }
         }
         switch ($this->group) {
         case self::GROUP_XORG:
@@ -521,10 +552,14 @@ class NewsLetter
 
     /** Get the prefix to use for all 'admin' pages of this NL.
      */
-    public function adminPrefix($enforce_xnet=true)
+    public function adminPrefix($enforce_xnet=true, $with_group=true)
     {
         if (!empty($GLOBALS['IS_XNET_SITE'])) {
-            return $this->group . '/admin/nl';
+            if ($with_group) {
+                return $this->group . '/admin/nl';
+            } else {
+                return 'admin/nl';
+            }
         }
         switch ($this->group) {
         case self::GROUP_XORG:
@@ -559,6 +594,18 @@ class NewsLetter
         return $this->custom_css;
     }
 
+    public function canSyncWithGroup()
+    {
+        switch ($this->group) {
+          case self::GROUP_XORG:
+          case self::GROUP_AX:
+          case self::GROUP_EP:
+            return false;
+          default:
+            return true;
+        }
+    }
+
     // }}}
 }
 
@@ -588,6 +635,7 @@ class NLIssue
     public $send_before;  // Date at which issue should be sent
     public $head;  // Foreword of the issue (or body for letters with no articles)
     public $signature;  // Signature of the letter
+    public $reply_to;  // Adress to reply to the message (can be empty)
     public $arts = array();  // Articles of the issue
 
     const BATCH_SIZE = 60;  // Number of emails to send every minute.
@@ -612,7 +660,7 @@ class NLIssue
     {
         // Load this issue
         $res = XDB::query('SELECT  nlid, short_name, date, send_before, state, sufb_json,
-                                   title, mail_title, head, signature
+                                   title, mail_title, head, signature, reply_to
                              FROM  newsletter_issues
                             WHERE  id = {?}',
                           $id);
@@ -635,6 +683,7 @@ class NLIssue
         $this->title_mail  = $issue['mail_title'];
         $this->head        = $issue['head'];
         $this->signature   = $issue['signature'];
+        $this->reply_to    = $issue['reply_to'];
         $this->sufb        = $this->importJSonStoredUFB($issue['sufb_json']);
 
         if ($fetch_articles) {
@@ -825,7 +874,11 @@ class NLIssue
     public function last()
     {
         if (is_null($this->id_last)) {
-            $this->id_last = $this->nl->getIssue('last')->id;
+            try {
+                $this->id_last = $this->nl->getIssue('last')->id;
+            } catch (MailNotFound $e) {
+                $this->id_last = null;
+            }
         }
         return $this->id_last;
     }
@@ -833,6 +886,7 @@ class NLIssue
     // }}}
     // {{{ Edition, articles
 
+    const ERROR_INVALID_REPLY_TO = 'invalid_reply_to';
     const ERROR_INVALID_SHORTNAME = 'invalid_shortname';
     const ERROR_INVALID_UFC = 'invalid_ufc';
     const ERROR_TOO_LONG_UFC = 'too_long_ufc';
@@ -852,6 +906,12 @@ class NLIssue
             'signature' => $this->signature,
         );
 
+        if (!empty($this->reply_to) && !isvalid_email($this->reply_to)) {
+            $errors[] = self::ERROR_INVALID_REPLY_TO ;
+        } else {
+            $fields['reply_to'] = $this->reply_to;
+        }
+
         if ($this->isEditable()) {
             $fields['date'] = $this->date;
             if (!preg_match('/^[-a-z0-9]+$/i', $this->shortname) || is_numeric($this->shortname)) {
@@ -1128,6 +1188,9 @@ class NLIssue
         $mailer->assign('user', $user);
         $mailer->assign('prefix',  null);
         $mailer->assign('hash',    $hash);
+        if (!empty($this->reply_to)) {
+            $mailer->addHeader('Reply-To', $this->reply_to);
+        }
         $mailer->sendTo($user);
     }
 
@@ -1166,20 +1229,23 @@ class NLIssue
                        $this->id);
 
         $ufc = new PFC_And($this->getRecipientsUFC(), new UFC_NLSubscribed($this->nl->id, $this->id), new UFC_HasValidEmail());
-        $emailsCount = 0;
-        $uf = new UserFilter($ufc, array(new UFO_IsAdmin(), new UFO_Uid()));
+        $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);
             if (count($users) == 0) {
-                return $emailsCount;
+                break;
             }
             foreach ($users as $user) {
+                if (array_key_exists($user->id(), $global_sent)) {
+                    Platal::page()->kill('Sending the same newsletter issue ' . $this->id . ' to user ' . $user->id() . ' twice, something must be wrong.');
+                }
                 $sent[] = $user->id();
+                $global_sent[$user->id()] = true;
                 $this->sendTo($user, $hash);
-                ++$emailsCount;
             }
             XDB::execute("UPDATE  newsletter_ins
                              SET  last = {?}
@@ -1187,7 +1253,7 @@ class NLIssue
 
             sleep(60);
         }
-        return $emailsCount;
+        return count($global_sent);
     }
 
     // }}}