
Sending corporate emails to a group is a tool that is used in all companies and projects, since it is the means of sending newsletters, etc., messages that you want to share with a wide range of people.
There are many Open Source tools that do this function, but those of us who work with PHPRunner like to see it on this platform and, above all, integrated with our applications.
Solution requirements
When I thought about doing this example, I set myself these goals:
- Must be using a Gmail account (XOAUTH2). This type of authentication is required by the most secure email management platforms.
- He had to use a very complete message editor, in order to make messages very rich in graphic features, with an image or multimedia manager.
- You should be able to use text written in Word.
- You should be able to personalize the message with the recipient's information (name, last name, email, etc.)
- You should be able to send attachments.
- You should be able to select (multiple ways) the recipient list.
- It should be multi-language (English and Spanish) in all its components. This makes it easy to adapt to other languages
As in all the examples I post on the Blog, these are not applications, but rather examples for each one to personalize and integrate in their solutions.
DEMO environment
DEMO: https://fhumanes.com/postmanager
User: admin
Password: admin
I have limited the example to a maximum of 5 Contacts, so that it is not used as SPAM. Contacts can be deleted, so that each user can put an email account where they want to see the result of the email.
Functional description
Since the objective is not to make a "finished" product but to make it reusable for customization by the developers of PHPRunner, I am going to make special reference to the two entities in which I have worked on the example.
Steps to build an email:

You can create, edit and view the Message. This has a subject, a message body, and attachments.
The (rich) editor that comes with PHPRunner, Innovaeditor and the file manager that comes with it are used for the body of the message. The technical part will explain the changes that have been made in this editor.
These labels will be used in the text and will be replaced with the recipient's information:
{name} .- Name of the contact
{surname} .- Contact surname
{email} .- Contact email
Also, in the "paste" option you have an option to improve the HTML of Word documents. With this functionality, you can write the messages in Word and "paste" in the body of the messages.
Once we have the message we go to select the recipient contacts and for this we go to the "Contacts" option.

On this screen I have put several ways to select the Contacts list, although in both ways it starts with the click of the button "Select for Post" (1).
- Contacts can be selected with the check (2) that each Contact record has.
- If there is no selection, all the records shown are collected according to the selection of filters (3) or search (4) that have been indicated.
Starting from the selection, a popup screen will appear where we will have to indicate to which message the list of selected contacts is associated. This operation can be done multiple times.
Once these operations are done, associated with the Messages we will have the list of contacts that we have created for the message.

The sending of emails begins with the click of the "Send Email" button (1).
Also here are two methods of indicating to whom from the list the message should be sent:
- It is sent to the Contacts who have check (2). The message is sent even if it has been sent previously.
- If no contact has been checked, it is sent to all the users on the list, provided it has not been previously sent. This is controlled because the send date (3) is null, if the message has not been sent.
Technical data of the solution
The data model that the example uses is:

As I have indicated it is very simple. The goal is to personalize it with the needs of each organization.

As in almost all the examples I do, there is a configuration option to customize the installation data. I think the labels are self explanatory.
Innovaeditor editor adaptations
When reviewing the solution, especially in the management of resources (images, video, etc.) and the use that PHPRunner uses as standard, it was identified:
- There is no way (from PHPRunner) to customize the directory where the resources are located.
- The resource manager can be used without controlling access (a serious security issue)
- Although the editor has the possibility of being multi-language, it cannot be configured from PHPRunner.
The changes I have made, in the location of the PHPRunner installation (in my case C:\Program Files\PHPRunner10.4\source\plugins\innovaeditor) are:
. In directory "C:\Program Files\PHPRunner10.4\source\plugins\innovaeditor\assetmanager":
File "assetmanager.php", these lines are added.

// Control environment PHPRunner
$name_file = __DIR__."/phprunner_setting.php";
if (file_exists($name_file)) {
require __DIR__."/phprunner_setting.php";
}
The file "phprunner_setting.php" has been added with this content:
<?php
require_once("../../../include/dbcommon.php"); // DataBase PHPRunner
// Control user login
if (!isset($_SESSION["UserID"])) {
die("Please connect aplications");
}
// Confgig assetmanager
$bReturnAbsolute=false;
$sBaseVirtual0=$_SESSION['config'][array_search('EDITOR_BaseVirtual0', array_column($_SESSION['config'], 'name'))][value];
$sBase0=$_SESSION['config'][array_search('EDITOR_Base0', array_column($_SESSION['config'], 'name'))][value];
if ($_SESSION["language"] == "Spanish") {
$sLang="es-ES";
} else {
$sLang="en-US";
}
. In directory "C:\Program Files\PHPRunner10.4\source":
Innova.php file (it is for multi-language management)

if ($_SESSION["language"] == "Spanish") {
$sLang="es-ES";
$LangScript = '<script type="text/javascript" src="'. projectURL() . ("plugins/innovaeditor/scripts/language/".$sLang."/editor_lang.js").'"></script>';
} else {
$sLang="en-US";
$LangScript='';
}
echo '<html>
<body style="margin:0;">
<form name="innovaform" '.$onsubmit.'>'.
$LangScript.
'<script type="text/javascript" src="'. projectURL() . ("plugins/innovaeditor/scripts/innovaeditor.js").'"></script>
<textarea id="'.$cfield.'" name="'.$cfield.'" style="width:100%; height:'.($nHeight).'px;">';
Mail sending function
The PHPMAILER library is used, in its 6.1 version. This is the latest version and is not the same version that incorporates the PHPRunner product.
The code, with its comments, is the file "send_mail.php":
<?php
// For send of email selected
// $_SESSION['mail_select'] Id of email_contact selected
// $_SESSION['mail_where'] Condition where last select of email_contact
$select_record = $_SESSION['mail_select'];
$count = count($_SESSION['mail_select']);
if ($count == 0 ) { // All Record of List
$sql=$_SESSION['mail_sql'];
if ($_SESSION['mail_where'] <> ''){
$sql.= " and dateEmail is null";
} else {
$sql.= " where dateEmail is null";
}
$resql=db_query($sql,$conn);
while($row=db_fetch_array($resql))
{
$select_record[] = $row['idemail_contact'];
}
$_SESSION['mail_select'] = $select_record;
}
// field of Config
$GMAIL_Username = $_SESSION['config'][array_search('GMAIL_Username', array_column($_SESSION['config'], 'name'))][value];
$GMAIL_Password = $_SESSION['config'][array_search('GMAIL_Password', array_column($_SESSION['config'], 'name'))][value];
$GMAIL_setFrom_email = $_SESSION['config'][array_search('GMAIL_setFrom_email', array_column($_SESSION['config'], 'name'))][value];$GMAIL_Username = $_SESSION['config'][array_search('GMAIL_Username', array_column($_SESSION['config'], 'name'))][value];
$GMAIL_setFrom_name = $_SESSION['config'][array_search('GMAIL_setFrom_name', array_column($_SESSION['config'], 'name'))][value];
$EDITOR_UrlBase0 = $_SESSION['config'][array_search('EDITOR_UrlBase0', array_column($_SESSION['config'], 'name'))][value];
$EDITOR_BaseVirtual0 = $_SESSION['config'][array_search('EDITOR_BaseVirtual0', array_column($_SESSION['config'], 'name'))][value];
//Import PHPMailer classes into the global namespace
use PHPMailer\PHPMailer\PHPMailer;
use PHPMailer\PHPMailer\SMTP;
require __DIR__.'/../phpmailer_6.1/autoload.php';
//Create a new PHPMailer instance
$mail = new PHPMailer;
// Activo condificacción utf-8
$mail->CharSet = 'UTF-8';
//Tell PHPMailer to use SMTP
$mail->isSMTP();
//Enable SMTP debugging
// SMTP::DEBUG_OFF = off (for production use)
// SMTP::DEBUG_CLIENT = client messages
// SMTP::DEBUG_SERVER = client and server messages
$mail->SMTPDebug = SMTP::DEBUG_OFF;
//Set the hostname of the mail server
$mail->Host = 'smtp.gmail.com';
// use
// $mail->Host = gethostbyname('smtp.gmail.com');
// if your network does not support SMTP over IPv6
//Set the SMTP port number - 587 for authenticated TLS, a.k.a. RFC4409 SMTP submission
$mail->Port = 587;
//Set the encryption mechanism to use - STARTTLS or SMTPS
$mail->SMTPSecure = PHPMailer::ENCRYPTION_STARTTLS;
//Whether to use SMTP authentication
$mail->SMTPAuth = true;
//Username to use for SMTP authentication - use full email address for gmail
$mail->Username = $GMAIL_Username;
//Password to use for SMTP authentication
$mail->Password = $GMAIL_Password;
//Set who the message is to be sent from
$mail->setFrom($GMAIL_setFrom_email,$GMAIL_setFrom_name);
//Set an alternative reply-to address
// $mail->addReplyTo('fernandohumanes@gmail.com', 'First Last');
//Set the subject line
$mail->Subject = $masterData['subject'];
//Read an HTML message body from an external file, convert referenced images to embedded,
//convert HTML into a basic plain-text alternative body
$message = str_replace('"'.$EDITOR_BaseVirtual0,'"'.$EDITOR_UrlBase0,$masterData['message']);
// $mail->msgHTML($message);
// Attachments
$filesArray = my_json_decode($masterData['attachments']);
foreach ($filesArray as $File) {
$mail->addAttachment(__DIR__."/../".$File["name"],$File["usrName"],'base64', $File["type"]);
}
$count_send = 0;
foreach ($_SESSION['mail_select'] as &$contact) {
$sql = "SELECT contact.name, contact.surname, email_contact.email FROM contact join email_contact
on (contact.contact_id = email_contact.contact_contact_id and email_contact.idemail_contact = $contact)";
$resql=db_query($sql,$conn);
while($row=db_fetch_array($resql)) {
$count_send = $count_send + 1;
// Message customization
$message0 = str_replace('{name}',$row['name'],$message);
$message0 = str_replace('{surname}',$row['surname'],$message0);
$message0 = str_replace('{email}',$row['email'],$message0);
$mail->msgHTML($message0);
// $mail->AltBody = $mail->html2text($message0); // Text plain alternative
$mail->addAddress($row['email'], $row['name'].' '.$row['surname']);
$status = $mail->send();
// Updating the email sending record
$sql2 = "update email_contact set dateEmail = now(), Status = '$status' where idemail_contact = $contact";
$resql2=db_query($sql2,$conn);
}
}
// delete variables of session
unset($_SESSION['mail_select']);
unset($_SESSION['mail_where']);
?>
Gmail account settings

For Gmail to allow us to connect, it is necessary to configure the "Access of unsafe applications".
For any questions or explanations, contact me via email [email="fernandohumanes@gmail.com"]fernandohumanes@gmail.com[/email]
In my blog I leave you all the sources, so you can download them and adapt them to your computers.