WYSIJA_help_queue

The MailPoet Newsletters WYSIJA help queue class.

Defined (1)

The class is defined in the following location(s).

/helpers/queue.php  
  1. class WYSIJA_help_queue extends WYSIJA_object{ 
  2. var $email_id = 0; 
  3. var $report = true; 
  4. var $send_limit = 0; 
  5. var $finish = false; 
  6. var $error = false; 
  7. var $nbprocess = 0; 
  8. var $start = 0; 
  9. var $stoptime = 0; 
  10. var $successSend =0; 
  11. var $errorSend=0; 
  12. var $consecutiveError=0; 
  13. var $messages = array(); 
  14. var $pause = 0; 
  15. var $config; 
  16. var $listsubClass; 
  17. var $subClass; 
  18.  
  19. /** 
  20. */ 
  21. function __construct() { 
  22. $this->config = WYSIJA::get('config', 'model'); 
  23. $this->subClass = WYSIJA::get('user', 'model');//acymailing_get('class.sub'); 
  24. $this->listsubClass = WYSIJA::get('user_list', 'model');//acymailing_get('class.listsub'); 
  25. $this->listsubClass->checkAccess = false; 
  26. $this->listsubClass->sendNotif = false; 
  27. $this->listsubClass->sendConf = false; 
  28.  
  29. $is_multisite=is_multisite(); 
  30.  
  31. //$is_multisite=true;//PROD comment that line 
  32.  
  33. // get the right sending limit based on the setup we are on,  
  34. // if we are on a multisite we need to see which sending method is used to determine that 
  35. if($is_multisite && $this->config->getValue('sending_method')=='network') { 
  36. $this->send_limit=(int)$this->config->getValue('ms_sending_emails_number'); 
  37. }else{ 
  38. $this->send_limit=(int)$this->config->getValue('sending_emails_number'); 
  39.  
  40. if(isset($_REQUEST['totalsend'])) { 
  41. $this->send_limit = (int) $_REQUEST['totalsend']-$_REQUEST['alreadysent']; 
  42.  
  43. @ini_set('max_execution_time', 0); 
  44. @ini_set('default_socket_timeout', 10); 
  45. @ignore_user_abort(true); 
  46.  
  47. //we set a stoppage time to avoid broken process 
  48. $time_limit = ini_get('max_execution_time'); 
  49. if(!empty($time_limit)) { 
  50. $this->stoptime = time()+$time_limit-4; 
  51.  
  52. /** 
  53. * @param int $email_id 
  54. * @param int $user_id we can specify that parameter if we want to process the emails of only one specific user 
  55. * @return boolean 
  56. */ 
  57. function process($email_id=false, $user_id=false) { 
  58. if($email_id) $this->email_id=(int)$email_id; 
  59. $model_queue = WYSIJA::get('queue', 'model'); 
  60. $queue_elements = $model_queue->getReady($this->send_limit, $this->email_id, $user_id); 
  61.  
  62. $this->total=count($queue_elements); 
  63. $this->start=0; 
  64.  
  65. // there is no elements queued 
  66. if(empty($queue_elements)) { 
  67. // there might be some delaied emails let's list those ones 
  68. $queue_elements_delayed = $model_queue->getDelayed($this->email_id); 
  69.  
  70. // let's see if there are any delayed emails so that we display something about it 
  71. if(empty($queue_elements_delayed)) { 
  72. $this->clear(); 
  73. }else{ 
  74. // report is on let's print the details about those delayed elements 
  75. if($this->report) { 
  76. $html_response = '<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />'; 
  77. $html_response .= '<style>body{font-size:12px;font-family: Arial, Helvetica, sans-serif;}</style></head><body>'; 
  78. $html_response.= '<div style="background-color : white;border : 1px solid grey; padding : 3px;font-size:14px">'; 
  79. $html_response.= '<span id="divpauseinfo" style="padding:10px;margin:5px;font-size:16px;font-weight:bold;display:none;background-color:black;color:white;"> </span>'; 
  80. $helper_toolbox=WYSIJA::get('toolbox', 'helper'); 
  81.  
  82. $html_response.= sprintf(__('There are %1$s pending email(s)', WYSIJA), count($queue_elements_delayed)); 
  83. $html_response.= ' - <small>'.date_i18n(get_option('date_format').' '.get_option('time_format'), $helper_toolbox->servertime_to_localtime(time())).'</small>'; 
  84. $html_response.= '</div>'; 
  85.  
  86. // for each elements let's calculate and print when the email is supposed to be sent 
  87. foreach($queue_elements_delayed as $element) { 
  88. $html_response.= '<div id="divinfo">'.sprintf(__('Email will be sent to %1$s at %2$s', WYSIJA), '<b>'.$element['email'].'</b>', '<em>'.date_i18n(get_option('date_format').' '.get_option('time_format'), $helper_toolbox->servertime_to_localtime($element['send_at'])).'</em>').' </div>'; 
  89.  
  90. echo $html_response; 
  91.  
  92. $this->finish = true; 
  93. return true; 
  94.  
  95. // report is on let's print the details about those delayed elements 
  96. if($this->report) { 
  97. if(!headers_sent() AND ob_get_level() > 0) { 
  98. ob_end_flush(); 
  99. $html_response = '<html><head><meta http-equiv="Content-Type" content="text/html;charset=utf-8" />'; 
  100. $html_response .= '<title>'.addslashes(__('Send Process', WYSIJA)).'</title>'; 
  101. $html_response .= '<style>body{font-size:12px;font-family: Arial, Helvetica, sans-serif;}</style></head><body>'; 
  102. $html_response.= "<div style='padding: 3px;'>"; 
  103. $html_response.= "<span id='divpauseinfo' style='padding:10px;margin:5px;font-size:16px;font-weight:bold;display:none;background-color:black;color:white;'> </span>"; 
  104. $html_response.= __('Total of batch', WYSIJA).': <span id="counter"/>'.$this->start.'</span> / '. $this->total; 
  105. $html_response.= '</div>'; 
  106. $html_response.= "<div id='divinfo' style='display:none; position:fixed; bottom:3px;left:3px;background-color : white; border : 1px solid grey; padding : 3px;'> </div>"; 
  107.  
  108. $url = 'admin.php?page=wysija_campaigns&action=manual_send&emailid='.$this->email_id.'&_wpnonce='.WYSIJA_view::secure(array('action' => 'manual_send'), true).'&totalsend='.$this->total.'&alreadysent='; 
  109.  
  110. $html_response.= '<script type="text/javascript" language="javascript">'; 
  111. $html_response.= 'var mycounter = document.getElementById("counter");'; 
  112. $html_response.= 'var divinfo = document.getElementById("divinfo"); 
  113. var divpauseinfo = document.getElementById("divpauseinfo"); 
  114. function setInfo(message) { divinfo.style.display = \'block\';divinfo.innerHTML=message; } 
  115. function setPauseInfo(nbpause) { divpauseinfo.style.display = \'\';divpauseinfo.innerHTML=nbpause;} 
  116. function setCounter(val) { mycounter.innerHTML=val;} 
  117. var scriptpause = '.intval($this->pause).'; 
  118. function handlePause() { 
  119. setPauseInfo(scriptpause); 
  120. if(scriptpause > 0) { 
  121. scriptpause = scriptpause - 1; 
  122. setTimeout(\'handlePause()\', 1000); 
  123. }else{ 
  124. document.location.href=\''.$url.'\'+mycounter.innerHTML; 
  125. </script>'; 
  126. echo $html_response; 
  127. if(function_exists('ob_flush')) @ob_flush(); 
  128. @flush(); 
  129. }//endifreport 
  130.  
  131. $helper_mailer=WYSIJA::get('mailer', 'helper'); 
  132. $helper_mailer->report = false; 
  133. if($this->config->getValue('smtp_keepalive', 1)) $helper_mailer->SMTPKeepAlive = true; 
  134. else $helper_mailer->SMTPKeepAlive = false; 
  135. $queue_delete = array(); 
  136. $queue_update = array(); 
  137. $stats_add = array(); 
  138. $action_subscriber = array(); 
  139. $max_try = (int) $this->config->getValue('queue_try', 3); 
  140. $current_mail = $this->start; 
  141. $this->nbprocess = 0; 
  142. if(count($queue_elements) < $this->send_limit) { 
  143. $this->finish = true; 
  144. WYSIJA::log('helpers -> Queue ->process', $queue_elements, 'queue_process'); 
  145.  
  146. // go through each queued element and process it 
  147. foreach($queue_elements as $queue_item) { 
  148. $current_mail++; $this->nbprocess++; 
  149. if($this->report) { 
  150. echo '<script type="text/javascript" language="javascript">setCounter('. $current_mail .')</script>'; 
  151. if(function_exists('ob_flush')) @ob_flush(); 
  152. @flush(); 
  153. WYSIJA::log('helpers -> Queue ->process ->sendOne', array('email_id'=>$queue_item->email_id, 'oneQueue'=>$queue_item), 'queue_process'); 
  154. $result = $helper_mailer->sendOne($queue_item->email_id, $queue_item); 
  155.  
  156. $queue_delete_ok = true; 
  157. $other_message = ''; 
  158.  
  159. // email has been sent ok let's make the process resulting after that 
  160. if($result) { 
  161. $this->successSend ++; 
  162. $this->consecutiveError = 0; 
  163. $queue_delete[$queue_item->email_id][] = $queue_item->user_id; 
  164. $stats_add[$queue_item->email_id][1][(int)$helper_mailer->sendHTML][] = $queue_item->user_id; 
  165. $queue_delete_ok = $this->_deleteQueue($queue_delete); 
  166. WYSIJA::log('helpers -> Queue ->process ->sendOne resultOK(queue delete)', $queue_delete, 'queue_process'); 
  167. $queue_delete = array(); 
  168.  
  169. // each 10 elements processed we record the statistics for that email and update the queue 
  170. if($this->nbprocess%10 == 0) { 
  171. $this->_statsAdd($stats_add); 
  172. $this->_queueUpdate($queue_update); 
  173. $stats_add = array(); 
  174. $queue_update = array(); 
  175.  
  176. }else{ 
  177. // email has not been sent and the mailer class returned an error 
  178. $this->errorSend ++; 
  179. $new_try = false; 
  180. if(in_array($helper_mailer->errorNumber, $helper_mailer->errorNewTry)) { 
  181. // we check if that's still possible to have a new try at sending the email 
  182. if(empty($max_try) OR $queue_item->number_try < $max_try-1) { 
  183. $new_try = true; 
  184. $other_message = sprintf(__('Next try in %s minutes.', WYSIJA), round($this->config->getValue('queue_delay')/60)); 
  185. if($helper_mailer->errorNumber == 1) $this->consecutiveError ++; 
  186. if($this->consecutiveError == 2) sleep(1); 
  187.  
  188. //if it's not possible to have a new try at sending the email then we delete the email from the queue and we add some stats 
  189. if(!$new_try) { 
  190. $queue_delete[$queue_item->email_id][] = $queue_item->user_id; 
  191. $stats_add[$queue_item->email_id][0][(int)@$helper_mailer->sendHTML][] = $queue_item->user_id; 
  192. if($helper_mailer->errorNumber == 1 AND $this->config->getValue('bounce_action_maxtry')) { 
  193. $queue_delete_ok = $this->_deleteQueue($queue_delete); 
  194. $queue_delete = array(); 
  195.  
  196. // ADDED to add the stats immediately instead of waiting 
  197. $this->_statsAdd($stats_add); 
  198. $stats_add = array(); 
  199.  
  200. // this is not used actually it would be used if we had a rule for the bounce max try case scenario 
  201. // this is used in acy not in wysija 
  202. //$other_message .= $this->_subscriberAction($queue_item->user_id); 
  203.  
  204. $other_message .=''; 
  205. }else{ 
  206. $queue_update[$queue_item->email_id][] = $queue_item->user_id; 
  207. WYSIJA::log('helpers -> Queue ->process ->sendOne resultFAILED(queue update)', $queue_update, 'queue_process'); 
  208. //$messageOnScreen = '['.$oneQueue->email_id.'] '.$mailHelper->reportMessage; 
  209. $message_on_screen = $helper_mailer->reportMessage; 
  210. if(!empty($other_message)) $message_on_screen .= ' => '.$other_message; 
  211. $this->_display($message_on_screen, $result, $current_mail); 
  212. if(!$queue_delete_ok) { 
  213. $this->finish = true; 
  214. break; 
  215.  
  216. // when we reach the execution time limit we just refresh the screen 
  217. if(!empty($this->stoptime) AND $this->stoptime < time()) { 
  218. $this->_display(__('Process refreshed to avoid a time limit.', WYSIJA)); 
  219. if($this->nbprocess < count($queue_elements)) $this->finish = false; 
  220. break; 
  221.  
  222. // too many consecutive errors compared to th enumber of success 
  223. if($this->consecutiveError > 2 AND $this->successSend>3) { 
  224. $this->_display(__('Process refreshed to avoid a possible loss of connection.', WYSIJA)); 
  225. break; 
  226.  
  227. // if there are too many consecutive errors or the connection has been aborted by the client we finish the request 
  228. if($this->consecutiveError > 5 OR connection_aborted()) { 
  229. $this->finish = true; 
  230. break; 
  231.  
  232. //delete the elements from the queue and add statistics and update the queue 
  233. $this->_deleteQueue($queue_delete); 
  234. $this->_statsAdd($stats_add); 
  235. $this->_queueUpdate($queue_update); 
  236. if($this->config->getValue('smtp_keepalive', 1)) $helper_mailer->SmtpClose(); 
  237.  
  238. if(!empty($this->total) AND $current_mail >= $this->total) { 
  239. $this->finish = true; 
  240. if($this->consecutiveError>5) { 
  241. $this->_handleError(); 
  242. return false; 
  243. if($this->report && !$this->finish) { 
  244. echo '<script type="text/javascript" language="javascript">handlePause();</script>'; 
  245.  
  246.  
  247. if($this->report) { 
  248. echo '</body></html>'; 
  249. exit; 
  250. return true; 
  251.  
  252. /** 
  253. * erase an array of queue elements from the database 
  254. * @param array $queue_delete 
  255. * @return boolean 
  256. */ 
  257. function _deleteQueue($queue_delete) { 
  258. if(empty($queue_delete)) return true; 
  259. $status = true; 
  260. $modelQ=WYSIJA::get('queue', 'model'); 
  261. foreach($queue_delete as $email_id => $subscribers) { 
  262. $nbsub = count($subscribers); 
  263. //$res=$modelQ->delete(array("email_id"=>intval($email_id), "user_id"=>$subscribers)); 
  264. //$realquery='DELETE a.* FROM `[wysija]queue` as a LEFT JOIN `[wysija]email` as b on a.email_id = b.email_id WHERE b.email_id IS NULL'; 
  265.  
  266. $real_query='DELETE FROM `[wysija]queue` WHERE email_id = '.intval($email_id).' AND user_id IN ('.implode(', ', $subscribers).') LIMIT '.$nbsub; 
  267. WYSIJA::log('helpers -> Queue ->process ->deleteQueue', $real_query, 'queue_process'); 
  268. $res=$modelQ->query($real_query); 
  269.  
  270. if(!$res) { 
  271. $status = false; 
  272. WYSIJA::log('helpers -> Queue ->process ->deleteQueue failed', true, 'queue_process'); 
  273. //$this->_display($this->db->getErrorNum.' : '.$this->db->getErrorMsg()); 
  274. }else{ 
  275. WYSIJA::log('deleting queue ok', array('email_id'=>$email_id, 'subscribers'=>$subscribers), 'queue_process'); 
  276. $nbdeleted = $modelQ->getAffectedRows(); 
  277. if($nbdeleted != $nbsub) { 
  278. $status = false; 
  279.  
  280. $this->_display(__('Newsletters are already being sent. Your latest newsletter will be sent afterwards.', WYSIJA)); 
  281.  
  282.  
  283.  
  284. return $status; 
  285.  
  286. /** 
  287. * insert statistics entries in the database 
  288. * @param array $stats_add 
  289. * @return boolean 
  290. */ 
  291. function _statsAdd($stats_add) { 
  292. $time = time(); 
  293. if(empty($stats_add)) return true; 
  294. $modelEUS=WYSIJA::get('email_user_stat', 'model'); 
  295.  
  296. foreach($stats_add as $email_id => $infos) { 
  297. $email_id = intval($email_id); 
  298.  
  299. //Email.sent_at should be the last sending time 
  300. $query = 'UPDATE `[wysija]email` SET sent_at = '.$time.' WHERE email_id = '.$email_id; 
  301. $modelEUS->query($query); 
  302.  
  303. //User stat 
  304. foreach($infos as $status => $infosSub) { 
  305. foreach($infosSub as $html => $subscribers) { 
  306.  
  307. // if not status that means it failed sending so we record it as -2 otherwise it sent properly we record as 0 and will increment step by step 
  308. if(!$status) $status=-2; 
  309. else $status=0; 
  310.  
  311. $query = 'INSERT IGNORE INTO `[wysija]email_user_stat` (email_id, user_id, status, sent_at) VALUES ('.$email_id.', '.implode(', '.$status.', '.$time.'), ('.$email_id.', ', $subscribers).', '.$status.', '.$time.')'; 
  312. $modelEUS->query($query); 
  313.  
  314.  
  315. /** 
  316. * update the queued elements which failed and need more tries 
  317. * @param array $queue_update 
  318. * @return boolean 
  319. */ 
  320. function _queueUpdate($queue_update) { 
  321. if(empty($queue_update)) return true; 
  322. $delay = $this->config->getValue('queue_delay', 3600); 
  323. $model_queue=WYSIJA::get('queue', 'model'); 
  324. foreach($queue_update as $email_id => $subscribers) { 
  325. $query = 'UPDATE `[wysija]queue` SET send_at = send_at + '.$delay.', number_try = number_try +1 WHERE email_id = '.$email_id.' AND user_id IN ('.implode(', ', $subscribers).')'; 
  326. $model_queue->query($query); 
  327.  
  328. /** 
  329. */ 
  330. function _handleError() { 
  331. $this->finish = true; 
  332. $message = __('The Send Process stopped because there are too many errors.', WYSIJA); 
  333. $message .= '<br/>'; 
  334. $message .= __('We kept all non delivered emails in the queue, so you will be able to resume the send process later.', WYSIJA); 
  335. $message .= '<br/>'; 
  336. if($this->report) { 
  337. if(empty($this->successSend) AND empty($this->start)) { 
  338. $message .= __('Please verify your mail configuration and make sure you can send a test of this email.', WYSIJA); 
  339. $message .= '<br/>'; 
  340. $message .= __('If you recently, successfully, sent a lot of emails, those errors may also be due to your server limitations.', WYSIJA); 
  341. }else{ 
  342. $message .= __('Your server apparently refuses to send more emails.', WYSIJA); 
  343. $message .= '<br/>'; 
  344. $this->_display($message); 
  345.  
  346.  
  347. /** 
  348. * @param type $message 
  349. * @param type $status 
  350. * @param type $num 
  351. * @return type 
  352. */ 
  353. function _display($message, $status = '', $num = '') { 
  354. $this->messages[] = strip_tags($message); 
  355. if(!$this->report) return; 
  356. if(!empty($num)) { 
  357. $color = $status ? 'black' : 'red'; 
  358. echo '<br/>'.$num.' : <font color="'.$color.'">'.$message.'</font>'; 
  359. }else{ 
  360. echo '<script type="text/javascript" language="javascript">setInfo(\''. addslashes($message) .'\')</script>'; 
  361. if(function_exists('ob_flush')) @ob_flush(); 
  362. @flush(); 
  363.  
  364. /** 
  365. * clear the queue from fucked up things 
  366. * @return boolean 
  367. */ 
  368. function clear() { 
  369.  
  370. $model_config = WYSIJA::get('config', 'model'); 
  371. $model_queue = WYSIJA::get('queue', 'model'); 
  372.  
  373. //remove queued emails of unsubscribed users 
  374. $real_query = 'DELETE a.* FROM `[wysija]queue` as a LEFT JOIN `[wysija]user` as b on a.user_id = b.user_id WHERE b.status< '.$model_config->getValue('confirm_dbleoptin'); 
  375. $model_queue->query($real_query); 
  376.  
  377. //remove queued emails of deleted emails 
  378. $real_query = 'DELETE a.* FROM `[wysija]queue` as a LEFT JOIN `[wysija]email` as b on a.email_id = b.email_id WHERE b.email_id IS NULL'; 
  379. $model_queue->query($real_query); 
  380.  
  381. //remove queued emails of deleted users 
  382. $real_query = 'DELETE a.* FROM `[wysija]queue` as a LEFT JOIN `[wysija]user` as b on a.user_id = b.user_id WHERE b.user_id IS NULL'; 
  383. $model_queue->query($real_query); 
  384.  
  385. //finally check if there are any queued emails left that stays unsent from 2 days ago 
  386. $conditions = array(); 
  387. $conditions['less'] = array( 'send_at' => time() - (3600*48) ); 
  388.  
  389. // replaced the exists function with a count 
  390. $model_queue->setConditions( $conditions ); 
  391. $count_late_in_queue = $model_queue->count(); 
  392.  
  393. if( $count_late_in_queue > 1000 ) { 
  394. //send message your queue cannot send very fast 
  395. $model_config->save( array( 'queue_sends_slow' => 1 ) ); 
  396.  
  397. return true; 
  398.  
  399. /** 
  400. * function not used in wysija but in acy to handle the bounce max try case scenario 
  401. * @param type $subid 
  402. * @return string 
  403. function _subscriberAction($subid) { 
  404. return ''; 
  405. // we don't have a rule bounce_action_maxtry 
  406. if($this->config->getValue('bounce_action_maxtry') == 'delete') { 
  407. $this->subClass->delete($subid); 
  408. return ' user '.$subid.' deleted'; 
  409. $listId = 0; 
  410. if(in_array($this->config->getValue('bounce_action_maxtry'), array('sub', 'remove', 'unsub'))) { 
  411. $status = $this->subClass->getSubscriptionStatus($subid); 
  412. $message = ''; 
  413. $modelU=WYSIJA::get('user', 'model'); 
  414. switch($this->config->getValue('bounce_action_maxtry')) { 
  415. case 'sub' : 
  416. $listId = $this->config->getValue('bounce_action_lists_maxtry'); 
  417. if(!empty($listId)) { 
  418. $message .= ' user '.$subid.' subscribed to '.$listId; 
  419. if(empty($status[$listId])) { 
  420. $this->listsubClass->addSubscription($subid, array('1' => array($listId))); 
  421. }elseif($status[$listId]->status != 1) { 
  422. $this->listsubClass->updateSubscription($subid, array('1' => array($listId))); 
  423. case 'remove' : 
  424. $unsubLists = array_diff(array_keys($status), array($listId)); 
  425. if(!empty($unsubLists)) { 
  426. $message .= ' | user '.$subid.' removed from lists '.implode(', ', $unsubLists); 
  427. $this->listsubClass->removeSubscription($subid, $unsubLists); 
  428. }else{ 
  429. $message .= ' | user '.$subid.' not subscribed'; 
  430. break; 
  431. case 'unsub' : 
  432. $unsubLists = array_diff(array_keys($status), array($listId)); 
  433. if(!empty($unsubLists)) { 
  434. $message .= ' | user '.$subid.' unsubscribed from lists '.implode(', ', $unsubLists); 
  435. $this->listsubClass->updateSubscription($subid, array('-1' => $unsubLists)); 
  436. }else{ 
  437. $message .= ' | user '.$subid.' not subscribed'; 
  438. break; 
  439. case 'delete' : 
  440. $message .= ' | user '.$subid.' deleted'; 
  441. //$this->subClass->delete($subid); 
  442. $modelU->delete($subid); 
  443. $modelU->reset(); 
  444. break; 
  445. case 'block' : 
  446. $message .= ' | user '.$subid.' blocked'; 
  447. $modelU->query('UPDATE `[wysija]user` SET `enabled` = 0 WHERE `user_id` = '.intval($subid)); 
  448. $modelU->query('DELETE FROM `[wysija]queue` WHERE `user_id` = '.intval($subid)); 
  449. break; 
  450. return $message; 
  451. }*/ 
  452.