*/
private $children;
/**
* @ORM\OneToMany(targetEntity="MailPoet\Entities\NewsletterSegmentEntity", mappedBy="newsletter", orphanRemoval=true)
* @var ArrayCollection
*/
private $newsletterSegments;
/**
* @ORM\OneToMany(targetEntity="MailPoet\Entities\NewsletterOptionEntity", mappedBy="newsletter", orphanRemoval=true)
* @var ArrayCollection
*/
private $options;
/**
* @ORM\OneToMany(targetEntity="MailPoet\Entities\SendingQueueEntity", mappedBy="newsletter")
* @var ArrayCollection
*/
private $queues;
/**
* @ORM\OneToOne(targetEntity="MailPoet\Entities\WpPostEntity")
* @ORM\JoinColumn(name="wp_post_id", referencedColumnName="ID", nullable=true)
* @var WpPostEntity|null
*/
private $wpPost;
public function __construct() {
$this->children = new ArrayCollection();
$this->newsletterSegments = new ArrayCollection();
$this->options = new ArrayCollection();
$this->queues = new ArrayCollection();
}
/**
* @deprecated This is here only for backward compatibility with custom shortcodes https://kb.mailpoet.com/article/160-create-a-custom-shortcode
* This can be removed after 2026-01-01
*/
public function __get($key) {
$getterName = 'get' . Helpers::underscoreToCamelCase($key, $capitaliseFirstChar = true);
$callable = [$this, $getterName];
if (is_callable($callable)) {
// phpcs:ignore WordPress.PHP.DevelopmentFunctions.error_log_trigger_error -- Intended for deprecation warnings
trigger_error(
// phpcs:ignore WordPress.Security.EscapeOutput.OutputNotEscaped -- if the function is callable, it's safe to output
"Direct access to \$newsletter->{$key} is deprecated and will be removed after 2026-01-01. Use \$newsletter->{$getterName}() instead.",
E_USER_DEPRECATED
);
return call_user_func($callable);
}
}
public function __clone() {
// reset ID
$this->id = null;
$this->newsletterSegments = new ArrayCollection();
$this->children = new ArrayCollection();
$this->options = new ArrayCollection();
$this->queues = new ArrayCollection();
}
/**
* @return string|null
*/
public function getHash() {
return $this->hash;
}
/**
* @param string|null $hash
*/
public function setHash($hash) {
$this->hash = $hash;
}
/**
* @return string
*/
public function getSubject() {
return $this->subject;
}
/**
* @param string $subject
*/
public function setSubject($subject) {
$this->subject = $subject;
}
/**
* @return string
*/
public function getType() {
return $this->type;
}
/**
* @param string $type
*/
public function setType($type) {
$this->type = $type;
}
/**
* @return string
*/
public function getSenderAddress() {
return $this->senderAddress;
}
/**
* @param string $senderAddress
*/
public function setSenderAddress($senderAddress) {
$this->senderAddress = $senderAddress;
}
/**
* @return string
*/
public function getSenderName() {
return $this->senderName;
}
/**
* @param string $senderName
*/
public function setSenderName($senderName) {
$this->senderName = $senderName;
}
/**
* @return string
*/
public function getStatus() {
return $this->status;
}
/**
* @param string $status
*/
public function setStatus($status) {
$this->status = $status;
// activate/deactivate unfinished tasks
$newTaskStatus = null;
if (($status === self::STATUS_DRAFT) && $this->canBeSetActive()) {
$newTaskStatus = ScheduledTaskEntity::STATUS_PAUSED;
}
if (($status === self::STATUS_ACTIVE) && $this->canBeSetActive()) {
$newTaskStatus = ScheduledTaskEntity::STATUS_SCHEDULED;
}
if (!$newTaskStatus) return;
$queues = $this->getUnfinishedQueues();
foreach ($queues as $queue) {
/** @var SendingQueueEntity $queue */
$task = $queue->getTask();
if ($task === null) continue;
$scheduled = new Carbon($task->getScheduledAt());
if ($scheduled < (new Carbon())->subDays(30)) continue;
if (($status === self::STATUS_DRAFT) && ($task->getStatus() !== ScheduledTaskEntity::STATUS_SCHEDULED)) continue;
if (($status === self::STATUS_ACTIVE) && ($task->getStatus() !== ScheduledTaskEntity::STATUS_PAUSED)) continue;
$task->setStatus($newTaskStatus);
}
}
/**
* @return string
*/
public function getReplyToAddress() {
return $this->replyToAddress;
}
/**
* @param string $replyToAddress
*/
public function setReplyToAddress($replyToAddress) {
$this->replyToAddress = $replyToAddress;
}
/**
* @return string
*/
public function getReplyToName() {
return $this->replyToName;
}
/**
* @param string $replyToName
*/
public function setReplyToName($replyToName) {
$this->replyToName = $replyToName;
}
/**
* @return string
*/
public function getPreheader() {
return $this->preheader;
}
/**
* @param string $preheader
*/
public function setPreheader($preheader) {
$this->preheader = $preheader;
}
/**
* @return array|null
*/
public function getBody() {
return $this->body;
}
/**
* @param array|null $body
*/
public function setBody($body) {
$this->body = $body;
}
/**
* @return DateTimeInterface|null
*/
public function getSentAt() {
return $this->sentAt;
}
/**
* @param DateTimeInterface|null $sentAt
*/
public function setSentAt($sentAt) {
$this->sentAt = $sentAt;
}
/**
* @return string|null
*/
public function getUnsubscribeToken() {
return $this->unsubscribeToken;
}
/**
* @return string
*/
public function getGaCampaign() {
return $this->gaCampaign;
}
/**
* @param string $gaCampaign
*/
public function setGaCampaign($gaCampaign) {
$this->gaCampaign = $gaCampaign;
}
/**
* @param string|null $unsubscribeToken
*/
public function setUnsubscribeToken($unsubscribeToken) {
$this->unsubscribeToken = $unsubscribeToken;
}
/**
* @return NewsletterEntity|null
*/
public function getParent() {
$this->safelyLoadToOneAssociation('parent');
return $this->parent;
}
/**
* @param NewsletterEntity|null $parent
*/
public function setParent($parent) {
$this->parent = $parent;
}
/**
* @return ArrayCollection
*/
public function getChildren() {
return $this->children;
}
/**
* @return ArrayCollection
*/
public function getNewsletterSegments() {
return $this->newsletterSegments;
}
/**
* @return int[]
*/
public function getSegmentIds() {
return array_filter($this->newsletterSegments->map(function(NewsletterSegmentEntity $newsletterSegment = null) {
if (!$newsletterSegment) return null;
$segment = $newsletterSegment->getSegment();
return $segment ? (int)$segment->getId() : null;
})->toArray());
}
/**
* @return ArrayCollection
*/
public function getOptions() {
return $this->options;
}
public function getOption(string $name): ?NewsletterOptionEntity {
$option = $this->options->filter(function (NewsletterOptionEntity $option = null) use ($name): bool {
if (!$option) return false;
return ($field = $option->getOptionField()) ? $field->getName() === $name : false;
})->first();
return $option ?: null;
}
/**
* @return array Associative array of newsletter option values with option names as keys
*/
public function getOptionsAsArray(): array {
$optionsArray = [];
foreach ($this->options as $option) {
$name = $option->getName();
if (!$name) {
continue;
}
$optionsArray[$name] = $option->getValue();
}
return $optionsArray;
}
public function getOptionValue(string $name) {
$option = $this->getOption($name);
return $option ? $option->getValue() : null;
}
public function getFilterSegmentId(): ?int {
$optionValue = $this->getOptionValue(NewsletterOptionFieldEntity::NAME_FILTER_SEGMENT_ID);
if ($optionValue) {
return (int)$optionValue;
}
$parentNewsletter = $this->getParent();
if ($parentNewsletter instanceof NewsletterEntity && $this->getId() !== $parentNewsletter->getId()) {
return $parentNewsletter->getFilterSegmentId();
}
return null;
}
/**
* @return ArrayCollection
*/
public function getQueues() {
return $this->queues;
}
public function getLatestQueue(): ?SendingQueueEntity {
$criteria = new Criteria();
$criteria->orderBy(['id' => Criteria::DESC]);
$criteria->setMaxResults(1);
return $this->queues->matching($criteria)->first() ?: null;
}
public function getLastUpdatedQueue(): ?SendingQueueEntity {
$criteria = new Criteria();
$criteria->orderBy(['updatedAt' => Criteria::DESC]);
$criteria->setMaxResults(1);
return $this->queues->matching($criteria)->first() ?: null;
}
/**
* @return Collection
*/
public function getUnfinishedQueues(): Collection {
$criteria = new Criteria();
$expr = Criteria::expr();
$criteria->where($expr->neq('countToProcess', 0));
return $this->queues->matching($criteria);
}
public function getGlobalStyle(string $category, string $style): ?string {
$body = $this->getBody();
if ($body === null) {
return null;
}
return $body['globalStyles'][$category][$style] ?? null;
}
public function setGlobalStyle(string $category, string $style, $value): void {
$body = $this->getBody();
if ($body === null) {
return;
}
$this->body['globalStyles'][$category][$style] = $value;
}
public function getProcessedAt(): ?DateTimeInterface {
$processedAt = null;
$queue = $this->getLatestQueue();
if ($queue instanceof SendingQueueEntity) {
$task = $queue->getTask();
if ($task instanceof ScheduledTaskEntity) {
$processedAt = $task->getProcessedAt();
}
}
return $processedAt;
}
public function getContent(): string {
$content = $this->getBody()['content'] ?? '';
return json_encode($content) ?: '';
}
/**
* Only some types of newsletters can be set as sent. Some others are just active or draft.
*/
public function canBeSetSent(): bool {
return in_array($this->getType(), [self::TYPE_NOTIFICATION_HISTORY, self::TYPE_STANDARD], true);
}
public function canBeSetActive(): bool {
return in_array($this->getType(), self::ACTIVABLE_EMAILS, true);
}
public function getWpPost(): ?WpPostEntity {
$this->safelyLoadToOneAssociation('wpPost');
return $this->wpPost;
}
public function setWpPost(?WpPostEntity $wpPostEntity): void {
$this->wpPost = $wpPostEntity;
}
public function getWpPostId(): ?int {
$wpPost = $this->wpPost;
return $wpPost ? $wpPost->getId() : null;
}
public function getCampaignName(): ?string {
$wpPost = $this->getWpPost();
if (!$wpPost) {
return null;
}
return $wpPost->getPostTitle();
}
/**
* Used for cases when we present newsletter by name.
* Newsletters created via legacy editor have only subjects.
*/
public function getCampaignNameOrSubject(): string {
$campaignName = $this->getCampaignName();
return $campaignName ?: $this->getSubject();
}
public function isTransactional(): bool {
// Legacy Abandoned Cart emails are transactional
if (
$this->getType() === NewsletterEntity::TYPE_AUTOMATIC
&& $this->getOptionValue(NewsletterOptionFieldEntity::NAME_EVENT) === AbandonedCart::SLUG
) {
return true;
}
return in_array($this->getType(), [
NewsletterEntity::TYPE_AUTOMATION_TRANSACTIONAL,
NewsletterEntity::TYPE_WC_TRANSACTIONAL_EMAIL,
]);
}
}