X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=include%2Fnewsletter.inc.php;h=fa368f9f56853388e9e31d615822909b87928ae9;hb=3da338db27ff7e09b46cad976342aeb133a60aad;hp=7944c7d09a7b0bec76ea0f86e0899eaf48bf7a6a;hpb=673e56aa757fce4ea57c27ab2c0d5ce87d713e51;p=platal.git diff --git a/include/newsletter.inc.php b/include/newsletter.inc.php index 7944c7d..fa368f9 100644 --- a/include/newsletter.inc.php +++ b/include/newsletter.inc.php @@ -1,6 +1,6 @@ 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 @@ -105,15 +104,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.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; } // }}} @@ -400,25 +397,32 @@ class NewsLetter /** Get the count of subscribers to the NL. * @return Number of subscribers. */ - public function subscriberCount() + public function subscriberCount($lost = null, $sex = null, $grade = null, $first_promo = null, $last_promo = null) { - return XDB::fetchOneCell('SELECT COUNT(uid) - FROM newsletter_ins - WHERE nlid = {?}', $this->id); + $cond = new PFC_And(new UFC_NLSubscribed($this->id)); + if (!is_null($sex)) { + $cond->addChild(new UFC_Sex($sex)); + } + if (!is_null($grade)) { + $cond->addChild(new UFC_Promo('>=', $grade, $first_promo)); + $cond->addChild(new UFC_Promo('<=', $grade, $last_promo)); + } + if (!($lost === null)) { + if ($lost === true) { + $cond->addChild(new PFC_Not(new UFC_HasEmailRedirect())); + } else { + $cond->addChild(new UFC_HasEmailRedirect()); + } + } + $uf = new UserFilter($cond); + return $uf->getTotalCount(); } /** 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); + public function lostSubscriberCount($sex = null) + { + return $this->subscriberCount(true, $sex); } /** Get the number of subscribers to the NL whose last received mailing was $last. @@ -496,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'])); } // }}} @@ -507,7 +514,7 @@ class NewsLetter */ public function cssFile() { - if ($this->custom_css) { + if ($this->hasCustomCss()) { $base = $this->group; } else { $base = self::FORMAT_DEFAULT_GROUP; @@ -519,7 +526,7 @@ class NewsLetter */ public function tplFile() { - if ($this->custom_css) { + if ($this->hasCustomCss()) { $base = $this->group; } else { $base = self::FORMAT_DEFAULT_GROUP; @@ -542,10 +549,14 @@ 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: return 'epletter'; + case self::GROUP_FX: + return 'fxletter'; default: // Don't display groups NLs on X.org assert(!$enforce_xnet); @@ -566,16 +577,79 @@ 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: return 'epletter/admin'; + case self::GROUP_FX: + return 'fxletter/admin'; default: // Don't display groups NLs on X.org assert(!$enforce_xnet); } } + /** Get the prefix to use for all 'stat' pages of this NL. + */ + public function statPrefix($enforce_xnet = true, $with_group = true) + { + if (!empty($GLOBALS['IS_XNET_SITE'])) { + if ($with_group) { + return $this->group . '/stat/nl'; + } else { + return 'stat/nl'; + } + } + 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: + return 'epletter/stat'; + case self::GROUP_FX: + return 'fxletter/stat'; + default: + // Don't display groups NLs on X.org + assert(!$enforce_xnet); + } + } + + /** 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() + { + return array( + 'index' => array('link' => $this->prefix(), 'title' => 'Archives'), + 'admin' => array('link' => $this->adminPrefix(), 'title' => 'Administrer'), + 'stats' => array('link' => $this->statPrefix(), 'title' => 'Statistiques') + ); + } + /** Hack used to remove "admin" links on X.org page on X.net * The 'admin' links are enabled for all pages, except for X.org when accessing NL through X.net */ @@ -593,15 +667,26 @@ 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: return false; default: return true; @@ -1166,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)) { @@ -1193,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', 'mail->domain . '>'); + $mailer->sendTo($user); } @@ -1230,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(), 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); @@ -1243,7 +1356,15 @@ class NLIssue } foreach ($users as $user) { if (array_key_exists($user->id(), $global_sent)) { - Platal::kill('Sending the same newsletter issue ' . $this->id . ' to user ' . $user->id() . ' twice, something must be wrong.'); + // 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(); $global_sent[$user->id()] = true; @@ -1453,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: ?>