Commit 2629a7cc authored by Patrick Barroca's avatar Patrick Barroca 🐧

hotline #47546 : better handling of newsletter send errors

parent d86dd0b4
......@@ -112,8 +112,13 @@ function sendNewsletterClick(event) {
public function sendAction() {
if ($newsletter = Class_Newsletter::find((int)$this->_getParam('id')))
$newsletter->send();
if (!$newsletter = Class_Newsletter::find((int)$this->_getParam('id'))) {
$this->_helper->notify($this->_('Envoi impossible : Newsletter inconnue'));
return $this->_redirectToIndex();
}
if (!$newsletter->send())
$this->_helper->notify($this->_('Envoi impossible : erreur à la création de la commande d\'envoi'));
$this->_redirectToIndex();
}
......@@ -272,4 +277,12 @@ function sendNewsletterClick(event) {
'id' => $model->getId()], null, true),
['prependBase' => false]);
}
public function showStatusAction() {
$this->view->titre = $this->_('Détails de l\'erreur');
$this->view->model = Class_Newsletter_Dispatch::find((int)$this->_getParam('id'));
if (!$this->view->model)
throw new Zend_Controller_Action_Exception($this->_('Désolé, cette page n\'existe pas'), 404);
}
}
\ No newline at end of file
<?php
echo $this->tag('p', nl2br($this->model->getErrorMessage()));
<?php
error_reporting(E_ERROR | E_PARSE);
define("PATCH_LEVEL","312");
define("PATCH_LEVEL","313");
define("APPLI","cosmogramme");
define("COSMOPATH", "/var/www/html/vhosts/opac2/www/htdocs");
......
<?php
try {
Zend_Db_Table_Abstract::getDefaultAdapter()
->query('alter table newsletter_dispatch add error longtext');
} catch(Exception $e) {}
\ No newline at end of file
......@@ -55,16 +55,36 @@ class Class_Batch_SendNewsletters extends Class_Batch_Abstract {
public function run() {
$this->getCommand()
->exec('/usr/bin/php -f '
. realpath(dirname(__FILE__)) . '/../../../scripts/sendNewsletter.php '
. $this->getExecParams()
. ' > /dev/null &');
$shell = '/usr/bin/php -f '
. realpath(dirname(__FILE__)) . '/../../../scripts/sendNewsletter.php '
. $this->getExecParams()
. ' > /dev/null & echo $!';
$command = $this->getCommand();
$command->exec($shell);
if ((!$output = $command->getOutput())
|| !is_array($output)
|| !$output[0]) {
$this->_endWithError('Unable to run ' . $shell);
return false;
}
return $this;
$command->exec('ps '. $output[0]);
if (!$success = 0 === $command->getReturnVar())
$this->_endWithError('Unable to run ' . $shell);
return $success;
}
protected function _endWithError($message) {
$this->_dispatch
->setError(json_encode(['message' => $message]))
->beEnded()
;
}
public function sendOne($email) {
if (!$this->_dispatch)
......@@ -136,6 +156,11 @@ class Class_Batch_SendNewsletters extends Class_Batch_Abstract {
}
protected function _checksum($params) {
return sha1(serialize($params));
}
public function getTimeLimit() {
if (!$this->_time_limit)
$this->_time_limit = Class_Systeme_TimeLimit::getInstance();
......
......@@ -99,10 +99,15 @@ class Class_Newsletter extends Storm_Model_Abstract {
public function send() {
if (!$dispatch = $this->getLastDispatchInProgress())
$dispatch = Class_Newsletter_Dispatch::newFrom($this);
return (new Class_Batch_SendNewsletters($this->_getOrCreateDispatchToRun()))
->run();
}
return (new Class_Batch_SendNewsletters($dispatch))->run();
protected function _getOrCreateDispatchToRun() {
return ($dispatch = $this->getLastDispatch()) && $dispatch->isRunnable()
? $dispatch->resetError()
: Class_Newsletter_Dispatch::newFrom($this);
}
......@@ -404,11 +409,17 @@ class Class_Newsletter extends Storm_Model_Abstract {
return $this->getTitre();
}
public function getLastDispatchInProgress() {
return Class_Newsletter_Dispatch::lastDispatchInProgressFor($this);
}
public function getLastDispatch() {
return Class_Newsletter_Dispatch::lastDispatchFor($this);
}
public function getSortedRecipientsByDedicatedAndLabel() {
$groups = $this->getRecipientsGroups();
usort(
......
......@@ -46,6 +46,13 @@ class Newsletter_DispatchLoader extends Storm_Model_Loader {
'ended_on' => null]);
}
public function lastDispatchFor($newsletter) {
return Class_Newsletter_Dispatch::findFirstBy(['newsletter_id' => $newsletter->getId(),
'order' => 'created_on desc']);
}
}
......@@ -69,7 +76,8 @@ class Class_Newsletter_Dispatch extends Storm_Model_Abstract {
'users' => ['through' => 'dispatch_users']];
protected $_default_attribute_values = ['collected' => 0,
'ended_on' => null];
'ended_on' => null,
'error' => null];
public function beforeSave() {
......@@ -114,10 +122,10 @@ class Class_Newsletter_Dispatch extends Storm_Model_Abstract {
$params = ['dispatch_id' => $this->getId(),
'user_id' => $model->getId()];
if (Class_Newsletter_DispatchUser::findFirstBy($params))
return;
if (in_array( $model->getMail(),$blacklist_mails))
if (Class_Newsletter_DispatchUser::findFirstBy($params)
|| in_array($model->getMail(), $blacklist_mails))
return;
Class_Newsletter_DispatchUser::newInstance(['dispatch_id' => $this->getId(),
'user_id' => $model->getId(),
'mail' => $model->getMail()])
......@@ -146,4 +154,44 @@ class Class_Newsletter_Dispatch extends Storm_Model_Abstract {
$this->getNewsletter()->setLastDistributionDateWithFormat();
return $this;
}
public function getErrorMessage() {
if (!$this->hasError()
|| (!$json = json_decode($this->getError(), true))
|| !array_key_exists('message', $json))
return '';
return $json['message'];
}
public function isRunnable() {
return $this->isInProgress() || $this->canRetry();
}
public function canRetry() {
return $this->hasError()
&& $this->getCollected()
&& $this->numberOfDoneUsers() < $this->numberOfDispatchUsers();
}
public function isInProgress() {
return !$this->hasEndedOn();
}
public function resetError() {
if (!$this->canRetry())
return $this;
$this
->setError(null)
->setEndedOn(null)
->save();
return $this;
}
}
......@@ -21,27 +21,39 @@
class ZendAfi_View_Helper_getSendProgressJsonFor extends ZendAfi_View_Helper_BaseHelper {
public function getSendProgressJsonFor($newsletter) {
if(!$newsletter)
if (!$newsletter)
return $this->_toStatus($this->_('Newsletter inconnue'));
($last_date = DateTime::createFromFormat("Y-m-d H:i:s", $newsletter->getLastDistributionDate()))
? $last_date_text = $this->_toStatus($last_date->format("d/m/Y H:i"))
($last_date = DateTime::createFromFormat('Y-m-d H:i:s', $newsletter->getLastDistributionDate()))
? $last_date_text = $this->_toStatus($last_date->format('d/m/Y H:i'))
: $last_date_text = $this->_toStatus($this->_('Aucune'));
if(!$dispatch = $newsletter->getLastDispatchInProgress())
if (!$dispatch = $newsletter->getLastDispatch())
return $last_date_text;
if($dispatch->getCollected() && (!$dispatch->hasEndedOn()))
return array_merge($this->_toStatus($this->_('en cours')),
if (!$dispatch->hasEndedOn())
return array_merge($this->_toStatus(!$dispatch->getCollected()
? $this->_('collecte des destinataires')
: $this->_('envoi en cours')),
['done' => $dispatch->numberOfDoneUsers(),
'total' => $dispatch->numberOfDispatchUsers()]);
if(!$last_date) {
$dispatch->delete();
return $last_date_text;
}
return $dispatch->hasError()
? $this->_toStatus($this->_tag('a', $this->_('Erreur lors de l\'envoi'),
['data-popup' => 'true',
'href' => $this->view->url(['module' => 'admin',
'controller' => 'newsletter',
'action' => 'show-status',
'id' => $dispatch->getId()],
null, true)])
. ($dispatch->getCollected()
? $this->_(', envoyé à %s sur %s',
$dispatch->numberOfDoneUsers(),
$dispatch->numberOfDispatchUsers())
: $this->_(', aucun mail envoyé')))
return $this->_toStatus($this->_('échec de l\'envoi'));
: $this->_toStatus($last_date->format('d/m/Y H:i')
. $this->_(', %s mails envoyés', $dispatch->numberOfDoneUsers()));
}
......
......@@ -25,20 +25,25 @@ class ZendAfi_View_Helper_TagProgressBarForNewsletter extends ZendAfi_View_Helpe
$data_url = $this->view->url(['module'=>'admin',
'controller' => 'newsletter',
'action' => 'send-progress',
'id' => $newsletter->getId()],null,true);
'id' => $newsletter->getId()],
null, true);
$progress_data = 'function getNewsletterProgress'.$newsletter->getId().'(){$.getJSON("'.$data_url.'", function (data) {'.
'if(!data.done || !data.total) {'.
'$("#progress_bar_newsletter_'.$newsletter->getId().'").text(data.status); return true;'.
'$("#progress_bar_newsletter_'.$newsletter->getId().' span").html(data.status);'.
'if(!data.total) {'.
'$("#progress_bar_newsletter_'.$newsletter->getId().'").html(data.status);'.
'initializePopups();'.
'return true;'.
'}'.
'$("#progress_bar_newsletter_'.$newsletter->getId().'").progressbar({value: data.done, max: data.total});'.
'setTimeout(function() {getNewsletterProgress'.$newsletter->getId().'()}, 100);'.
'setTimeout(function() {getNewsletterProgress'.$newsletter->getId().'();}, 100);'.
'})};';
Class_ScriptLoader::getInstance()
->addInlineScript($progress_data)
->addJQueryReady('getNewsletterProgress'.$newsletter->getId().'()');
return $this->_tag('div', '',
return $this->_tag('div', $this->_tag('span', ''),
['id' => 'progress_bar_newsletter_' . $newsletter->getId()]);
}
}
\ No newline at end of file
......@@ -22,22 +22,29 @@
define('BASE_URL', $argv[3]);
$_SERVER['SERVER_NAME'] = $argv[2];
$_SERVER['HTTP_HOST'] = $argv[1];
$dispatch_id = $argv[4];
set_include_path('.'
. PATH_SEPARATOR .realpath(dirname(__FILE__)). '/../library'
. PATH_SEPARATOR .realpath(dirname(__FILE__)). '/../library/storm/zf/library'
. PATH_SEPARATOR .realpath(dirname(__FILE__)). '/../library/storm/src'
. PATH_SEPARATOR .realpath(dirname(__FILE__)).'/../library/Class'
. PATH_SEPARATOR .realpath(dirname(__FILE__)).'/../library/ZendAfi'
. PATH_SEPARATOR . realpath(dirname(__FILE__)).'/../application/modules'
. PATH_SEPARATOR . realpath(dirname(__FILE__)).'/application/modules'
);
require_once 'includes.php';
include_once "local.php";
include_once "fonctions/fonctions.php";
require_once "Zend/Loader.php";
require_once "startup.php";
$bokeh = (new Bokeh_Engine())->warmUp();
if ('test' != $dispatch_id
&& (!$dispatch = Class_Newsletter_Dispatch::find($dispatch_id))) {
printf("Unkown dispatch %s\n", $dispatch_id);
exit(1);
}
setupOpac();
$my_shutdown = function() use ($dispatch) {
if (!$error = error_get_last())
return;
Class_Newsletter_Dispatch::find($argv[4])->sendBy(20);
\ No newline at end of file
if (E_ERROR === $error['type'])
$dispatch->setError(json_encode($error))
->beEnded();
};
register_shutdown_function($my_shutdown);
$bokeh->powerOn();
if ($dispatch)
$dispatch->sendBy(20);
\ No newline at end of file
......@@ -1134,3 +1134,19 @@ class UpgradeDB_312_Test extends UpgradeDBTestCase {
$this->assertFieldType('ouvertures', 'multimedia', 'tinyint(1)');
}
}
class UpgradeDB_313_Test extends UpgradeDBTestCase {
public function prepare() {
try {
$this->query('alter table newsletter_dispatch drop column error');
} catch(Exception $e) {}
}
/** @test */
public function dispatchShouldHaveErrorColumn() {
$this->assertFieldType('newsletter_dispatch', 'error', 'longtext');
}
}
<?php
/**
* Copyright (c) 2012-2014, Agence Française Informatique (AFI). All rights reserved.
*
* BOKEH is free software; you can redistribute it and/or modify
* it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
* the Free Software Foundation.
*
* There are special exceptions to the terms and conditions of the AGPL as it
* is applied to this software (see README file).
*
* BOKEH is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU AFFERO GENERAL PUBLIC LICENSE for more details.
*
* You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
* along with BOKEH; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
class Scripts_SendNewsletterTest extends PHPUnit_Framework_TestCase {
/** @test */
public function shouldSucceed() {
exec('cd ' . __DIR__ . '/../.. && php -f scripts/sendNewsletter.php "localhost" "localhost" "" "test"',
$output, $result);
$this->assertEquals(0, $result, implode("\n", $output));
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment