*
* PHP version 5
*
- * Copyright © 2009-2013 The Galette Team
+ * Copyright © 2009-2023 The Galette Team
*
* This file is part of Galette (http://galette.tuxfamily.org).
*
* @package Galette
*
* @author Johan Cwiklinski <johan@x-tnd.be>
- * @copyright 2009-2013 The Galette Team
+ * @copyright 2009-2023 The Galette Team
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
- * @version SVN: $Id$
* @link http://galette.tuxfamily.org
* @since Available since 0.7dev - 2009-03-07
*/
namespace Galette\Core;
-use Analog\Analog as Analog;
+use Analog\Analog;
+use ArrayObject;
use Galette\Entity\Adherent;
use Galette\IO\File;
+use Laminas\Db\ResultSet\ResultSet;
/**
* Mailing features
* @name Mailing
* @package Galette
* @author Johan Cwiklinski <johan@x-tnd.be>
- * @copyright 2009-2013 The Galette Team
+ * @copyright 2009-2023 The Galette Team
* @license http://www.gnu.org/licenses/gpl-3.0.html GPL License 3.0 or (at your option) any later version
* @link http://galette.tuxfamily.org
* @since Available since 0.7dev - 2009-03-07
+ *
+ * @property string $subject
+ * @property string $message
+ * @property boolean $html
+ * @property integer $current_step
+ * @property-read integer $step
+ * @property integer|string $id
+ * @property-read string $alt_message
+ * @property-read string $wrapped_message
+ * @property-read PHPMailer\PHPMailer\PHPMailer $mail
+ * @property-read PHPMailer\PHPMailer\PHPMailer $_mail
+ * @property-read array $errors
+ * @property-read array $recipients
+ * @property-read string|false $tmp_path
+ * @property array $attachments
+ * @property-read string $sender_name
+ * @property-read string $sender_address
+ * @property integer $history_id
*/
class Mailing extends GaletteMail
{
- const STEP_START = 0;
- const STEP_PREVIEW = 1;
- const STEP_SEND = 2;
- const STEP_SENT = 3;
+ public const STEP_START = 0;
+ public const STEP_PREVIEW = 1;
+ public const STEP_SEND = 2;
+ public const STEP_SENT = 3;
- const MIME_HTML = 'text/html';
- const MIME_TEXT = 'text/plain';
- const MIME_DEFAULT = self::MIME_TEXT;
+ public const MIME_HTML = 'text/html';
+ public const MIME_TEXT = 'text/plain';
+ public const MIME_DEFAULT = self::MIME_TEXT;
- private $_id;
+ private $id;
- private $_unreachables;
- private $_mrecipients;
- private $_current_step;
+ private $unreachables = array();
+ private $mrecipients = array();
+ private $current_step;
- private $_mime_type;
+ private $mime_type;
- private $_tmp_path;
- private $_history_id;
+ private $tmp_path;
+ private $history_id;
/**
* Default constructor
*
- * @param array $members An array of members
- * @param int $id Identifier, defaults to null
+ * @param Preferences $preferences Preferences instance
+ * @param array $members An array of members
+ * @param int $id Identifier, defaults to null
*/
- public function __construct($members, $id = null)
+ public function __construct(Preferences $preferences, array $members = [], int $id = null)
{
- if ( $id !== null ) {
- $this->_id = $id;
- } else {
- $this->_generateNewId();
- }
- $this->_current_step = self::STEP_START;
- $this->_mime_type = self::MIME_DEFAULT;
+ parent::__construct($preferences);
+ $this->id = $id ?? $this->generateNewId();
+
+ $this->current_step = self::STEP_START;
+ $this->mime_type = self::MIME_DEFAULT;
/** TODO: add a preference that propose default mime-type to use,
then init it here */
- if ( $members !== null) {
- //Check which members have a valid email adress and which have not
+ if (count($members)) {
+ //Check which members have a valid email address and which have not
$this->setRecipients($members);
}
- $this->_loadAttachments();
+ $this->loadAttachments();
+ }
+
+ /**
+ * Generate new mailing id and temporary path
+ *
+ * @return string
+ */
+ private function generateNewId(): string
+ {
+ $id = '';
+ $chars = 'abcdefghjkmnpqrstuvwxyz0123456789';
+ $i = 0;
+ $size = 30;
+ while ($i <= $size - 1) {
+ $num = mt_rand(0, strlen($chars) - 1) % strlen($chars);
+ $id .= substr($chars, $num, 1);
+ $i++;
+ }
+
+ $this->id = $id;
+ $this->generateTmpPath($this->id);
+ return $this->id;
}
/**
- * Generate new mailing id
+ * Generate temporary path
+ *
+ * @param string $id Random id, defautls to null
*
* @return void
*/
- private function _generateNewId()
+ private function generateTmpPath($id = null)
{
- $pass = new Password();
- $this->_id = $pass->makeRandomPassword(30);
- $this->_tmp_path = GALETTE_ATTACHMENTS_PATH . '/' . $this->_id;
+ if ($id === null) {
+ $id = $this->generateNewId();
+ }
+ $this->tmp_path = GALETTE_ATTACHMENTS_PATH . '/' . $id;
}
/**
*
* @return void
*/
- private function _loadAttachments()
+ private function loadAttachments()
{
$dir = '';
- if ( isset($this->_tmp_path)
- && trim($this->_tmp_path) !== ''
+ if (
+ isset($this->tmp_path)
+ && trim($this->tmp_path) !== ''
) {
- $dir = $this->_tmp_path;
+ $dir = $this->tmp_path;
} else {
- $dir = GALETTE_ATTACHMENTS_PATH . $this->_id . '/';
+ $dir = GALETTE_ATTACHMENTS_PATH . $this->id . '/';
}
$files = glob($dir . '*.*');
- foreach ( $files as $file ) {
+ foreach ($files as $file) {
$f = new File($dir);
$f->setFileName(str_replace($dir, '', $file));
$this->attachments[] = $f;
/**
* Loads a mailing from history
*
- * @param ResultSet $rs Mailing entry
- * @param boolean $new True if we create a 'new' mailing,
- * false otherwise (from preview for example)
+ * @param ArrayObject $rs Mailing entry
+ * @param boolean $new True if we create a 'new' mailing,
+ * false otherwise (from preview for example)
*
* @return boolean
*/
- public function loadFromHistory($rs, $new = true)
+ public function loadFromHistory(ArrayObject $rs, $new = true)
{
- $orig_recipients = unserialize($rs->mailing_recipients);
+ global $zdb;
+
+ try {
+ $orig_recipients = unserialize($rs->mailing_recipients);
+ } catch (\Throwable $e) {
+ Analog::log(
+ 'Unable to unserialize recipients for mailing ' . $rs->mailing_id,
+ Analog::ERROR
+ );
+ $orig_recipients = [];
+ }
$_recipients = array();
- foreach ( $orig_recipients as $k=>$v ) {
- $m = new Adherent($k);
+ $mdeps = ['parent' => true];
+ foreach ($orig_recipients as $k => $v) {
+ $m = new Adherent($zdb, $k, $mdeps);
$_recipients[] = $m;
}
$this->setRecipients($_recipients);
$this->subject = $rs->mailing_subject;
$this->message = $rs->mailing_body;
+ $this->html = $this->message != strip_tags($this->message) ? true : false;
+ if ($rs->mailing_sender_name !== null || $rs->mailing_sender_address !== null) {
+ $this->setSender(
+ $rs->mailing_sender_name,
+ $rs->mailing_sender_address
+ );
+ }
//if mailing has already been sent, generate a new id and copy attachments
- if ( $rs->mailing_sent && $new ) {
- $this->_generateNewId();
- $this->_copyAttachments($rs->mailing_id);
+ if ($rs->mailing_sent && $new) {
+ $this->generateNewId();
+ $this->copyAttachments($rs->mailing_id);
} else {
- $this->_tmp_path = null;
- $this->_id = $rs->mailing_id;
- $this->_loadAttachments();
- $this->_history_id = $rs->mailing_id;
+ $this->tmp_path = null;
+ $this->id = $rs->mailing_id;
+ if (!$this->attachments) {
+ $this->loadAttachments();
+ }
+ $this->history_id = $rs->mailing_id;
}
+ return true;
}
/**
*
* @return void
*/
- private function _copyAttachments($id)
+ private function copyAttachments($id)
{
$source_dir = GALETTE_ATTACHMENTS_PATH . $id . '/';
- $dest_dir = GALETTE_ATTACHMENTS_PATH . $this->_id . '/';
+ $dest_dir = GALETTE_ATTACHMENTS_PATH . $this->id . '/';
- if ( file_exists($source_dir) ) {
- if ( file_exists($dest_dir) ) {
+ if (file_exists($source_dir)) {
+ if (file_exists($dest_dir)) {
throw new \RuntimeException(
str_replace(
'%s',
- $this->_id,
+ $this->id,
'Attachments directory already exists for mailing %s!'
)
);
//copy attachments from source mailing and populate attachments
$this->attachments = array();
$files = glob($source_dir . '*.*');
- foreach ( $files as $file ) {
+ foreach ($files as $file) {
$f = new File($source_dir);
$f->setFileName(str_replace($source_dir, '', $file));
$f->copyTo($dest_dir);
}
/**
- * Apply final header to mail and send it :-)
+ * Apply final header to email and send it :-)
*
- * @return GaletteMail::MAIL_ERROR|GaletteMail::MAIL_SENT
+ * @return int
*/
public function send()
{
$m = array();
- foreach ( $this->_mrecipients as $member ) {
- $m[$member->email] = $member->sname;
+ foreach ($this->mrecipients as $member) {
+ $email = $member->getEmail();
+ $m[$email] = $member->sname;
}
parent::setRecipients($m);
return parent::send();
*
* @param array $members Array of Adherent objects
*
- * @return void
+ * @return bool
*/
public function setRecipients($members)
{
$m = array();
- $this->_mrecipients = array();
- $this->_unreachables = array();
+ $this->mrecipients = array();
+ $this->unreachables = array();
foreach ($members as $member) {
- $email = $member->email;
- if ( trim($email) != '' && self::isValidEmail($email) ) {
- if ( !in_array($member, $this->_mrecipients) ) {
- $this->_mrecipients[] = $member;
+ $email = $member->getEmail();
+
+ if (trim($email) != '' && self::isValidEmail($email)) {
+ if (!in_array($member, $this->mrecipients)) {
+ $this->mrecipients[] = $member;
}
$m[$email] = $member->sname;
} else {
- if ( !in_array($member, $this->_unreachables) ) {
- $this->_unreachables[] = $member;
+ if (!in_array($member, $this->unreachables)) {
+ $this->unreachables[] = $member;
}
}
}
- parent::setRecipients($m);
+ return parent::setRecipients($m);
}
/**
*/
public function store($files)
{
- if ( !file_exists($this->_tmp_path) ) {
- //directory does not exists, create it
- mkdir($this->_tmp_path);
+ if ($this->tmp_path === null) {
+ $this->generateTmpPath();
}
- if ( !is_dir($this->_tmp_path) ) {
+ if (!file_exists($this->tmp_path)) {
+ //directory does not exist, create it
+ mkdir($this->tmp_path);
+ }
+
+ if (!is_dir($this->tmp_path)) {
throw new \RuntimeException(
- $this->_tmp_path . ' should be a directory!'
+ $this->tmp_path . ' should be a directory!'
);
}
//store files
- $attachment = new File($this->_tmp_path);
+ $attachment = new File($this->tmp_path);
$res = $attachment->store($files);
- if ( $res < 0 ) {
+ if ($res < 0) {
return $res;
} else {
$this->attachments[] = $attachment;
*
* @param int $id Mailing history id
*
- * @return boolean
+ * @return void
*/
public function moveAttachments($id)
{
- if ( isset($this->_tmp_path)
- && trim($this->_tmp_path) !== ''
+ if (
+ isset($this->tmp_path)
+ && trim($this->tmp_path) !== ''
+ && count($this->attachments) > 0
) {
- foreach ( $this->attachments as &$attachment ) {
+ foreach ($this->attachments as &$attachment) {
$old_path = $attachment->getDestDir() . $attachment->getFileName();
- $new_path = GALETTE_ATTACHMENTS_PATH . $this->_id .'/' . $attachment->getFileName();
- if ( !file_exists(GALETTE_ATTACHMENTS_PATH . $this->_id) ) {
- mkdir(GALETTE_ATTACHMENTS_PATH . $this->_id);
+ $new_path = GALETTE_ATTACHMENTS_PATH . $id . '/' .
+ $attachment->getFileName();
+ if (!file_exists(GALETTE_ATTACHMENTS_PATH . $id)) {
+ mkdir(GALETTE_ATTACHMENTS_PATH . $id);
}
$moved = rename($old_path, $new_path);
- if ( $moved ) {
+ if ($moved) {
$attachment->setDestDir(GALETTE_ATTACHMENTS_PATH);
}
}
- rmdir($this->_tmp_path);
- $this->_tmp_path = null;
+ rmdir($this->tmp_path);
+ $this->tmp_path = null;
}
}
public function removeAttachment($name)
{
$to_remove = null;
- if ( isset($this->_tmp_path)
- && trim($this->_tmp_path) !== ''
- && file_exists($this->_tmp_path)
+ if (
+ isset($this->tmp_path)
+ && trim($this->tmp_path) !== ''
+ && file_exists($this->tmp_path)
) {
- $to_remove = $this->_tmp_path;
- } else if ( file_exists(GALETTE_ATTACHMENTS_PATH . $this->_id) ) {
- $to_remove = GALETTE_ATTACHMENTS_PATH . $this->_id;
+ $to_remove = $this->tmp_path;
+ } elseif (file_exists(GALETTE_ATTACHMENTS_PATH . $this->id)) {
+ $to_remove = GALETTE_ATTACHMENTS_PATH . $this->id;
}
- if ( $to_remove !== null ) {
+ if ($to_remove !== null) {
$to_remove .= '/' . $name;
- if ( !$this->attachments ) {
- $this->_loadAttachments();
+ if (!$this->attachments) {
+ $this->loadAttachments();
}
- if ( file_exists($to_remove) ) {
+ if (file_exists($to_remove)) {
$i = 0;
- foreach ( $this->attachments as $att ) {
- if ( $att->getFileName() == $name ) {
+ foreach ($this->attachments as $att) {
+ if ($att->getFileName() == $name) {
unset($this->attachments[$i]);
unlink($to_remove);
break;
/**
* Remove mailing attachments
*
- * @param boolean $temp Remove only tmporary attachments, to avoid history breaking
+ * @param boolean $temp Remove only tmporary attachments,
+ * to avoid history breaking
*
- * @return void
+ * @return void|false
*/
public function removeAttachments($temp = false)
{
$to_remove = null;
- if ( isset($this->_tmp_path)
- && trim($this->_tmp_path) !== ''
- && file_exists($this->_tmp_path)
+ if (
+ isset($this->tmp_path)
+ && trim($this->tmp_path) !== ''
+ && file_exists($this->tmp_path)
) {
- $to_remove = $this->_tmp_path;
- } else if ( file_exists(GALETTE_ATTACHMENTS_PATH . $this->_id) ) {
- if ( $temp === true ) {
+ $to_remove = $this->tmp_path;
+ } elseif (file_exists(GALETTE_ATTACHMENTS_PATH . $this->id)) {
+ if ($temp === true) {
return false;
}
- $to_remove = GALETTE_ATTACHMENTS_PATH . $this->_id;
+ $to_remove = GALETTE_ATTACHMENTS_PATH . $this->id;
}
- if ( $to_remove !== null ) {
+ if ($to_remove !== null) {
$rdi = new \RecursiveDirectoryIterator(
$to_remove,
\FilesystemIterator::SKIP_DOTS
$rdi,
\RecursiveIteratorIterator::CHILD_FIRST
);
- foreach ( $contents as $path) {
- if ( $path->isFile() ) {
+ foreach ($contents as $path) {
+ if ($path->isFile()) {
unlink($path->getPathname());
} else {
rmdir($path->getPathname());
*/
public function getAttachmentErrorMessage($code)
{
- $f = new File($this->_tmp_path);
+ $f = new File($this->tmp_path);
return $f->getErrorMessage($code);
}
*/
public function existsInHistory()
{
- return isset($this->_history_id);
+ return isset($this->history_id);
}
/**
* Global getter method
*
- * @param string $name name of the property we want to retrive
+ * @param string $name name of the property we want to retrieve
*
- * @return false|object the called property
+ * @return mixed the called property
*/
public function __get($name)
{
$forbidden = array('ordered');
- if ( !in_array($name, $forbidden) ) {
- switch($name) {
- case 'alt_message':
- return $this->cleanedHtml();
- break;
- case 'step':
- return $this->current_step;
- break;
- case 'subject':
- return $this->getSubject();
- break;
- case 'message':
- return $this->getMessage();
- break;
- case 'html':
- return $this->isHTML();
- break;
- case 'mail':
- case '_mail':
- return $this->getPhpMailer();
- break;
- case 'errors':
- return $this->getErrors();
- break;
- case 'recipients':
- return $this->_mrecipients;
- break;
- case 'tmp_path':
- if ( isset($this->_tmp_path) && trim($this->_tmp_path) !== '') {
- return $this->_tmp_path;
- } else {
- //no attachments
- return false;
- }
- break;
- case 'attachments':
- return $this->attachments;
- break;
- default:
- $rname = '_' . $name;
- Analog::log(
- '[' . get_class($this) . 'Trying to get ' . $name .
- ' renamed: ' . $rname,
- Analog::DEBUG
- );
- return $this->$rname;
- break;
+ if (!in_array($name, $forbidden)) {
+ switch ($name) {
+ case 'alt_message':
+ return $this->cleanedHtml();
+ case 'step':
+ return $this->current_step;
+ case 'subject':
+ return $this->getSubject();
+ case 'message':
+ return $this->getMessage();
+ case 'wrapped_message':
+ return $this->getWrappedMessage();
+ case 'html':
+ return $this->isHTML();
+ case 'mail':
+ case '_mail':
+ return $this->getPhpMailer();
+ case 'errors':
+ return $this->getErrors();
+ case 'recipients':
+ return $this->mrecipients;
+ case 'tmp_path':
+ if (isset($this->tmp_path) && trim($this->tmp_path) !== '') {
+ return $this->tmp_path;
+ } else {
+ //no attachments
+ return false;
+ }
+ case 'attachments':
+ return $this->attachments;
+ case 'sender_name':
+ return $this->getSenderName();
+ case 'sender_address':
+ return $this->getSenderAddress();
+ case 'history_id':
+ return $this->$name;
+ default:
+ Analog::log(
+ '[' . get_class($this) . 'Trying to get ' . $name,
+ Analog::DEBUG
+ );
+ return $this->$name;
}
} else {
Analog::log(
- '[' . get_class($this) . 'Unable to get ' . $name .
- ' renamed: ' . $rname,
+ '[' . get_class($this) . 'Unable to get ' . $name,
Analog::ERROR
);
return false;
}
/**
- * Global setter method
- *
- * @param string $name name of the property we want to assign a value to
- * @param object $value a relevant value for the property
- *
- * @return void
- */
- public function __set($name, $value)
+ * Global isset method
+ * Required for twig to access properties via __get
+ *
+ * @param string $name name of the property we want to retrieve
+ *
+ * @return bool
+ */
+ public function __isset($name)
{
- $rname = '_' . $name;
-
- switch( $name ) {
- case 'subject':
- $this->setSubject($value);
- break;
- case 'message':
- $this->setMessage($value);
- break;
- case 'html':
- if ( is_bool($value) ) {
- $this->isHTML($value);
- } else {
- Analog::log(
- '[' . get_class($this) . '] Value for field `' . $name .
- '` should be boolean - (' . gettype($value) . ')' .
- $value . ' given',
- Analog::WARNING
- );
+ $forbidden = array('ordered');
+ if (!in_array($name, $forbidden)) {
+ switch ($name) {
+ case 'alt_message':
+ case 'step':
+ case 'subject':
+ case 'message':
+ case 'wrapped_message':
+ case 'html':
+ case 'mail':
+ case '_mail':
+ case 'errors':
+ case 'recipients':
+ case 'tmp_path':
+ case 'attachments':
+ case 'sender_name':
+ case 'sender_address':
+ return true;
}
- break;
- case 'current_step':
- if ( is_int($value)
- && ( $value == self::STEP_START
- || $value == self::STEP_PREVIEW
- || $value == self::STEP_SEND
- || $value == self::STEP_SENT )
- ) {
- $this->_current_step = (int)$value;
- } else {
+ return isset($this->$name);
+ }
+
+ return false;
+ }
+
+ /**
+ * Global setter method
+ *
+ * @param string $name name of the property we want to assign a value to
+ * @param mixed $value a relevant value for the property
+ *
+ * @return void
+ */
+ public function __set($name, $value)
+ {
+ switch ($name) {
+ case 'subject':
+ $this->setSubject($value);
+ break;
+ case 'message':
+ $this->setMessage($value);
+ break;
+ case 'html':
+ if (is_bool($value)) {
+ $this->isHTML($value);
+ } else {
+ Analog::log(
+ '[' . get_class($this) . '] Value for field `' . $name .
+ '` should be boolean - (' . gettype($value) . ')' .
+ $value . ' given',
+ Analog::WARNING
+ );
+ }
+ break;
+ case 'current_step':
+ if (
+ is_int($value)
+ && ($value == self::STEP_START
+ || $value == self::STEP_PREVIEW
+ || $value == self::STEP_SEND
+ || $value == self::STEP_SENT)
+ ) {
+ $this->current_step = (int)$value;
+ } else {
+ Analog::log(
+ '[' . get_class($this) . '] Value for field `' . $name .
+ '` should be integer and know - (' . gettype($value) . ')' .
+ $value . ' given',
+ Analog::WARNING
+ );
+ }
+ break;
+ case 'id':
+ $this->id = $value;
+ break;
+ default:
Analog::log(
- '[' . get_class($this) . '] Value for field `' . $name .
- '` should be integer and know - (' . gettype($value) . ')' .
- $value . ' given',
+ '[' . get_class($this) . '] Unable to set property `' . $name . '`',
Analog::WARNING
);
- }
- break;
- case 'id':
- $this->_id = $value;
- break;
- default:
- Analog::log(
- '[' . get_class($this) . '] Unable to set proprety `' . $name . '`',
- Analog::WARNING
- );
- return false;
- break;
}
}
}