dolibarr/htdocs/core/class/html.formmail.class.php
Laurent Destailleur dc3f3e15e8 Merge
2026-06-12 19:25:23 +02:00

2295 lines
91 KiB
PHP

<?php
/* Copyright (C) 2005-2012 Laurent Destailleur <eldy@users.sourceforge.net>
* Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
* Copyright (C) 2010-2011 Juanjo Menent <jmenent@2byte.es>
* Copyright (C) 2015-2017 Marcos García <marcosgdf@gmail.com>
* Copyright (C) 2015-2017 Nicolas ZABOURI <info@inovea-conseil.com>
* Copyright (C) 2018-2025 Frédéric France <frederic.france@free.fr>
* Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
* Copyright (C) 2023 Anthony Berton <anthony.berton@bb2a.fr>
* Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
*
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* \file htdocs/core/class/html.formmail.class.php
* \ingroup core
* \brief File of class to generate HTML form to send email of unitary email.
*/
require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/class/cemailtemplate.class.php'; // So the class ModelMail that was defined into this file in old version is still available when including this file
/**
* Class to manage a HTML form to send a unitary email
* Usage: $formail = new FormMail($db)
* $formmail->proprietes=1 or string or array of values
* $formmail->show_form() show the form
*/
class FormMail extends Form
{
/**
* @var DoliDB Database handler.
*/
public $db;
/**
* @var int 1 = Include HTML form tag and show submit button
* 0 = Do not include form tag and submit button
* -1 = Do not include form tag but include submit button
*/
public $withform;
/**
* @var string name from
*/
public $fromname;
/**
* @var string email from
*/
public $frommail;
/**
* @var string user, company, robot
*/
public $fromtype;
/**
* @var int from ID
*/
public $fromid;
/**
* @var int Add also the robot email as possible senders
*/
public $fromalsorobot;
/**
* @var string thirdparty etc
*/
public $totype;
/**
* @var int ID
*/
public $toid;
/**
* @var string Reply-to name
*/
public $replytoname;
/**
* @var string Reply-to email
*/
public $replytomail;
/**
* @var string To name
*/
public $toname;
/**
* @var string To email
*/
public $tomail;
/**
* @var string Track id
*/
public $trackid;
/**
* @var string If you know a MSGID of an email and want to send the email in reply to it. Will be added into header as In-Reply-To: <...>
*/
public $inreplyto;
/**
* @var int<0,1>
*/
public $withsubstit; // Show substitution array
/**
* @var int<0,1>
*/
public $withfrom;
/**
* @var int<0,1>|string[]
*/
public $withto; // Show recipient emails
/**
* @var int<0,1>
*/
public $withreplyto;
/**
* @var int<0,1>|string 0 = Do not Show free text for recipient emails
* 1 = Show free text for recipient emails
* or a free email
*/
public $withtofree;
/**
* @var int<0,1>|string[]
*/
public $withtocc;
/**
* @var int<0,1>|string|string[] When 1|'1', enable BCC field, when not 0, use as default BCC email
*/
public $withtoccc;
/**
* @var int<0,1>|string
*/
public $withtopic;
/**
* @var int<0,1>
*/
public $witherrorsto;
/**
* @var int<0,2>|string 0=No attaches files, 1=Show attached files, 2=Can add new attached files, 'text'=Show attached files and the text
*/
public $withfile;
/**
* @var string Use case string to a button "Fill with layout" for this use case. Example 'wesitepage', 'emailing', 'email', ...
*/
public $withlayout;
/**
* @var string 'text' or 'html' to add a button "Fill with AI generation"
*/
public $withaiprompt;
/**
* @var int<-1,1> 1=Add a checkbox "Attach also main document" for mass actions (checked by default), -1=Add checkbox (not checked by default)
*/
public $withmaindocfile;
/**
* @var int<0,1>|string
*/
public $withbody;
/**
* @var int<0,1>
*/
public $withfromreadonly;
/**
* @var int<0,1>
*/
public $withreplytoreadonly;
/**
* @var int<0,1>
*/
public $withtoreadonly;
/**
* @var int<0,1>
*/
public $withtoccreadonly;
/**
* @var int<0,1>
*/
public $witherrorstoreadonly;
/**
* @var int<0,1>
*/
public $withtocccreadonly;
/**
* @var int<0,1>
*/
public $withtopicreadonly;
/**
* @var int<0,1>
*/
public $withbodyreadonly;
/**
* @var int<0,1>
*/
public $withfilereadonly;
/**
* @var int<0,1>
*/
public $withdeliveryreceipt;
/**
* @var int<0,1>
*/
public $withcancel;
/**
* @var int<0,1>
*/
public $withdeliveryreceiptreadonly;
/**
* @var int<-1,1>
*/
public $withfckeditor;
/**
* @var string ckeditortoolbar
*/
public $ckeditortoolbar;
/**
* @var array<string,string>
*/
public $substit = array();
/**
* @var array<int,array<string,string>>
*/
public $substit_lines = array();
/**
* @var array{}|array{models:string,langsmodels?:string,fileinit?:string[],returnurl:string}
*/
public $param = array();
/**
* @var string[]
*/
public $withtouser = array();
/**
* @var string[]
*/
public $withtoccuser = array();
/**
* @var CEmailTemplate[]
*/
public $lines_model;
/**
* @var int<-1,1> -1 suggests the checkbox 'one email per recipient' not checked, 0 = no suggestion, 1 = suggest and checked
*/
public $withoptiononeemailperrecipient;
/**
* Constructor
*
* @param DoliDB $db Database handler
*/
public function __construct($db)
{
$this->db = $db;
$this->withform = 1;
$this->withfrom = 1;
$this->withto = 1;
$this->withtofree = 1;
$this->withtocc = 1;
$this->withtoccc = '0';
$this->witherrorsto = 0;
$this->withtopic = 1;
$this->withfile = 0; // 1=Add section "Attached files". 2=Can add files.
$this->withmaindocfile = 0; // 1=Add a checkbox "Attach also main document" for mass actions (checked by default), -1=Add checkbox (not checked by default)
$this->withbody = 1;
$this->withfromreadonly = 1;
$this->withreplytoreadonly = 1;
$this->withtoreadonly = 0;
$this->withtoccreadonly = 0;
$this->withtocccreadonly = 0;
$this->witherrorstoreadonly = 0;
$this->withtopicreadonly = 0;
$this->withfilereadonly = 0;
$this->withbodyreadonly = 0;
$this->withdeliveryreceiptreadonly = 0;
$this->withfckeditor = -1; // -1 = Auto
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Clear list of attached files in send mail form (also stored in session)
*
* @return void
*/
public function clear_attached_files()
{
// phpcs:enable
global $conf, $user;
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
// Set tmp user directory
$vardir = $conf->user->dir_output."/".$user->id;
$upload_dir = $vardir.'/temp/'; // TODO Add $keytoavoidconflict in upload_dir path
if (is_dir($upload_dir)) {
dol_delete_dir_recursive($upload_dir);
}
$keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
unset($_SESSION["listofpaths".$keytoavoidconflict]);
unset($_SESSION["listofnames".$keytoavoidconflict]);
unset($_SESSION["listofmimes".$keytoavoidconflict]);
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Add a file into the list of attached files (stored in SECTION array)
*
* @param string $path Full absolute path on filesystem of file, including file name
* @param string $file Only filename (can be basename($path))
* @param string $type Mime type (can be dol_mimetype($file))
* @return void
*/
public function add_attached_files($path, $file = '', $type = '')
{
// phpcs:enable
$listofpaths = array();
$listofnames = array();
$listofmimes = array();
if (empty($file)) {
$file = basename($path);
}
if (empty($type)) {
$type = dol_mimetype($file);
}
$keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
$listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
}
if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
$listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
}
if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
$listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
}
if (!in_array($file, $listofnames)) {
$listofpaths[] = $path;
$listofnames[] = $file;
$listofmimes[] = $type;
$_SESSION["listofpaths".$keytoavoidconflict] = implode(';', $listofpaths);
$_SESSION["listofnames".$keytoavoidconflict] = implode(';', $listofnames);
$_SESSION["listofmimes".$keytoavoidconflict] = implode(';', $listofmimes);
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Remove a file from the list of attached files (stored in SECTION array)
*
* @param int $keytodelete Key index in file array (0, 1, 2, ...)
* @return void
*/
public function remove_attached_files($keytodelete)
{
// phpcs:enable
$listofpaths = array();
$listofnames = array();
$listofmimes = array();
$keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
$listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
}
if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
$listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
}
if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
$listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
}
if ($keytodelete >= 0) {
unset($listofpaths[$keytodelete]);
unset($listofnames[$keytodelete]);
unset($listofmimes[$keytodelete]);
$_SESSION["listofpaths".$keytoavoidconflict] = implode(';', $listofpaths);
$_SESSION["listofnames".$keytoavoidconflict] = implode(';', $listofnames);
$_SESSION["listofmimes".$keytoavoidconflict] = implode(';', $listofmimes);
//var_dump($_SESSION['listofpaths']);
}
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Return list of attached files (stored in SECTION array)
*
* @return array{paths:string[],names:string[],mimes:string[]}
*/
public function get_attached_files()
{
// phpcs:enable
$listofpaths = array();
$listofnames = array();
$listofmimes = array();
$keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
$listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
}
if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
$listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
}
if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
$listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
}
return array('paths' => $listofpaths, 'names' => $listofnames, 'mimes' => $listofmimes);
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Show the form to input an email
* this->withfile: 0=No attaches files, 1=Show attached files, 2=Can add new attached files
* this->withmaindocfile
*
* @param string $addfileaction Name of action when posting file attachments
* @param string $removefileaction Name of action when removing file attachments
* @return void
* @deprecated
*/
public function show_form($addfileaction = 'addfile', $removefileaction = 'removefile')
{
// phpcs:enable
print $this->get_form($addfileaction, $removefileaction);
}
// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
/**
* Get the form to input an email
* this->withfile: 0=No attaches files, 1=Show attached files, 2=Can add new attached files
* this->param: Contains more parameters like email templates info
* this->withfckeditor: 1=We use an advanced editor, so we switch content into HTML
*
* @param string $addfileaction Name of action when posting file attachments
* @param string $removefileaction Name of action when removing file attachments
* @return string Form to show
*/
public function get_form($addfileaction = 'addfile', $removefileaction = 'removefile')
{
// phpcs:enable
global $conf, $langs, $user, $hookmanager, $form;
if (!is_object($form)) {
$form = new Form($this->db);
}
// Required to show editor assistants
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
$formfile = new FormFile($this->db);
require_once DOL_DOCUMENT_ROOT.'/core/class/html.formai.class.php';
$formai = new FormAI($this->db);
// Load translation files required by the page
$langs->loadLangs(array('other', 'mails', 'members'));
// Clear temp files. Must be done before call of triggers, at beginning (mode = init), or when we select a new template
if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelselected') && GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1')) {
$this->clear_attached_files();
}
// Call hook getFormMail
$hookmanager->initHooks(array('formmail'));
$parameters = array(
'addfileaction' => $addfileaction,
'removefileaction' => $removefileaction,
'trackid' => $this->trackid
);
$reshook = $hookmanager->executeHooks('getFormMail', $parameters, $this);
if (!empty($reshook)) {
return $hookmanager->resPrint;
} else {
$out = '';
$disablebademails = 1;
// Define output language
$outputlangs = $langs;
$newlang = '';
if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($this->param['langsmodels'])) {
$newlang = $this->param['langsmodels'];
}
if (!empty($newlang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($newlang);
$outputlangs->load('other');
}
// Get message template for $this->param["models"] into c_email_templates
$arraydefaultmessage = -1;
if ($this->param['models'] != 'none') {
$model_id = 0;
if (array_key_exists('models_id', $this->param)) {
$model_id = $this->param["models_id"];
}
$arraydefaultmessage = $this->getEMailTemplate($this->db, $this->param["models"], $user, $outputlangs, $model_id, 1, '', ($model_id > 0 ? -1 : 1)); // If $model_id is empty, preselect the first one
}
// Define list of attached files
$listofpaths = array();
$listofnames = array();
$listofmimes = array();
$keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelselected') && GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1')) {
if (!empty($arraydefaultmessage->joinfiles) && !empty($this->param['fileinit']) && is_array($this->param['fileinit'])) {
foreach ($this->param['fileinit'] as $path) {
if (!empty($path)) {
$this->add_attached_files($path);
}
}
}
}
if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
$listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
}
if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
$listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
}
if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
$listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
}
$out .= "\n".'<!-- Begin form mail type='.$this->param["models"].' --><div id="mailformdiv"></div>'."\n";
if ($this->withform == 1) {
$out .= '<form method="POST" name="mailform" id="mailform" enctype="multipart/form-data" action="'.$this->param["returnurl"].'#formmail">'."\n";
$out .= '<a id="formmail" name="formmail"></a>';
$out .= '<input style="display:none" type="submit" id="sendmailhidden" name="sendmail">';
$out .= '<input type="hidden" name="token" value="'.newToken().'" />';
$out .= '<input type="hidden" name="page_y" value="" />';
$out .= '<input type="hidden" name="trackid" value="'.$this->trackid.'" />';
$out .= '<input type="hidden" name="inreplyto" value="'.$this->inreplyto.'" />';
}
if (!empty($this->withfrom)) {
if (!empty($this->withfromreadonly)) {
$out .= '<input type="hidden" id="fromname" name="fromname" value="'.$this->fromname.'" />';
$out .= '<input type="hidden" id="frommail" name="frommail" value="'.$this->frommail.'" />';
}
}
foreach ($this->param as $key => $value) {
if (is_array($value)) {
$out .= "<!-- param key=".$key." is array, we do not output input field for it -->\n";
} else {
$out .= '<input type="hidden" id="'.$key.'" name="'.$key.'" value="'.$value.'" />'."\n";
}
}
$modelmail_array = array();
$break = '';
if ($this->param['models'] != 'none') {
$result = $this->fetchAllEMailTemplate($this->param["models"], $user, $outputlangs);
if ($result < 0) {
setEventMessages($this->error, $this->errors, 'errors');
}
foreach ($this->lines_model as $line) {
$reg = array();
if (preg_match('/\((.*)\)/', $line->label, $reg)) {
$labeltouse = $langs->trans($reg[1]); // langs->trans when label is __(xxx)__
} else {
$labeltouse = $line->label;
}
if ($break != $line->lang) {
// New break for a new language, we add the break
$s = $line->lang;
$shtml = '----- '.$langs->trans("Language_".$line->lang).' -----';
$modelmail_array['separator_'.$line->lang] = array('label' => $s, 'data-html' => $shtml, 'disabled' => 'disabled');
}
// We escape the $labeltouse to store it into $modelmail_array.
$s = dol_escape_htmltag($labeltouse);
$shtml = dol_escape_htmltag($labeltouse);
if ($line->lang) {
$shtml = picto_from_langcode($line->lang).'</span> '.$shtml;
}
if ($line->private) {
$shtml .= ' - <span class="opacitymedium small">'.dol_escape_htmltag($langs->trans("Private")).'</span>';
}
$modelmail_array[$line->id] = array('label' => $s, 'data-html' => $shtml);
}
}
// Zone to select email template
if (count($modelmail_array) > 0) {
$model_mail_selected_id = GETPOSTISSET('modelmailselected') ? GETPOSTINT('modelmailselected') : ($arraydefaultmessage->id > 0 ? $arraydefaultmessage->id : 0);
// If list of template is filled
$out .= '<div class="center" style="padding: 0px 0 12px 0">'."\n";
$out .= $this->selectarray('modelmailselected', $modelmail_array, $model_mail_selected_id, $langs->trans('SelectMailModel'), 0, 0, '', 0, 0, 0, '', 'minwidth150', 1, '', 0, 1);
if ($user->admin) {
$out .= info_admin($langs->trans("YouCanChangeValuesForThisListFrom", $langs->transnoentitiesnoconv('Setup').' - '.$langs->transnoentitiesnoconv('EMails')), 1);
}
// Language selector for predefined message templates (only when multilang is enabled)
if (getDolGlobalInt('MAIN_MULTILANGS')) {
// This feature is in conflict with the existing one where all templates are show with the language in a flag so user
// can choose the template in the correct language.To avoid duplicate and conflict selection, we currently enable this on a hidden constant.
// A solution to be compatible would be to wait the user has selected the template, and the combo to select language is shown if no language is forced for the template.
if (getDolGlobalInt('MAIN_MULTILANGS_ASK_LANG_IN_SEPARATE_COMBO')) {
include_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php';
$formadmin = new FormAdmin($this->db);
$currentlang = (is_object($outputlangs) ? $outputlangs->defaultlang : $langs->defaultlang);
$out .= ' &nbsp; ';
$out .= $formadmin->select_language($currentlang, 'lang_id', 0, array(), 1, 0, 0, 'maxwidth150');
}
}
$out .= '<input type="submit" class="button reposition smallpaddingimp" value="'.$langs->trans('Apply').'" name="modelselected" id="modelselected">';
$out .= ' &nbsp; ';
$out .= '</div>';
} elseif (!empty($this->param['models']) && in_array($this->param['models'], array(
'propal_send', 'order_send', 'facture_send',
'shipping_send', 'reception_send', 'fichinter_send', 'supplier_proposal_send', 'order_supplier_send',
'invoice_supplier_send', 'supplier_payment_send', 'thirdparty', 'contract', 'user', 'recruitmentcandidature_send', 'product_send', 'all'
))) {
// If list of template is empty
$out .= '<div class="center" style="padding: 0px 0 12px 0">'."\n";
$out .= '<span class="opacitymedium">'.$langs->trans('SelectMailModel').':</span> ';
$out .= '<select name="modelmailselected" disabled="disabled"><option value="none">'.$langs->trans("NoTemplateDefined").'</option></select>'; // Do not put 'disabled' on 'option' tag, it is already on 'select' and it makes chrome crazy.
if ($user->admin) {
$out .= info_admin($langs->trans("YouCanChangeValuesForThisListFrom", $langs->transnoentitiesnoconv('Setup').' - '.$langs->transnoentitiesnoconv('EMails')), 1);
}
$out .= ' &nbsp; ';
$out .= '<input type="submit" class="button reposition smallpaddingimp" value="'.$langs->trans('Apply').'" name="modelselected" disabled="disabled" id="modelselected">';
$out .= ' &nbsp; ';
$out .= '</div>';
} else {
$out .= '<!-- No template available for $this->param["models"] = '.$this->param['models'].' -->';
}
$out .= '<table class="tableforemailform boxtablenotop centpercent">'."\n";
// Substitution array/string
$helpforsubstitution = '';
if (is_array($this->substit) && count($this->substit)) {
$helpforsubstitution .= $langs->trans('AvailableVariables').' :<br><br><span class="small">'."\n";
foreach ($this->substit as $key => $val) {
// Do not show deprecated variables into the tooltip help of substitution variables
if (in_array($key, array('__NEWREF__', '__REFCLIENT__', '__REFSUPPLIER__', '__SUPPLIER_ORDER_DATE_DELIVERY__', '__SUPPLIER_ORDER_DELAY_DELIVERY__'))) {
continue;
}
if (is_array($val)) {
$val = implode(', ', $val);
} // key __MULTICURRENCY_CODE__ is an array and crashes dolGetFirstLineOfText function which accept only text
$helpforsubstitution .= $key.' -> '.$langs->trans(dol_string_nohtmltag(dolGetFirstLineOfText((string) $val))).'<br>';
}
$helpforsubstitution .= '</span>';
}
/*
if (!empty($this->withsubstit)) { // Unset or set ->withsubstit=0 to disable this.
$out .= '<tr><td colspan="2" class="right">';
if (is_numeric($this->withsubstit)) {
$out .= $form->textwithpicto($langs->trans("EMailTestSubstitutionReplacedByGenericValues"), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltip'); // Old usage
} else {
$out .= $form->textwithpicto($langs->trans('AvailableVariables'), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltip'); // New usage
}
$out .= "</td></tr>\n";
}*/
// From
if (!empty($this->withfrom)) {
if (!empty($this->withfromreadonly)) {
$out .= '<tr><td class="fieldrequired minwidth200">'.$langs->trans("MailFrom").'</td><td>';
// $this->fromtype is the default value to use to select sender
if (!($this->fromtype === 'user' && $this->fromid > 0)
&& !($this->fromtype === 'company')
&& !($this->fromtype === 'robot')
&& !preg_match('/user_aliases/', $this->fromtype)
&& !preg_match('/global_aliases/', $this->fromtype)
&& !preg_match('/senderprofile/', $this->fromtype)
) {
// Use this->fromname and this->frommail or error if not defined
$out .= $this->fromname;
if ($this->frommail) {
$out .= ' &lt;'.$this->frommail.'&gt;';
} else {
if ($this->fromtype) {
$langs->load('errors');
$out .= '<span class="warning"> &lt;'.$langs->trans('ErrorNoMailDefinedForThisUser').'&gt; </span>';
}
}
} else {
$liste = array();
// Add user email
if (empty($user->email)) {
$langs->load('errors');
$s = $user->getFullName($langs).' &lt;'.$langs->trans('ErrorNoMailDefinedForThisUser').'&gt;';
} else {
$s = $user->getFullName($langs).' &lt;'.$user->email.'&gt;';
}
$liste['user'] = array('label' => $s, 'data-html' => $s);
// Add also company main email
if (getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
$s = getDolGlobalString('MAIN_INFO_SOCIETE_NOM', getDolGlobalString('MAIN_INFO_SOCIETE_EMAIL')).' &lt;' . getDolGlobalString('MAIN_INFO_SOCIETE_MAIL').'&gt;';
$liste['company'] = array('label' => $s, 'data-html' => $s);
}
// Add also email aliases if there is some
$listaliases = array(
'global_aliases' => getDolGlobalString('MAIN_INFO_SOCIETE_MAIL_ALIASES'),
);
if (!empty($arraydefaultmessage->email_from) && !empty($arraydefaultmessage->id)) {
$templatemailfrom = ' &lt;'.$arraydefaultmessage->email_from.'&gt;';
$liste['from_template_'.((int) $arraydefaultmessage->id)] = array('label' => $templatemailfrom, 'data-html' => $templatemailfrom);
}
// Also add robot email
if (!empty($this->fromalsorobot)) {
if (getDolGlobalString('MAIN_MAIL_EMAIL_FROM') && getDolGlobalString('MAIN_MAIL_EMAIL_FROM') != getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
$s = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
if ($this->frommail) {
$s .= ' &lt;' . getDolGlobalString('MAIN_MAIL_EMAIL_FROM').'&gt;';
}
$liste['main_from'] = array('label' => $s, 'data-html' => $s);
}
}
// Add also email aliases from the c_email_senderprofile table
$sql = "SELECT rowid, label, email FROM ".$this->db->prefix()."c_email_senderprofile";
$sql .= " WHERE active = 1 AND (private = 0 OR private = ".((int) $user->id).") AND entity IN (".getEntity('c_email_senderprofile').")";
$sql .= " ORDER BY position";
$resql = $this->db->query($sql);
if ($resql) {
$num = $this->db->num_rows($resql);
$i = 0;
while ($i < $num) {
$obj = $this->db->fetch_object($resql);
if ($obj) {
$listaliases['senderprofile_'.$obj->rowid] = $obj->label.' <'.$obj->email.'>';
}
$i++;
}
} else {
dol_print_error($this->db);
}
foreach ($listaliases as $typealias => $listalias) {
$posalias = 0;
$listaliasarray = explode(',', $listalias);
foreach ($listaliasarray as $listaliasval) {
$posalias++;
$listaliasval = trim($listaliasval);
if ($listaliasval) {
$listaliasval = preg_replace('/</', '&lt;', $listaliasval);
$listaliasval = preg_replace('/>/', '&gt;', $listaliasval);
if (!preg_match('/&lt;/', $listaliasval)) {
$listaliasval = '&lt;'.$listaliasval.'&gt;';
}
$liste[$typealias.'_'.$posalias] = array('label' => $listaliasval, 'data-html' => $listaliasval);
}
}
}
// Using ajaxcombo here make the '<email>' no more visible on list because <emailofuser> is not a valid html tag,
// so we transform before each record into $liste to be printable with ajaxcombo by replacing <> into ()
// $liste['senderprofile_0_0'] = array('label'=>'rrr', 'data-html'=>'rrr &lt;aaaa&gt;');
foreach ($liste as $key => $val) {
if (!empty($liste[$key]['data-html'])) {
$liste[$key]['data-html'] = str_replace(array('&lt;', '<', '&gt;', '>'), array('__LTCHAR__', '__LTCHAR__', '__GTCHAR__', '__GTCHAR__'), $liste[$key]['data-html']);
$liste[$key]['data-html'] = str_replace(array('__LTCHAR__', '__GTCHAR__'), array('<span class="opacitymedium">(', ')</span>'), $liste[$key]['data-html']);
}
}
$out .= ' '.$form->selectarray('fromtype', $liste, (empty($arraydefaultmessage->email_from) || empty($arraydefaultmessage->id)) ? $this->fromtype : 'from_template_'.((int) $arraydefaultmessage->id), 0, 0, 0, '', 0, 0, 0, '', 'fromforsendingprofile maxwidth200onsmartphone', 1, '', $disablebademails);
}
$out .= "</td></tr>\n";
} else {
$out .= '<tr><td class="fieldrequired width200">'.$langs->trans("MailFrom")."</td><td>";
$out .= $langs->trans("Name").':<input type="text" id="fromname" name="fromname" class="maxwidth200onsmartphone" value="'.$this->fromname.'" />';
$out .= '&nbsp; &nbsp; ';
$out .= $langs->trans("EMail").':&lt;<input type="text" id="frommail" name="frommail" class="maxwidth200onsmartphone" value="'.$this->frommail.'" />&gt;';
$out .= "</td></tr>\n";
}
}
// To
if (!empty($this->withto) || is_array($this->withto)) {
$out .= $this->getHtmlForTo();
}
// To User
if (!empty($this->withtouser) && is_array($this->withtouser) && getDolGlobalString('MAIN_MAIL_ENABLED_USER_DEST_SELECT')) {
$out .= '<tr><td>';
$out .= $langs->trans("MailToUsers");
$out .= '</td><td>';
// multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time
$tmparray = $this->withtouser;
foreach ($tmparray as $key => $val) {
$tmparray[$key] = dol_htmlentities($tmparray[$key], 0, 'UTF-8', true);
}
$withtoselected = GETPOST("receiveruser", 'array'); // Array of selected value
if (empty($withtoselected) && count($tmparray) == 1 && GETPOST('action', 'aZ09') == 'presend') {
$withtoselected = array_keys($tmparray);
}
$out .= $form->multiselectarray("receiveruser", $tmparray, $withtoselected, 0, 0, 'inline-block minwidth500', 0, "");
$out .= "</td></tr>\n";
}
// With option for one email per recipient
if (!empty($this->withoptiononeemailperrecipient)) {
if (abs($this->withoptiononeemailperrecipient) == 1) {
$out .= '<tr><td class="minwidth200">';
$out .= $langs->trans("GroupEmails");
$out .= '</td><td>';
$out .= ' <input type="checkbox" id="oneemailperrecipient" value="1" name="oneemailperrecipient"'.($this->withoptiononeemailperrecipient > 0 ? ' checked="checked"' : '').'> ';
$out .= '<label for="oneemailperrecipient">';
$out .= $form->textwithpicto($langs->trans("OneEmailPerRecipient"), $langs->trans("WarningIfYouCheckOneRecipientPerEmail"), 1, 'help');
$out .= '</label>';
//$out .= '<span class="hideonsmartphone opacitymedium">';
//$out .= ' - ';
//$out .= $langs->trans("WarningIfYouCheckOneRecipientPerEmail");
//$out .= '</span>';
if (getDolGlobalString('MASS_ACTION_EMAIL_ON_DIFFERENT_THIRPARTIES_ADD_CUSTOM_EMAIL')) {
if (!empty($this->withto) && !is_array($this->withto)) {
$out .= ' <span class="opacitymedium">'.$langs->trans("or").'</span> <input type="email" name="emailto" value="">';
}
}
$out .= '</td></tr>';
} else {
$out .= '<tr><td><input type="hidden" name="oneemailperrecipient" value="1"></td><td></td></tr>';
}
}
// CC
if (!empty($this->withtocc) || is_array($this->withtocc)) {
$out .= $this->getHtmlForCc();
}
// To User cc
if (!empty($this->withtoccuser) && is_array($this->withtoccuser) && getDolGlobalString('MAIN_MAIL_ENABLED_USER_DEST_SELECT')) {
$out .= '<tr><td>';
$out .= $langs->trans("MailToCCUsers");
$out .= '</td><td>';
// multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time
$tmparray = $this->withtoccuser;
foreach ($tmparray as $key => $val) {
$tmparray[$key] = dol_htmlentities($tmparray[$key], 0, 'UTF-8', true);
}
$withtoselected = GETPOST("receiverccuser", 'array'); // Array of selected value
if (empty($withtoselected) && count($tmparray) == 1 && GETPOST('action', 'aZ09') == 'presend') {
$withtoselected = array_keys($tmparray);
}
$out .= $form->multiselectarray("receiverccuser", $tmparray, $withtoselected, 0, 0, 'inline-block minwidth500', 0, "");
$out .= "</td></tr>\n";
}
// CCC
if (!empty($this->withtoccc) || is_array($this->withtoccc)) {
$out .= $this->getHtmlForWithCcc();
}
// Replyto
if (!empty($this->withreplyto)) {
if ($this->withreplytoreadonly) {
$out .= '<input type="hidden" id="replyname" name="replyname" value="'.$this->replytoname.'" />';
$out .= '<input type="hidden" id="replymail" name="replymail" value="'.$this->replytomail.'" />';
$out .= "<tr><td>".$langs->trans("MailReply")."</td><td>".$this->replytoname.($this->replytomail ? (" &lt;".$this->replytomail."&gt;") : "");
$out .= "</td></tr>\n";
}
}
// Errorsto
if (!empty($this->witherrorsto)) {
$out .= $this->getHtmlForWithErrorsTo();
}
// Ask delivery receipt
if (!empty($this->withdeliveryreceipt) && getDolGlobalInt('MAIN_EMAIL_SUPPORT_ACK')) {
$out .= $this->getHtmlForDeliveryreceipt();
}
// Topic
if (!empty($this->withtopic)) {
$out .= $this->getHtmlForTopic($arraydefaultmessage, $helpforsubstitution);
}
// Attached files
if (!empty($this->withfile)) {
$out .= '<tr>';
$out .= '<td class="tdtop">'.$langs->trans("MailFile").'</td>';
$out .= '<td>';
if ($this->withmaindocfile) {
// withmaindocfile is set to 1 or -1 to show the checkbox (-1 = checked or 1 = not checked)
if (GETPOSTISSET('sendmail')) {
$this->withmaindocfile = (GETPOST('addmaindocfile', 'alpha') ? -1 : 1);
} elseif (is_object($arraydefaultmessage) && $arraydefaultmessage->id > 0) {
// If a template was selected, we use setup of template to define if join file checkbox is selected or not.
$this->withmaindocfile = ($arraydefaultmessage->joinfiles ? -1 : 1);
}
}
if (!empty($this->withmaindocfile)) {
if ($this->withmaindocfile == 1) {
$out .= '<input type="checkbox" id="addmaindocfile" name="addmaindocfile" value="1" />';
} elseif ($this->withmaindocfile == -1) {
$out .= '<input type="checkbox" id="addmaindocfile" name="addmaindocfile" value="1" checked="checked" />';
}
if (getDolGlobalString('MAIL_MASS_ACTION_ADD_LAST_IF_MAIN_DOC_NOT_FOUND')) {
$out .= ' <label for="addmaindocfile">'.$langs->trans("JoinMainDocOrLastGenerated").'.</label><br>';
} else {
$out .= ' <label for="addmaindocfile">'.$langs->trans("JoinMainDoc").'.</label><br>';
}
}
if (is_numeric($this->withfile)) {
// TODO Trick to have param removedfile containing nb of file to delete. But this does not works without javascript
$out .= '<input type="hidden" class="removedfilehidden" name="removedfile" value="">'."\n";
$out .= '<script nonce="'.getNonce().'" type="text/javascript">';
$out .= 'jQuery(document).ready(function () {';
$out .= ' jQuery(".removedfile").click(function() {';
$out .= ' jQuery(".removedfilehidden").val(jQuery(this).val());';
$out .= ' });';
$out .= '})';
$out .= '</script>'."\n";
if (count($listofpaths)) {
foreach ($listofpaths as $key => $val) {
$relativepathtofile = substr($val, (strlen(DOL_DATA_ROOT) - strlen($val)));
$entity = (isset($this->param['object_entity']) ? $this->param['object_entity'] : $conf->entity);
if ($entity > 1) {
$relativepathtofile = str_replace('/'.$entity.'/', '/', $relativepathtofile);
}
// Try to extract data from full path
$formfile_params = array();
preg_match('#^(/)(\w+)(/)(.+)$#', $relativepathtofile, $formfile_params);
$out .= '<div id="attachfile_'.$key.'">';
// Preview of attachment
$out .= img_mime($listofnames[$key]).$listofnames[$key];
$out .= ' '.$formfile->showPreview(array('fullname' => $val,'name' => basename($val)), $formfile_params[2], $formfile_params[4], 0, ($entity == 1 ? '' : 'entity='.((int) $entity)));
if (!$this->withfilereadonly) {
$out .= ' <input type="image" style="border: 0px;" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/delete.png" value="'.($key + 1).'" class="removedfile input-nobottom" id="removedfile_'.$key.'" name="removedfile_'.$key.'" />';
//$out.= ' <a href="'.$_SERVER["PHP_SELF"].'?removedfile='.($key+1).'&id=removedfile_'.$key.'">'.img_delete($langs->trans("Remove"), 'id="removedfile_'.$key.'" name="removedfile_'.$key.'"', 'removedfile input-nobottom').'</a>';
}
$out .= '<br></div>';
}
} /*elseif (empty($this->withmaindocfile)) {
//$out .= '<span class="opacitymedium">'.$langs->trans("NoAttachedFiles").'</span><br>';
}*/
if ($this->withfile == 2) {
$maxfilesizearray = getMaxFileSizeArray();
$maxmin = $maxfilesizearray['maxmin'];
if ($maxmin > 0) {
$out .= '<input type="hidden" name="MAX_FILE_SIZE" value="'.($maxmin * 1024).'">'; // MAX_FILE_SIZE must precede the field type=file
}
// Can add other files
if (!getDolGlobalString('FROM_MAIL_DONT_USE_INPUT_FILE_MULTIPLE')) {
$out .= '<input type="file" class="flat" id="addedfile" name="addedfile[]" value="'.$langs->trans("Upload").'" multiple />';
} else {
$out .= '<input type="file" class="flat" id="addedfile" name="addedfile" value="'.$langs->trans("Upload").'" />';
}
$out .= ' ';
$out .= '<input type="submit" class="button smallpaddingimp" id="'.$addfileaction.'" name="'.$addfileaction.'" value="'.$langs->trans("MailingAddFile").'" />';
}
} else {
$out .= $this->withfile;
}
$out .= "</td></tr>\n";
}
// Message (+ Links to choose layout or ai prompt)
if (!empty($this->withbody)) {
$defaultmessage = GETPOST('message', 'restricthtml');
if (!GETPOST('modelselected', 'alpha') || GETPOST('modelmailselected') != '-1') {
if ($arraydefaultmessage && $arraydefaultmessage->content) {
$defaultmessage = (string) $arraydefaultmessage->content;
} elseif (!is_numeric($this->withbody)) {
$defaultmessage = $this->withbody;
}
}
// Complete substitution array with the url to make online payment
$paymenturl = '';
// Set the online payment url link into __ONLINE_PAYMENT_URL__ key
require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
$validpaymentmethod = getValidOnlinePaymentMethods('');
if (empty($this->substit['__REF__'])) { // @phan-suppress-current-line PhanTypeMismatchProperty
$paymenturl = '';
} else {
$langs->loadLangs(array('paypal', 'other'));
$typeforonlinepayment = 'free';
if ($this->param["models"] == 'order' || $this->param["models"] == 'order_send') {
$typeforonlinepayment = 'order'; // TODO use detection on something else than template
}
if ($this->param["models"] == 'invoice' || $this->param["models"] == 'facture_send') {
$typeforonlinepayment = 'invoice'; // TODO use detection on something else than template
}
if ($this->param["models"] == 'member') {
$typeforonlinepayment = 'member'; // TODO use detection on something else than template
}
$url = getOnlinePaymentUrl(0, $typeforonlinepayment, $this->substit['__REF__']);
$paymenturl = $url;
}
if (count($validpaymentmethod) > 0 && $paymenturl) {
$langs->load('other');
$this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'] = str_replace('\n', "\n", $langs->transnoentities("PredefinedMailContentLink", $paymenturl));
$this->substit['__ONLINE_PAYMENT_URL__'] = $paymenturl;
} elseif (count($validpaymentmethod) > 0) {
$this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'] = '__ONLINE_PAYMENT_TEXT_AND_URL__';
$this->substit['__ONLINE_PAYMENT_URL__'] = '__ONLINE_PAYMENT_URL__';
} else {
$this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'] = '';
$this->substit['__ONLINE_PAYMENT_URL__'] = '';
}
$this->substit['__ONLINE_INTERVIEW_SCHEDULER_TEXT_AND_URL__'] = '';
// Generate the string with the template for lines repeated and filled for each line
$lines = '';
$defaultlines = $arraydefaultmessage->content_lines;
if (isset($defaultlines)) {
foreach ($this->substit_lines as $lineid => $substit_line) {
$lines .= make_substitutions($defaultlines, $substit_line, $outputlangs)."\n";
}
}
$this->substit['__LINES__'] = $lines;
$defaultmessage = str_replace('\n', "\n", $defaultmessage);
// Deal with format differences between message and some substitution variables (text / HTML)
$atleastonecomponentishtml = 0;
if (strpos($defaultmessage, '__USER_SIGNATURE__') !== false && dol_textishtml($this->substit['__USER_SIGNATURE__'])) {
$atleastonecomponentishtml++;
}
if (strpos($defaultmessage, '__SENDEREMAIL_SIGNATURE__') !== false && dol_textishtml($this->substit['__SENDEREMAIL_SIGNATURE__'])) {
$atleastonecomponentishtml++;
}
if (strpos($defaultmessage, '__ONLINE_PAYMENT_TEXT_AND_URL__') !== false && dol_textishtml($this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'])) {
$atleastonecomponentishtml++;
}
if (strpos($defaultmessage, '__ONLINE_INTERVIEW_SCHEDULER_TEXT_AND_URL__') !== false && dol_textishtml($this->substit['__ONLINE_INTERVIEW_SCHEDULER_TEXT_AND_URL__'])) {
$atleastonecomponentishtml++;
}
if (dol_textishtml($defaultmessage)) {
$atleastonecomponentishtml++;
}
if ($atleastonecomponentishtml) {
if (!dol_textishtml($this->substit['__USER_SIGNATURE__'])) {
$this->substit['__USER_SIGNATURE__'] = dol_nl2br($this->substit['__USER_SIGNATURE__']);
}
if (!dol_textishtml($this->substit['__SENDEREMAIL_SIGNATURE__'])) {
$this->substit['__SENDEREMAIL_SIGNATURE__'] = dol_nl2br($this->substit['__SENDEREMAIL_SIGNATURE__']);
}
if (!dol_textishtml($this->substit['__LINES__'])) {
$this->substit['__LINES__'] = dol_nl2br($this->substit['__LINES__']);
}
if (!dol_textishtml($this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'])) {
$this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'] = dol_nl2br($this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__']);
}
if (!dol_textishtml($defaultmessage)) {
$defaultmessage = dol_nl2br($defaultmessage);
}
}
if (GETPOSTISSET("message") && !GETPOST('modelselected')) {
$defaultmessage = GETPOST("message", "restricthtml");
} else {
// Pass $outputlangs so __(TranslationKey)__ in the template body is resolved
// in the language of the selected email template, not the operator's language
// (see issue #34540).
$defaultmessage = make_substitutions($defaultmessage, $this->substit, $outputlangs);
// Clean first \n and br (to avoid empty line when CONTACTCIVNAME is empty)
$defaultmessage = preg_replace("/^(<br>)+/", "", $defaultmessage);
$defaultmessage = preg_replace("/^\n+/", "", $defaultmessage);
}
$out .= '<!-- Message line from get_form -->';
$out .= '<tr>';
$out .= '<td class="tdtop">';
$out .= $form->textwithpicto($langs->trans('MailText'), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltipfrombody');
$out .= '</td>';
$out .= '<td class="tdtop">';
$formmail = $this;
$showlinktolayout = ($formmail->withfckeditor && getDolGlobalInt('MAIN_EMAIL_USE_LAYOUT')) ? $formmail->withlayout : '';
$showlinktolayoutlabel = $langs->trans("FillMessageWithALayout");
$showlinktoai = ($formmail->withaiprompt && isModEnabled('ai')) ? 'textgenerationemail' : '';
$showlinktoailabel = $langs->trans("AIEnhancements");
$formatforouput = '';
$htmlname = 'message';
$formai->substit = $this->substit;
$formai->substit_lines = $this->substit_lines;
// Fill $out
$db = $this->db;
include DOL_DOCUMENT_ROOT.'/core/tpl/formlayoutai.tpl.php';
$out .= '</td>';
$out .= '</tr>';
$out .= '<tr>';
$out .= '<td colspan="2">';
if ($this->withbodyreadonly) {
$out .= nl2br($defaultmessage);
$out .= '<input type="hidden" id="message" name="message" disabled value="'.$defaultmessage.'" />';
} else {
if (!isset($this->ckeditortoolbar)) {
$this->ckeditortoolbar = 'dolibarr_mailings';
}
// Editor wysiwyg
require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
if ($this->withfckeditor == -1) {
if (getDolGlobalString('FCKEDITOR_ENABLE_MAIL')) {
$this->withfckeditor = 1;
} else {
$this->withfckeditor = 0;
}
}
$uselocalbrowser = getDolGlobalBool('FCKEDITOR_ENABLE_IMAGE_UPLOAD');
// $uselocalbrowser = true;
$doleditor = new DolEditor('message', $defaultmessage, '', 280, $this->ckeditortoolbar, 'In', true, $uselocalbrowser, $this->withfckeditor, 8, '95%');
$out .= $doleditor->Create(1);
}
$out .= "</td></tr>\n";
}
$out .= '</table>'."\n";
if ($this->withform == 1 || $this->withform == -1) {
$out .= '<div class="center">';
$out .= '<input type="submit" class="button button-add" id="sendmail" name="sendmail" value="'.$langs->trans("SendMail").'"';
// Add a javascript test to avoid to forget to submit file before sending email
if ($this->withfile == 2 && $conf->use_javascript_ajax) {
$out .= ' onClick="if (document.mailform.addedfile.value != \'\') { alert(\''.dol_escape_js($langs->trans("FileWasNotUploaded")).'\'); return false; } else { return true; }"';
}
$out .= ' />';
if ($this->withcancel) {
$out .= '<input class="button button-cancel" type="submit" id="cancel" name="cancel" value="'.$langs->trans("Cancel").'" />';
}
$out .= '</div>'."\n";
}
if ($this->withform == 1) {
$out .= '</form>'."\n";
}
// Disable enter key if option MAIN_MAILFORM_DISABLE_ENTERKEY is set
if (getDolGlobalString('MAIN_MAILFORM_DISABLE_ENTERKEY')) {
$out .= '<script nonce="'.getNonce().'" type="text/javascript">';
$out .= 'jQuery(document).ready(function () {';
$out .= ' $(document).on("keypress", \'#mailform\', function (e) { /* Note this is called at every key pressed ! */
var code = e.keyCode || e.which;
if (code == 13) {
console.log("Enter was intercepted and blocked");
e.preventDefault();
return false;
}
});';
$out .= ' })';
$out .= '</script>';
}
$out .= "<!-- End form mail -->\n";
return $out;
}
}
/**
* get html For To
*
* @return string html
*/
public function getHtmlForTo()
{
global $langs, $form;
$out = '<tr><td class="fieldrequired">';
if ($this->withtofree) {
$out .= $form->textwithpicto($langs->trans("MailTo"), $langs->trans("YouCanUseCommaSeparatorForSeveralRecipients"));
} else {
$out .= $langs->trans("MailTo");
}
$out .= '</td><td>';
if ($this->withtoreadonly) {
if (!empty($this->toname) && !empty($this->tomail)) {
$out .= '<input type="hidden" id="toname" name="toname" value="'.$this->toname.'" />';
$out .= '<input type="hidden" id="tomail" name="tomail" value="'.$this->tomail.'" />';
if ($this->totype == 'thirdparty') {
$soc = new Societe($this->db);
$soc->fetch($this->toid);
$out .= $soc->getNomUrl(1);
} elseif ($this->totype == 'contact') {
$contact = new Contact($this->db);
$contact->fetch($this->toid);
$out .= $contact->getNomUrl(1);
} else {
$out .= $this->toname;
}
$out .= ' &lt;'.$this->tomail.'&gt;';
if ($this->withtofree) {
$out .= '<br>'.$langs->trans("and").' <input class="minwidth200" id="sendto" name="sendto" spellcheck="false" value="'.(!is_array($this->withto) && !is_numeric($this->withto) ? (GETPOSTISSET("sendto") ? GETPOST("sendto") : $this->withto) : "").'" />';
}
} else {
// Note withto may be a text like 'AllRecipientSelected'
$out .= (!is_array($this->withto) && !is_numeric($this->withto)) ? $this->withto : "";
}
} else {
// The free input of email
if (!empty($this->withtofree)) {
$out .= '<input class="minwidth200" id="sendto" name="sendto" spellcheck="false" value="'.(($this->withtofree && !is_numeric($this->withtofree)) ? $this->withtofree : (!is_array($this->withto) && !is_numeric($this->withto) ? (GETPOSTISSET("sendto") ? GETPOST("sendto") : $this->withto) : "")).'" />';
}
// The select combo
if (!empty($this->withto) && is_array($this->withto)) {
if (!empty($this->withtofree)) {
$out .= ' <span class="opacitymedium">'.$langs->trans("and")."/".$langs->trans("or")."</span> ";
}
$tmparray = $this->withto;
foreach ($tmparray as $key => $val) {
if (is_array($val)) {
$label = $val['label'];
} else {
$label = $val;
}
$tmparray[$key] = array();
$tmparray[$key]['id'] = $key;
$tmparray[$key]['label'] = $label;
$tmparray[$key]['label'] = str_replace(array('<', '>'), array('(', ')'), $tmparray[$key]['label']);
// multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time
$tmparray[$key]['label'] = dol_htmlentities($tmparray[$key]['label'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', true);
$tmparray[$key]['labelhtml'] = $label;
$tmparray[$key]['labelhtml'] = str_replace(array('&lt;', '<', '&gt;', '>'), array('__LTCHAR__', '__LTCHAR__', '__GTCHAR__', '__GTCHAR__'), $tmparray[$key]['labelhtml']);
$tmparray[$key]['labelhtml'] = str_replace(array('__LTCHAR__', '__GTCHAR__'), array('<span class="opacitymedium">(', ')</span>'), $tmparray[$key]['labelhtml']);
}
$withtoselected = GETPOST("receiver", 'array'); // Array of selected value
if (!getDolGlobalInt('MAIN_MAIL_NO_WITH_TO_SELECTED')) {
if (empty($withtoselected) && count($tmparray) == 1 && GETPOST('action', 'aZ09') == 'presend') {
$withtoselected = array_keys($tmparray);
}
}
$out .= $form->multiselectarray("receiver", $tmparray, $withtoselected, 0, 0, 'inline-block minwidth500', 0, 0);
}
}
$out .= "</td></tr>\n";
return $out;
}
/**
* get html For CC
*
* @return string html
*/
public function getHtmlForCc()
{
global $langs, $form;
$out = '<tr><td>';
$out .= $form->textwithpicto($langs->trans("MailCC"), $langs->trans("YouCanUseCommaSeparatorForSeveralRecipients"));
$out .= '</td><td>';
if ($this->withtoccreadonly) {
$out .= (!is_array($this->withtocc) && !is_numeric($this->withtocc)) ? $this->withtocc : "";
} else {
$out .= '<input class="minwidth200" id="sendtocc" name="sendtocc" value="'.(GETPOST("sendtocc", "alpha") ? GETPOST("sendtocc", "alpha") : ((!is_array($this->withtocc) && !is_numeric($this->withtocc)) ? $this->withtocc : '')).'" />';
if (!empty($this->withtocc) && is_array($this->withtocc)) {
$out .= ' <span class="opacitymedium">'.$langs->trans("and")."/".$langs->trans("or")."</span> ";
$tmparray = $this->withtocc;
foreach ($tmparray as $key => $val) {
if (is_array($val)) {
$label = $val['label'];
} else {
$label = $val;
}
$tmparray[$key] = array();
$tmparray[$key]['id'] = $key;
$tmparray[$key]['label'] = $label;
$tmparray[$key]['label'] = str_replace(array('<', '>'), array('(', ')'), $tmparray[$key]['label']);
// multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time
$tmparray[$key]['label'] = dol_htmlentities($tmparray[$key]['label'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', true);
$tmparray[$key]['labelhtml'] = $label;
$tmparray[$key]['labelhtml'] = str_replace(array('&lt;', '<', '&gt;', '>'), array('__LTCHAR__', '__LTCHAR__', '__GTCHAR__', '__GTCHAR__'), $tmparray[$key]['labelhtml']);
$tmparray[$key]['labelhtml'] = str_replace(array('__LTCHAR__', '__GTCHAR__'), array('<span class="opacitymedium">(', ')</span>'), $tmparray[$key]['labelhtml']);
}
$withtoccselected = GETPOST("receivercc", 'array'); // Array of selected value
$out .= $form->multiselectarray("receivercc", $tmparray, $withtoccselected, 0, 0, 'inline-block minwidth500', 0, 0);
}
}
$out .= "</td></tr>\n";
return $out;
}
/**
* get html For WithCCC
* This information is show when MAIN_EMAIL_USECCC is set.
*
* @return string html
*/
public function getHtmlForWithCcc()
{
global $langs, $form;
$out = '<tr><td>';
$out .= $form->textwithpicto($langs->trans("MailCCC"), $langs->trans("YouCanUseCommaSeparatorForSeveralRecipients"));
$out .= '</td><td>';
if (!empty($this->withtocccreadonly)) {
$out .= (!is_array($this->withtoccc) && !is_numeric($this->withtoccc)) ? $this->withtoccc : "";
} else {
$out .= '<input class="minwidth200" id="sendtoccc" name="sendtoccc" value="'.(GETPOSTISSET("sendtoccc") ? GETPOST("sendtoccc", "alpha") : ((!is_array($this->withtoccc) && !is_numeric($this->withtoccc)) ? $this->withtoccc : '')).'" />';
if (!empty($this->withtoccc) && is_array($this->withtoccc)) {
$out .= ' <span class="opacitymedium">'.$langs->trans("and")."/".$langs->trans("or")."</span> ";
$tmparray = $this->withtoccc;
foreach ($tmparray as $key => $val) {
if (is_array($val)) {
$label = $val['label'];
} else {
$label = $val;
}
$tmparray[$key] = array();
$tmparray[$key]['id'] = $key;
$tmparray[$key]['label'] = $label;
$tmparray[$key]['label'] = str_replace(array('<', '>'), array('(', ')'), $tmparray[$key]['label']);
// multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time
$tmparray[$key]['label'] = dol_htmlentities($tmparray[$key]['label'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', true);
$tmparray[$key]['labelhtml'] = $label;
$tmparray[$key]['labelhtml'] = str_replace(array('&lt;', '<', '&gt;', '>'), array('__LTCHAR__', '__LTCHAR__', '__GTCHAR__', '__GTCHAR__'), $tmparray[$key]['labelhtml']);
$tmparray[$key]['labelhtml'] = str_replace(array('__LTCHAR__', '__GTCHAR__'), array('<span class="opacitymedium">(', ')</span>'), $tmparray[$key]['labelhtml']);
}
$withtocccselected = GETPOST("receiverccc", 'array'); // Array of selected value
$out .= $form->multiselectarray("receiverccc", $tmparray, $withtocccselected, 0, 0, 'inline-block minwidth500', 0, 0);
}
}
$showinfobcc = '';
if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_PROPOSAL_TO') && !empty($this->param['models']) && $this->param['models'] == 'propal_send') {
$showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_PROPOSAL_TO');
}
if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_ORDER_TO') && !empty($this->param['models']) && $this->param['models'] == 'order_send') {
$showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_ORDER_TO');
}
if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_INVOICE_TO') && !empty($this->param['models']) && $this->param['models'] == 'facture_send') {
$showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_INVOICE_TO');
}
if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_PROPOSAL_TO') && !empty($this->param['models']) && $this->param['models'] == 'supplier_proposal_send') {
$showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_PROPOSAL_TO');
}
if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_ORDER_TO') && !empty($this->param['models']) && $this->param['models'] == 'order_supplier_send') {
$showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_ORDER_TO');
}
if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_INVOICE_TO') && !empty($this->param['models']) && $this->param['models'] == 'invoice_supplier_send') {
$showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_INVOICE_TO');
}
if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_PROJECT_TO') && !empty($this->param['models']) && $this->param['models'] == 'project') { // don't know why there is not '_send' at end of this models name.
$showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_PROJECT_TO');
}
if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SHIPMENT_TO') && !empty($this->param['models']) && $this->param['models'] == 'shipping_send') {
$showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SHIPMENT_TO');
}
if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_RECEPTION_TO') && !empty($this->param['models']) && $this->param['models'] == 'reception_send') {
$showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_RECEPTION_TO');
}
if ($showinfobcc) {
$out .= ' + '.$showinfobcc;
}
$out .= "</td></tr>\n";
return $out;
}
/**
* get Html For WithErrorsTo
*
* @return string html
*/
public function getHtmlForWithErrorsTo()
{
global $langs;
//if (! $this->errorstomail) $this->errorstomail=$this->frommail;
$errorstomail = getDolGlobalString('MAIN_MAIL_ERRORS_TO', (!empty($this->errorstomail) ? $this->errorstomail : ''));
if ($this->witherrorstoreadonly) {
$out = '<tr><td>'.$langs->trans("MailErrorsTo").'</td><td>';
$out .= '<input type="hidden" id="errorstomail" name="errorstomail" value="'.$errorstomail.'" />';
$out .= $errorstomail;
$out .= "</td></tr>\n";
} else {
$out = '<tr><td>'.$langs->trans("MailErrorsTo").'</td><td>';
$out .= '<input class="minwidth200" id="errorstomail" name="errorstomail" value="'.$errorstomail.'" />';
$out .= "</td></tr>\n";
}
return $out;
}
/**
* get Html For Asking for Delivery Receipt
*
* @return string html
*/
public function getHtmlForDeliveryreceipt()
{
global $langs;
$out = '<tr><td><label for="deliveryreceipt">'.$langs->trans("DeliveryReceipt").'</label></td><td>';
if (!empty($this->withdeliveryreceiptreadonly)) {
$out .= yn($this->withdeliveryreceipt);
} else {
$defaultvaluefordeliveryreceipt = 0;
if (getDolGlobalString('MAIL_FORCE_DELIVERY_RECEIPT_PROPAL') && !empty($this->param['models']) && $this->param['models'] == 'propal_send') {
$defaultvaluefordeliveryreceipt = 1;
}
if (getDolGlobalString('MAIL_FORCE_DELIVERY_RECEIPT_SUPPLIER_PROPOSAL') && !empty($this->param['models']) && $this->param['models'] == 'supplier_proposal_send') {
$defaultvaluefordeliveryreceipt = 1;
}
if (getDolGlobalString('MAIL_FORCE_DELIVERY_RECEIPT_ORDER') && !empty($this->param['models']) && $this->param['models'] == 'order_send') {
$defaultvaluefordeliveryreceipt = 1;
}
if (getDolGlobalString('MAIL_FORCE_DELIVERY_RECEIPT_INVOICE') && !empty($this->param['models']) && $this->param['models'] == 'facture_send') {
$defaultvaluefordeliveryreceipt = 1;
}
if (getDolGlobalString('MAIL_FORCE_DELIVERY_RECEIPT_SUPPLIER_ORDER') && !empty($this->param['models']) && $this->param['models'] == 'order_supplier_send') {
$defaultvaluefordeliveryreceipt = 1;
}
//$out .= $form->selectyesno('deliveryreceipt', (GETPOSTISSET("deliveryreceipt") ? GETPOST("deliveryreceipt") : $defaultvaluefordeliveryreceipt), 1);
$out .= '<input type="checkbox" id="deliveryreceipt" name="deliveryreceipt" value="1"'.((GETPOSTISSET("deliveryreceipt") ? GETPOST("deliveryreceipt") : $defaultvaluefordeliveryreceipt) ? ' checked="checked"' : '').'>';
}
$out .= "</td></tr>\n";
return $out;
}
/**
* Return Html section for the Topic of message
*
* @param CEmailTemplate $arraydefaultmessage Array with message template content
* @param string $helpforsubstitution Help string for substitution
* @return string Text for topic
*/
public function getHtmlForTopic($arraydefaultmessage, $helpforsubstitution)
{
global $conf, $langs, $form;
$defaulttopic = GETPOST('subject', 'restricthtml');
if (!GETPOST('modelselected', 'alpha') || GETPOST('modelmailselected') != '-1') {
if ($arraydefaultmessage && $arraydefaultmessage->topic) {
$defaulttopic = $arraydefaultmessage->topic;
} elseif (!is_numeric($this->withtopic)) {
$defaulttopic = $this->withtopic;
}
}
// Resolve __(TranslationKey)__ in the language of the selected template
// (see issue #34540). Falls back to the caller's language when the template
// has no explicit language pinned.
$outputlangs = $langs;
if (is_object($arraydefaultmessage) && !empty($arraydefaultmessage->lang)) {
$outputlangs = new Translate("", $conf);
$outputlangs->setDefaultLang($arraydefaultmessage->lang);
$outputlangs->load('other');
}
$defaulttopic = make_substitutions($defaulttopic, $this->substit, $outputlangs);
$out = '<tr>';
$out .= '<td class="fieldrequired">';
$out .= $form->textwithpicto($langs->trans('MailTopicShort'), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltipfromtopic');
$out .= '</td>';
$out .= '<td>';
if ($this->withtopicreadonly) {
$out .= $defaulttopic;
$out .= '<input type="hidden" class="quatrevingtpercent" id="subject" name="subject" value="'.$defaulttopic.'" spellcheck="false">';
} else {
$out .= '<input type="text" class="quatrevingtpercent" id="subject" name="subject" value="'.((GETPOSTISSET("subject") && !GETPOST('modelselected')) ? GETPOST("subject") : ($defaulttopic ? $defaulttopic : '')).'" spellcheck="false">';
}
$out .= "</td></tr>\n";
return $out;
}
/**
* Return HTML code for selection of email layout
*
* @param string $htmlContent HTML name of WYSIWYG field to fill once layout has been chosen
* @param string $showlinktolayout Show link to layout
* @return string HTML for model email boxes
* @see getContentPageTemplate()
*/
public function getEmailLayoutSelector($htmlContent = 'message', $showlinktolayout = 'email')
{
global $conf, $db, $websitepage, $langs;
require_once DOL_DOCUMENT_ROOT.'/core/lib/emaillayout.lib.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
require_once DOL_DOCUMENT_ROOT.'/website/class/website.class.php';
require_once DOL_DOCUMENT_ROOT.'/website/class/websitepage.class.php';
$out = '<div id="template-selector" class="template-selector email-layout-container hidden" style="display:none;">';
$out .= '<div>';
// Define list of email layouts to use
$layouts = array(
'none' => 'None',
);
// Add layouts found on disk in install/doctemplates/maillayout directory
$arrayoflayoutemplates = dol_dir_list(DOL_DOCUMENT_ROOT.'/install/doctemplates/maillayout/', 'files', 0, '\.html$');
foreach ($arrayoflayoutemplates as $layouttemplatefile) {
$layoutname = preg_replace('/\.html$/i', '', $layouttemplatefile['name']);
// Exclude some layouts for some use cases
if ($layoutname == 'news' && (!in_array($showlinktolayout, array('emailing', 'websitepage')) || !isModEnabled('website'))) {
continue;
}
if ($layoutname == 'product' && (!in_array($showlinktolayout, array('emailing', 'websitepage')) || (!isModEnabled('product') && !isModEnabled('service')))) {
continue;
}
$layouts[$layoutname] = ucfirst($layoutname);
}
//}
// TODO Add a hook to allow to complete the list
foreach ($layouts as $layout => $templateFunction) {
$contentHtml = getHtmlOfLayout($layout);
$out .= '<div class="template-option" data-template="'.$layout.'" data-content="'.htmlentities($contentHtml).'">';
$out .= '<img class="maillayout" alt="'.$layout.'" src="'.DOL_URL_ROOT.'/theme/common/maillayout/'.$layout.'.png" />';
$out .= '<span class="template-option-text">'.$langs->trans($templateFunction).'</span>';
$out .= '</div>';
}
$out .= '</div>';
// Prepare the array for multiselect
// Fetch blogs
$blogArray = array();
if (isModEnabled('website')) {
$websitepage = new WebsitePage($this->db);
$arrayofblogs = $websitepage->fetchAll('', 'ASC,DESC', 'fk_website,date_creation', 0, 0, array('type_container' => 'blogpost'));
if (empty($conf->cache['websiteurl'])) {
$conf->cache['websiteurl'] = array();
}
if (!empty($arrayofblogs)) {
foreach ($arrayofblogs as $blog) {
if (!isset($conf->cache['websiteurl'][$blog->id])) {
$tmpwebsite = new Website($db);
$tmpwebsite->fetch($blog->fk_website);
$conf->cache['websiteurl'][$blog->fk_website] = (empty($tmpwebsite->virtualhost) ? $tmpwebsite->ref : $tmpwebsite->virtualhost);
}
$labelwebsite = $conf->cache['websiteurl'][$blog->fk_website];
//$blog->fk_website
$blogArray[$blog->id] = array(
'id' => $blog->id,
'label' => '['.$labelwebsite.' '.$blog->type_container.' '.$blog->id.'] '.dol_trunc($blog->title, 40),
'labelhtml' => '<span class="opacitymedium">['.$labelwebsite.' '.$blog->type_container.' '.$blog->id.']</span> '.dol_trunc($blog->title, 40),
);
}
}
}
// Fetch Product / Services
/* to use with multiselectarray but consume too much memory so replaced
if (in_array('product', array_keys($layouts))) {
$productArray = array();
if (isModEnabled('product') || isModEnabled('service')) {
include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
$form = new Form($this->db);
$arrayofproduct = $form->select_produits_list(0, 'product-select', '', 0, 0, '', 1, 2, 1);
if (!empty($arrayofproduct)) {
foreach ($arrayofproduct as $product) {
$productArray[$product["key"]] = array(
'id' => $product["key"],
'label' => $product["value"].' - '.dol_trunc($product["label2"], 40),
'labelhtml' => $product["value"].' - '.dol_trunc($product["label2"], 40),
);
}
}
}
}
*/
// Use the multiselect array function to create the dropdown
if (in_array('news', array_keys($layouts)) && (isModEnabled('product') || isModEnabled('service'))) {
$out .= '<div id="post-dropdown-container" class="email-layout-container hidden" style="margin-top: 8px; display:none;">';
$out .= '<label for="blogpost-select">Select Posts: </label>';
$out .= '<!-- select component for selection of blog posts -->'."\n";
// TODO WARNING: multiselectarray is ok only for very small list
$out .= self::multiselectarray('blogpost-select', $blogArray, array(), 0, 0, 'minwidth200 select-template');
$out .= ' <input type="submit" class="smallpaddingimp button reposition" name="submit" id="post-submit" value="'.dolPrintHTMLForAttribute($langs->trans("Select")).'">';
$out .= '</div>';
}
if (in_array('product', array_keys($layouts)) && (isModEnabled('product') || isModEnabled('service'))) {
include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
$form = new Form($this->db);
$out .= '<div id="product-dropdown-container" class="email-layout-container hidden" style="margin-top: 8px; display:none;">';
$out .= '<label for="product-select">'.img_picto('', 'product', 'class="pictofixedwidth"').$langs->trans("Product").' : </label>';
$out .= '<!-- select component for selection of product -->'."\n";
$out .= $form->select_produits(0, 'product-select', '', 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'inline-block valignmiddle', 0, '', null, 1);
// TODO multiselectarray is ok only for very small list but is ok for multiselect. We need a multiselect ok with ajax for long list
//$out .= self::multiselectarray('product-select', $productArray, array(), 0, 0, 'minwidth200 select-template');
$out .= ' <input type="submit" class="smallpaddingimp button reposition" name="submit" id="product-submit" value="'.dolPrintHTMLForAttribute($langs->trans("Select")).'">';
$out .= '</div>';
}
$out .= '</div>';
$out .= '<!-- Js code to manage choice of an email layout -->'."\n";
$out .= '<script type="text/javascript">
$(document).ready(function() {
$(".template-option").click(function() {
var template = $(this).data("template");
var subject = jQuery("#subject").val();
var fromtype = jQuery("#fromtype").val();
var sendto = jQuery("#sendto").val();
var sendtocc = jQuery("#sendtocc").val();
var sendtoccc = jQuery("#sendtoccc").val();
console.log("We choose a layout for email template=" + template + ", subject="+subject);
$(".template-option").removeClass("selected");
$(this).addClass("selected");
$(".select-template").val("").trigger("change");
if (template === "news") {
$("#post-dropdown-container").show();
$("#product-dropdown-container").hide();
console.log("Displaying dropdown for news selection");
} else if (template === "product") {
$("#product-dropdown-container").show();
$("#post-dropdown-container").hide();
console.log("Displaying dropdown for product selection");
} else {
$("#post-dropdown-container").hide();
$("#product-dropdown-container").hide();
}
var csrfToken = "' .newToken().'";
$.ajax({
type: "POST",
url: "'.DOL_URL_ROOT.'/core/ajax/mailtemplate.php",
data: {
token: csrfToken,
template: template,
subject: subject,
fromtype: fromtype,
sendto: sendto,
sendtocc: sendtocc,
sendtoccc: sendtoccc,
selectedPosts: "[]"
},
success: function(response) {
jQuery("#'.$htmlContent.'").val(response);
var editorInstance = CKEDITOR.instances["'.$htmlContent.'"];
if (editorInstance) {
editorInstance.setData(response);
}
},
error: function(xhr, status, error) {
console.error("An error occurred: " + xhr.responseText);
}
});
});
$("#blogpost-select").change(function() {
var selectedIds = $(this).val();
var contentHtml = $(".template-option.selected").data("content");
updateSelectedPostsContent(contentHtml, selectedIds);
});
$("#product-select").change(function() {
var selectedIds = $(this).val();
var contentHtml = $(".template-option.selected").data("content");
updateSelectedPostsContent(contentHtml, selectedIds);
});
function updateSelectedPostsContent(contentHtml, selectedIds) {
var csrfToken = "' .newToken().'";
template = $(".template-option.selected").data("template");
var subject = $("#subject").val();
$.ajax({
type: "POST",
url: "'.dol_buildpath('/core/ajax/mailtemplate.php', 1).'",
data: {
token: csrfToken,
template: template,
subject: subject,
selectedPosts: JSON.stringify(selectedIds)
},
success: function(response) {
jQuery("#'.$htmlContent.'").val(response);
var editorInstance = CKEDITOR.instances["'.$htmlContent.'"];
if (editorInstance) {
editorInstance.setData(response);
}
},
error: function(xhr, status, error) {
console.error("An error occurred: " + xhr.responseText);
}
});
}
});
</script>';
return $out;
}
/**
* Return templates of email with type = $type_template or type = 'all'.
* This search into table c_email_templates. Used by the get_form function.
*
* @param DoliDB $dbs Database handler
* @param string $type_template Get message for model/type=$type_template, type='all' also included.
* @param User $user Get templates public + limited to this user
* @param Translate $outputlangs Output lang object
* @param int $id Id of template to get, or
* -1 for first found with position 0, or
* 0 for first found whatever is position (priority order depends on lang provided or not) or
* -2 for exact match with label (no answer if not found)
* @param int $active 1=Only active template, 0=Only disabled, -1=All
* @param string $label Label of template to get
* @param int $defaultfortype 1=Only default templates, 0=Only not default, -1=All
* @return ModelMail|CEmailTemplate|int<-1,-1> One instance of CEmailTemplate or < 0 if error
*/
public function getEMailTemplate($dbs, $type_template, $user, $outputlangs, $id = 0, $active = 1, $label = '', $defaultfortype = -1)
{
global $conf;
if ($id == -2 && empty($label)) {
$this->error = 'LabelIsMandatoryWhenIdIs-2or-3';
return -1;
}
if ($type_template === 'societe') {
$type_template = 'thirdparty';
}
$ret = new CEmailTemplate($dbs);
$languagetosearch = (is_object($outputlangs) ? $outputlangs->defaultlang : '');
// Define $languagetosearchmain to fall back on main language (for example to get 'es_ES' for 'es_MX')
$tmparray = explode('_', $languagetosearch);
$languagetosearchmain = $tmparray[0].'_'.strtoupper($tmparray[0]);
if ($languagetosearchmain == $languagetosearch) {
$languagetosearchmain = '';
}
$sql = "SELECT rowid, entity, module, label, type_template, topic, email_from, joinfiles, content, content_lines, lang, email_from, email_to, email_tocc, email_tobcc";
$sql .= " FROM ".$dbs->prefix().'c_email_templates';
$sql .= " WHERE (type_template = '".$dbs->escape($type_template)."' OR type_template = '".$dbs->escape($type_template)."_send' OR type_template = 'all')";
$sql .= " AND entity IN (".getEntity('c_email_templates').")";
$sql .= " AND (private = 0 OR fk_user = ".((int) $user->id).")"; // Get all public or private owned
if ($active >= 0) {
$sql .= " AND active = ".((int) $active);
}
if ($defaultfortype >= 0) {
$sql .= " AND defaultfortype = ".((int) $defaultfortype);
}
if ($label) {
$sql .= " AND label = '".$dbs->escape($label)."'";
}
if (!($id > 0) && $languagetosearch) {
$sql .= " AND (lang = '".$dbs->escape($languagetosearch)."'".($languagetosearchmain ? " OR lang = '".$dbs->escape($languagetosearchmain)."'" : "")." OR lang IS NULL OR lang = '')";
}
if ($id > 0) {
$sql .= " AND rowid = ".(int) $id;
}
if ($id == -1) {
$sql .= " AND position = 0";
}
$sql .= " AND entity IN(".getEntity('c_email_templates', 1).")";
if ($languagetosearch) {
$sql .= $dbs->order("position,lang,label", "ASC,DESC,ASC"); // We want line with lang set first, then with lang null or ''
} else {
$sql .= $dbs->order("position,lang,label", "ASC,ASC,ASC"); // If no language provided, we give priority to lang not defined
}
//$sql .= $dbs->plimit(1);
//print $sql;
$resql = $dbs->query($sql);
if (!$resql) {
dol_print_error($dbs);
return -1;
}
// Get first found
while (1) {
$obj = $dbs->fetch_object($resql);
if ($obj) {
// If template is for a module, check module is enabled; if not, take next template
if ($obj->module) {
$tempmodulekey = $obj->module;
if (empty($conf->$tempmodulekey) || !isModEnabled($tempmodulekey)) {
continue;
}
}
// If a record was found
$ret->id = (int) $obj->rowid;
$ret->module = (string) $obj->module;
$ret->label = (string) $obj->label;
$ret->lang = $obj->lang;
$ret->topic = $obj->topic;
$ret->content = (string) $obj->content;
$ret->content_lines = (string) $obj->content_lines;
$ret->joinfiles = $obj->joinfiles;
$ret->email_from = (string) $obj->email_from;
$ret->email_tocc = (string) $obj->email_tocc;
$ret->email_tobcc = (string) $obj->email_tobcc;
break;
} else {
// If no record found
if ($id == -2) {
// Not found with the provided label
return -1;
} else {
// If there is no template at all
$defaultmessage = '';
if ($type_template == 'body') {
// Special case to use this->withbody as content
$defaultmessage = (string) $this->withbody;
} elseif ($type_template == 'facture_send' || $type_template == 'facture' || $type_template == 'facture_relance') {
$defaultmessage = $outputlangs->transnoentities("PredefinedMailContentSendInvoice");
} elseif ($type_template == 'propal_send' || $type_template == 'propal') {
$defaultmessage = $outputlangs->transnoentities("PredefinedMailContentSendProposal");
} elseif ($type_template == 'supplier_proposal_send' || $type_template == 'supplier_proposal') {
$defaultmessage = $outputlangs->transnoentities("PredefinedMailContentSendSupplierProposal");
} elseif ($type_template == 'order_send' || $type_template == 'order') {
$defaultmessage = $outputlangs->transnoentities("PredefinedMailContentSendOrder");
} elseif ($type_template == 'order_supplier_send' || $type_template == 'order_supplier') {
$defaultmessage = $outputlangs->transnoentities("PredefinedMailContentSendSupplierOrder");
} elseif ($type_template == 'invoice_supplier_send' || $type_template == 'invoice_supplier') {
$defaultmessage = $outputlangs->transnoentities("PredefinedMailContentSendSupplierInvoice");
} elseif ($type_template == 'shipping_send' || $type_template == 'shipping') {
$defaultmessage = $outputlangs->transnoentities("PredefinedMailContentSendShipping");
} elseif ($type_template == 'reception_send' || $type_template == 'reception') {
$defaultmessage = $outputlangs->transnoentities("PredefinedMailContentSendReception");
} elseif ($type_template == 'fichinter_send' || $type_template == 'fichinter') {
$defaultmessage = $outputlangs->transnoentities("PredefinedMailContentSendFichInter");
} elseif ($type_template == 'actioncomm_send' || $type_template == 'actioncomm') {
$defaultmessage = $outputlangs->transnoentities("PredefinedMailContentSendActionComm");
} elseif (!empty($type_template)) {
$defaultmessage = $outputlangs->transnoentities("PredefinedMailContentGeneric");
}
$ret->label = 'default';
$ret->lang = $outputlangs->defaultlang;
$ret->topic = '';
$ret->joinfiles = 1;
$ret->content = $defaultmessage;
$ret->content_lines = '';
break;
}
}
}
$dbs->free($resql);
return $ret;
}
/**
* Find if template exists
* Search into table c_email_templates
*
* @param string $type_template Get message for key module
* @param User $user Use template public or limited to this user
* @param Translate $outputlangs Output lang object
* @return int Return integer <0 if KO,
*/
public function isEMailTemplate($type_template, $user, $outputlangs)
{
$sql = "SELECT label, topic, content, lang";
$sql .= " FROM ".$this->db->prefix().'c_email_templates';
$sql .= " WHERE type_template='".$this->db->escape($type_template)."'";
$sql .= " AND entity IN (".getEntity('c_email_templates').")";
$sql .= " AND (fk_user is NULL or fk_user = 0 or fk_user = ".((int) $user->id).")";
if (is_object($outputlangs)) {
$sql .= " AND (lang = '".$this->db->escape($outputlangs->defaultlang)."' OR lang IS NULL OR lang = '')";
}
$sql .= $this->db->order("lang,label", "ASC");
//print $sql;
$resql = $this->db->query($sql);
if ($resql) {
$num = $this->db->num_rows($resql);
$this->db->free($resql);
return $num;
} else {
$this->error = get_class($this).' '.__METHOD__.' ERROR:'.$this->db->lasterror();
return -1;
}
}
/**
* Find if template exists and are available for current user, then set them into $this->lines_model.
* Search in table c_email_templates
*
* @param string $type_template Get message for key module
* @param User $user Use template public or limited to this user
* @param ?Translate $outputlangs Output lang object
* @param int<-1,1> $active 1=Only active template, 0=Only disabled, -1=All
* @return int<-1,max> Return integer <0 if KO, nb of records found if OK
*/
public function fetchAllEMailTemplate($type_template, $user, $outputlangs, $active = 1)
{
global $db, $conf;
$sql = "SELECT rowid, module, label, topic, content, content_lines, lang, fk_user, private, position";
$sql .= " FROM ".$this->db->prefix().'c_email_templates';
$sql .= " WHERE type_template IN ('".$this->db->escape($type_template)."', 'all')";
$sql .= " AND entity IN (".getEntity('c_email_templates').")";
$sql .= " AND (private = 0 OR fk_user = ".((int) $user->id).")"; // See all public templates or templates I own.
if ($active >= 0) {
$sql .= " AND active = ".((int) $active);
}
//if (is_object($outputlangs)) $sql.= " AND (lang = '".$this->db->escape($outputlangs->defaultlang)."' OR lang IS NULL OR lang = '')"; // Return all languages
$sql .= $this->db->order("lang,position,label", "ASC");
//print $sql;
$resql = $this->db->query($sql);
if ($resql) {
$num = $this->db->num_rows($resql);
$this->lines_model = array();
while ($obj = $this->db->fetch_object($resql)) {
// If template is for a module, check module is enabled.
if ($obj->module) {
$tempmodulekey = $obj->module;
if (empty($conf->$tempmodulekey) || !isModEnabled($tempmodulekey)) {
continue;
}
}
$line = new CEmailTemplate($db);
$line->id = (int) $obj->rowid;
$line->label = (string) $obj->label;
$line->lang = $obj->lang;
$line->fk_user = $obj->fk_user;
$line->private = $obj->private;
$line->position = $obj->position;
$line->topic = $obj->topic;
$line->content = $obj->content;
$line->content_lines = $obj->content_lines;
$this->lines_model[] = $line;
}
$this->db->free($resql);
return $num;
} else {
$this->error = get_class($this).' '.__METHOD__.' ERROR:'.$this->db->lasterror();
return -1;
}
}
/**
* Normalize a text for equality checks.
* @param string $value Raw text to normalize.
* @return string
*/
private static function normalizeTextForComparison($value)
{
$value = dol_string_nohtmltag((string) $value);
$value = preg_replace('/\s+/', ' ', $value);
return trim((string) $value);
}
/**
* Return language prefix from a language code.
* @param string $langcode Full language code (for example fr_FR).
* @return string
*/
private static function getLangPrefix($langcode)
{
$langcode = trim((string) $langcode);
if ($langcode === '') {
return '';
}
$prefix = preg_replace('/[_-].*$/', '', $langcode);
$prefix = strtolower((string) $prefix);
return preg_replace('/[^a-z]/', '', $prefix);
}
/**
* Pick best translated label/description from product multilangs.
*
* @param array<string,array{label?:string,description?:string,note?:string,other?:string}>|null $multilangs Product multilang rows indexed by language code.
* @param string $langcode Preferred language code.
* @return array{label:string,description:string}
*/
private static function getBestProductTranslation($multilangs, $langcode)
{
$langcode = trim((string) $langcode);
if ($langcode === '' || !is_array($multilangs) || empty($multilangs)) {
return array('label' => '', 'description' => '');
}
$prefix = self::getLangPrefix($langcode);
$candidates = array($langcode);
if ($prefix !== '' && $prefix !== $langcode) {
$candidates[] = $prefix;
}
foreach ($candidates as $candidate) {
if (empty($multilangs[$candidate]) || !is_array($multilangs[$candidate])) {
continue;
}
$label = trim((string) (isset($multilangs[$candidate]['label']) ? $multilangs[$candidate]['label'] : ''));
$description = trim((string) (isset($multilangs[$candidate]['description']) ? $multilangs[$candidate]['description'] : ''));
if ($label !== '' || $description !== '') {
return array('label' => $label, 'description' => $description);
}
}
if ($prefix !== '') {
foreach ($multilangs as $code => $row) {
if (!is_array($row)) {
continue;
}
if (!(strpos($code, $prefix.'_') === 0 || strpos($code, $prefix.'-') === 0)) {
continue;
}
$label = trim((string) (isset($row['label']) ? $row['label'] : ''));
$description = trim((string) (isset($row['description']) ? $row['description'] : ''));
if ($label !== '' || $description !== '') {
return array('label' => $label, 'description' => $description);
}
}
}
return array('label' => '', 'description' => '');
}
/**
* Set ->substit (and ->substit_line) array from object. This is call when suggesting the email template into forms before sending email.
* @param CommonObject $object Object to use
* @param Translate $outputlangs Object lang
* @return void
* @see getCommonSubstitutionArray()
*/
public function setSubstitFromObject($object, $outputlangs)
{
global $extrafields;
$parameters = array();
$tmparray = getCommonSubstitutionArray($outputlangs, 0, null, $object);
complete_substitutions_array($tmparray, $outputlangs, null, $parameters);
$this->substit = $tmparray;
$targetLang = '';
if (is_object($outputlangs) && !empty($outputlangs->defaultlang)) {
$targetLang = trim((string) $outputlangs->defaultlang);
}
// Fill substit_lines with each object lines content
if (is_array($object->lines)) {
foreach ($object->lines as $line) {
$substit_line = array(
'__PRODUCT_REF__' => isset($line->product_ref) ? $line->product_ref : '',
'__PRODUCT_LABEL__' => isset($line->product_label) ? $line->product_label : '',
'__PRODUCT_DESCRIPTION__' => isset($line->product_desc) ? $line->product_desc : '',
'__LABEL__' => isset($line->label) ? $line->label : '',
'__DESCRIPTION__' => isset($line->desc) ? $line->desc : '',
'__DATE_START_YMD__' => dol_print_date($line->date_start, 'day', false, $outputlangs),
'__DATE_END_YMD__' => dol_print_date($line->date_end, 'day', false, $outputlangs),
'__QUANTITY__' => $line->qty,
'__SUBPRICE__' => price($line->subprice),
'__AMOUNT__' => price($line->total_ttc),
'__AMOUNT_EXCL_TAX__' => price($line->total_ht)
);
// Create dynamic tags for __PRODUCT_EXTRAFIELD_FIELD__
if (!empty($line->fk_product)) {
if (!is_object($extrafields)) {
$extrafields = new ExtraFields($this->db);
}
$product = new Product($this->db);
$product->fetch($line->fk_product, '', '', '1');
$product->fetch_optionals();
if (getDolGlobalInt('MAIN_MULTILANGS') && $targetLang !== '' && !empty($product->multilangs) && is_array($product->multilangs)) {
$translated = self::getBestProductTranslation($product->multilangs, $targetLang);
$translatedLabel = trim((string) (isset($translated['label']) ? $translated['label'] : ''));
$translatedDescription = trim((string) (isset($translated['description']) ? $translated['description'] : ''));
$currentLabelNorm = self::normalizeTextForComparison($substit_line['__PRODUCT_LABEL__']);
$currentProductDescriptionNorm = self::normalizeTextForComparison($substit_line['__PRODUCT_DESCRIPTION__']);
$currentLineDescriptionNorm = self::normalizeTextForComparison($substit_line['__DESCRIPTION__']);
$productLabelNorm = self::normalizeTextForComparison($product->label);
$productDescriptionNorm = self::normalizeTextForComparison($product->description);
if ($translatedLabel !== '' && ($currentLabelNorm === '' || $currentLabelNorm === $productLabelNorm)) {
$substit_line['__PRODUCT_LABEL__'] = $translatedLabel;
}
if ($translatedDescription !== '' && ($currentProductDescriptionNorm === '' || $currentProductDescriptionNorm === $productDescriptionNorm)) {
$substit_line['__PRODUCT_DESCRIPTION__'] = $translatedDescription;
}
if ($translatedDescription !== '' && ($currentLineDescriptionNorm === '' || $currentLineDescriptionNorm === $productDescriptionNorm || $currentLineDescriptionNorm === $currentProductDescriptionNorm)) {
$substit_line['__DESCRIPTION__'] = $translatedDescription;
}
}
$extrafields->fetch_name_optionals_label($product->table_element, true);
if (!empty($extrafields->attributes[$product->table_element]['label']) && is_array($extrafields->attributes[$product->table_element]['label']) && count($extrafields->attributes[$product->table_element]['label']) > 0) {
foreach ($extrafields->attributes[$product->table_element]['label'] as $key => $label) {
$substit_line['__PRODUCT_EXTRAFIELD_'.strtoupper($key).'__'] = isset($product->array_options['options_'.$key]) ? $product->array_options['options_'.$key] : '';
}
}
}
$this->substit_lines[$line->id] = $substit_line; // @phan-suppress-current-line PhanTypeMismatchProperty
}
}
}
/**
* Get list of substitution keys available for emails. This is used for tooltips help.
* This include the complete_substitutions_array.
* @param string $mode 'formemail', 'formemailwithlines', 'formemailforlines', 'emailing', ...
* @param ?Object $object Object if applicable
* @return array<string,string> Array of substitution values for emails.
*/
public static function getAvailableSubstitKey($mode = 'formemail', $object = null)
{
global $langs;
$tmparray = array();
if ($mode == 'formemail' || $mode == 'formemailwithlines' || $mode == 'formemailforlines') {
$parameters = array('mode' => $mode);
$tmparray = getCommonSubstitutionArray($langs, 2, null, $object); // Note: On email template creation, this may be null because it is related to all type of objects
complete_substitutions_array($tmparray, $langs, null, $parameters);
if ($mode == 'formwithlines') {
$tmparray['__LINES__'] = '__LINES__'; // Will be set by the get_form function
}
if ($mode == 'formforlines') {
$tmparray['__QUANTITY__'] = '__QUANTITY__'; // Will be set by the get_form function
}
}
if ($mode == 'emailing') {
$parameters = array('mode' => $mode);
$tmparray = getCommonSubstitutionArray($langs, 2, array('object', 'objectamount'), $object); // Note: On email template creation, this may be null because it is related to all type of objects
complete_substitutions_array($tmparray, $langs, null, $parameters);
// For mass emailing, we have different keys specific to the data into tagerts list
$tmparray['__ID__'] = 'IdRecord';
$tmparray['__EMAIL__'] = 'EMailRecipient';
$tmparray['__LASTNAME__'] = 'Lastname';
$tmparray['__FIRSTNAME__'] = 'Firstname';
$tmparray['__MAILTOEMAIL__'] = 'TagMailtoEmail';
$tmparray['__OTHER1__'] = 'Other1';
$tmparray['__OTHER2__'] = 'Other2';
$tmparray['__OTHER3__'] = 'Other3';
$tmparray['__OTHER4__'] = 'Other4';
$tmparray['__OTHER5__'] = 'Other5';
$tmparray['__THIRDPARTY_CUSTOMER_CODE__'] = 'CustomerCode'; // If source is a thirdparty
$tmparray['__CHECK_READ__'] = $langs->trans('TagCheckMail');
$tmparray['__UNSUBSCRIBE__'] = $langs->trans('TagUnsubscribe');
$tmparray['__UNSUBSCRIBE_URL__'] = $langs->trans('TagUnsubscribe').' (URL)';
$onlinepaymentenabled = 0;
if (isModEnabled('paypal')) {
$onlinepaymentenabled++;
}
if (isModEnabled('stripe')) {
$onlinepaymentenabled++;
}
if ($onlinepaymentenabled && getDolGlobalString('PAYMENT_SECURITY_TOKEN')) {
$tmparray['__SECUREKEYPAYMENT__'] = getDolGlobalString('PAYMENT_SECURITY_TOKEN');
if (isModEnabled('member')) {
$tmparray['__SECUREKEYPAYMENT_MEMBER__'] = 'SecureKeyPAYMENTUniquePerMember';
}
if (isModEnabled('don')) {
$tmparray['__SECUREKEYPAYMENT_DONATION__'] = 'SecureKeyPAYMENTUniquePerDonation';
}
if (isModEnabled('invoice')) {
$tmparray['__SECUREKEYPAYMENT_INVOICE__'] = 'SecureKeyPAYMENTUniquePerInvoice';
}
if (isModEnabled('order')) {
$tmparray['__SECUREKEYPAYMENT_ORDER__'] = 'SecureKeyPAYMENTUniquePerOrder';
}
if (isModEnabled('contract')) {
$tmparray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'SecureKeyPAYMENTUniquePerContractLine';
}
//Online payment link
if (isModEnabled('member')) {
$tmparray['__ONLINEPAYMENTLINK_MEMBER__'] = 'OnlinePaymentLinkUniquePerMember';
}
if (isModEnabled('don')) {
$tmparray['__ONLINEPAYMENTLINK_DONATION__'] = 'OnlinePaymentLinkUniquePerDonation';
}
if (isModEnabled('invoice')) {
$tmparray['__ONLINEPAYMENTLINK_INVOICE__'] = 'OnlinePaymentLinkUniquePerInvoice';
}
if (isModEnabled('order')) {
$tmparray['__ONLINEPAYMENTLINK_ORDER__'] = 'OnlinePaymentLinkUniquePerOrder';
}
if (isModEnabled('contract')) {
$tmparray['__ONLINEPAYMENTLINK_CONTRACTLINE__'] = 'OnlinePaymentLinkUniquePerContractLine';
}
} else {
/* No need to show into tooltip help, option is not enabled
$vars['__SECUREKEYPAYMENT__']='';
$vars['__SECUREKEYPAYMENT_MEMBER__']='';
$vars['__SECUREKEYPAYMENT_INVOICE__']='';
$vars['__SECUREKEYPAYMENT_ORDER__']='';
$vars['__SECUREKEYPAYMENT_CONTRACTLINE__']='';
*/
}
if (getDolGlobalString('MEMBER_ENABLE_PUBLIC')) {
$tmparray['__PUBLICLINK_NEWMEMBERFORM__'] = 'BlankSubscriptionForm';
}
}
foreach ($tmparray as $key => $val) {
if (empty($val)) {
$tmparray[$key] = $key;
}
}
return $tmparray;
}
}