/** 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;
}
// }}}
}
}
+ /** 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.
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
/** 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:
/** 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:
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;
+ }
+ }
+
// }}}
}
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.
{
// 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);
$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) {
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;
}
// }}}
// {{{ 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';
'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)) {
$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);
}
$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 = {?}
sleep(60);
}
- return $emailsCount;
+ return count($global_sent);
}
// }}}