This commit is contained in:
proelements
2025-11-13 15:18:34 +02:00
commit 9ac2bf2aa0
1178 changed files with 296944 additions and 0 deletions

View File

@@ -0,0 +1,50 @@
<?php
namespace ElementorPro\Core\Integrations\Actions\Email;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Email_Address {
/**
* Recipient email address.
*
* @var array
*/
public $address;
/**
* Recipient name.
*
* @var string
*/
public $name;
/**
* Email_Address constructor.
*
* @param string $address
* @param string $name
*
* @return void
*/
public function __construct( $address, $name ) {
$this->address = (string) $address;
$this->name = (string) $name;
}
/**
* Format an email to be ready for header (e.g. `Recipient Name <user@email.com>` or `user@email.com`)
*
* @return string
*/
public function format() {
if ( ! empty( $this->name ) ) {
return sprintf( '%s <%s>', $this->name, $this->address );
}
return sprintf( '%s', $this->address );
}
}

View File

@@ -0,0 +1,239 @@
<?php
namespace ElementorPro\Core\Integrations\Actions\Email;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Email_Message {
/**
* Email sender.
*
* @var Email_Address
*/
public $from;
/**
* Email recipient.
*
* @var Email_Address
*/
public $to;
/**
* Email reply to address.
*
* @var Email_Address[]
*/
public $reply_to = [];
/**
* Email CC recipient.
*
* @var Email_Address[]
*/
public $cc = [];
/**
* Email BCC recipient.
*
* @var Email_Address[]
*/
public $bcc = [];
/**
* Email subject.
*
* @var string
*/
public $subject;
/**
* Email content type.
*
* @var string
*/
public $content_type;
/**
* Email body.
*
* @var string
*/
public $body;
/**
* Email attachments.
*
* @var array
*/
public $attachments = [];
/**
* Email_Message constructor.
*
* @return void
*/
public function __construct() {
// Set defaults.
$this->from( get_bloginfo( 'admin_email' ), get_bloginfo( 'name' ) );
}
/**
* Set the email sender.
*
* @param string $email
* @param string|null $name
*
* @return $this
*/
public function from( $email, $name = null ) {
$this->from = new Email_Address( $email, $name );
return $this;
}
/**
* Set the email recipient.
*
* @param string $email
* @param string|null $name
*
* @return $this
*/
public function to( $email, $name = null ) {
$this->to = new Email_Address( $email, $name );
return $this;
}
/**
* Add a reply to.
*
* @param string $email
* @param string|null $name
*
* @return $this
*/
public function reply_to( $email, $name = null ) {
$this->reply_to[] = new Email_Address( $email, $name );
return $this;
}
/**
* Add a CC.
*
* @param string $email
* @param string|null $name
*
* @return $this
*/
public function cc( $email, $name = null ) {
$this->cc[] = new Email_Address( $email, $name );
return $this;
}
/**
* Add a BCC.
*
* @param string $email
* @param string|null $name
*
* @return $this
*/
public function bcc( $email, $name = null ) {
$this->bcc[] = new Email_Address( $email, $name );
return $this;
}
/**
* Set the email subject.
*
* @param string $subject
*
* @return $this
*/
public function subject( $subject ) {
$this->subject = (string) $subject;
return $this;
}
/**
* Set the email content type.
*
* @param string $content_type
*
* @return $this
*/
public function content_type( $content_type ) {
$this->content_type = (string) $content_type;
return $this;
}
/**
* Set the email body using plain text.
*
* @param string $body
* @param string $content_type
*
* @return $this
*/
public function body( $body, $content_type = 'text/html' ) {
$this->body = (string) $body;
return $this->content_type( $content_type );
}
/**
* Set the email body using a view.
*
* @param string $path - View path,
* @param array $data - Data that will be passes to the view.
*
* @return $this
* @throws \Exception
*/
public function view( $path, $data = [] ) {
if ( ! is_file( $path ) ) {
throw new \Exception( "`{$path}` is not a valid view." );
}
ob_start();
// Inspired from Laravel's view mechanism:
// [1] https://github.dev/illuminate/filesystem/blob/b179f9ea3b3195d1f4b5ae2aee67e42eac6ceb5e/Filesystem.php#L98
// [2] https://github.dev/illuminate/view/blob/6dd315634a44450c5e443fa8735d4a526833fad3/Engines/PhpEngine.php#L48
call_user_func( function( $__view_path, $__view_data ) {
extract( $__view_data, EXTR_SKIP ); // phpcs:ignore WordPress.PHP.DontExtract.extract_extract
unset( $__view_data );
// `$__view_data` keys are available in the file as variables.
require $__view_path;
}, $path, $data );
$this->body = ob_get_clean();
return $this->content_type( 'text/html' );
}
/**
* Add an attachment.
*
* @param string $path - Attachment path on the server.
*
* @return $this
*/
public function attach( $path ) {
$this->attachments[] = (string) $path;
return $this;
}
}

View File

@@ -0,0 +1,128 @@
<?php
namespace ElementorPro\Core\Integrations\Actions\Email;
use ElementorPro\Core\Integrations\Actions\Action_Base;
use ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception;
use ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception;
if ( ! defined( 'ABSPATH' ) ) {
exit; // Exit if accessed directly
}
class Email extends Action_Base {
/**
* @param Email_Message $payload
*
* @return void
* @throws \Exception
*/
public function apply( $payload ) {
// Set default headers.
$headers = [
sprintf( 'Content-Type: %s; charset=UTF-8', $payload->content_type ),
sprintf( 'From: %s', $payload->from->format() ),
];
foreach ( $payload->reply_to as $recipient ) {
$headers[] = sprintf( 'Reply-To: %s', $recipient->format() );
}
// Set CC headers.
$cc_headers = [];
foreach ( $payload->cc as $recipient ) {
$cc_headers[] = sprintf( 'Cc: %s', $recipient->format() );
}
// Send email.
$this->send_mail(
$payload->to->format(),
$payload->subject,
$payload->body,
implode( PHP_EOL, array_merge( $headers, $cc_headers ) ),
$payload->attachments
);
// Send BCC emails.
foreach ( $payload->bcc as $bcc ) {
$this->send_mail(
$bcc->format(),
$payload->subject,
$payload->body,
implode( PHP_EOL, $headers ),
$payload->attachments
);
}
}
/**
* @alias `$this->run()`
*
* @param Email_Message $payload
*
* @return void
*@throws \Exception
*
*/
public function send( Email_Message $payload ) {
$this->run( $payload );
}
/**
* Validate the email message DTO.
*
* @param Email_Message $payload
*
* @throws \ElementorPro\Core\Integrations\Exceptions\Action_Validation_Failed_Exception
*
* @return void
*/
public function validate( $payload ) {
$required_fields = [
'from',
'to',
'subject',
'body',
'content_type',
];
foreach ( $required_fields as $field ) {
if ( empty( $payload->{$field} ) ) {
throw new Action_Validation_Failed_Exception(
static::class,
"`Email_Message::\${$field}` is required."
);
}
}
}
/**
* Calls `wp_mail()`. Used for testing.
*
* @param mixed ...$args
*
* @return void
*/
protected function send_mail( ...$args ) {
add_action( 'wp_mail_failed', [ $this, 'on_wp_mail_error' ] );
wp_mail( ...$args );
remove_action( 'wp_mail_failed', [ $this, 'on_wp_mail_error' ] );
}
/**
* Throw exception on `wp_mail()` error.
*
* @param \WP_Error $error
*
* @throws \ElementorPro\Core\Integrations\Exceptions\Action_Failed_Exception
*
* @return void
*/
public function on_wp_mail_error( \WP_Error $error ) {
throw new Action_Failed_Exception( static::class, '`wp_mail()` cannot send email', $error );
}
}