dolibarr 25.0.0-alpha
html.formmail.class.php
Go to the documentation of this file.
1<?php
2/* Copyright (C) 2005-2012 Laurent Destailleur <eldy@users.sourceforge.net>
3 * Copyright (C) 2005-2012 Regis Houssin <regis.houssin@inodbox.com>
4 * Copyright (C) 2010-2011 Juanjo Menent <jmenent@2byte.es>
5 * Copyright (C) 2015-2017 Marcos García <marcosgdf@gmail.com>
6 * Copyright (C) 2015-2017 Nicolas ZABOURI <info@inovea-conseil.com>
7 * Copyright (C) 2018-2026 Frédéric France <frederic.france@free.fr>
8 * Copyright (C) 2022 Charlene Benke <charlene@patas-monkey.com>
9 * Copyright (C) 2023 Anthony Berton <anthony.berton@bb2a.fr>
10 * Copyright (C) 2024-2026 MDW <mdeweerd@users.noreply.github.com>
11 * Copyright (C) 2026 Jose MARTINEZ <jose.martinez@pichinov.com>
12 * Copyright (C) 2026 Nathan Pixodeo <nathan@pixodeo.net>
13 *
14 *
15 * This program is free software; you can redistribute it and/or modify
16 * it under the terms of the GNU General Public License as published by
17 * the Free Software Foundation; either version 3 of the License, or
18 * (at your option) any later version.
19 *
20 * This program is distributed in the hope that it will be useful,
21 * but WITHOUT ANY WARRANTY; without even the implied warranty of
22 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 * GNU General Public License for more details.
24 *
25 * You should have received a copy of the GNU General Public License
26 * along with this program. If not, see <https://www.gnu.org/licenses/>.
27 */
28
34require_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
35require_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
36
37
44class FormMail extends Form
45{
49 public $db;
50
56 public $withform;
57
61 public $fromname;
62
66 public $frommail;
67
71 public $fromtype;
72
76 public $fromid;
77
81 public $fromalsorobot;
82
86 public $totype;
87
91 public $toid;
92
96 public $replytoname;
97
101 public $replytomail;
102
106 public $toname;
107
111 public $tomail;
112
116 public $trackid;
117
121 public $inreplyto;
122
126 public $withsubstit; // Show substitution array
130 public $withfrom;
131
135 public $withto; // Show recipient emails
139 public $withreplyto;
140
146 public $withtofree;
150 public $withtocc;
154 public $withtoccc;
158 public $withtopic;
162 public $witherrorsto;
163
167 public $withfile;
168
172 public $withlayout;
173
177 public $withaiprompt;
178
182 public $withmaindocfile;
186 public $withbody;
187
191 public $withfromreadonly;
195 public $withreplytoreadonly;
199 public $withtoreadonly;
203 public $withtoccreadonly;
207 public $witherrorstoreadonly;
211 public $withtocccreadonly;
215 public $withtopicreadonly;
219 public $withbodyreadonly;
223 public $withfilereadonly;
227 public $withdeliveryreceipt;
231 public $withcancel;
235 public $withdeliveryreceiptreadonly;
239 public $withfckeditor;
240
244 public $ckeditortoolbar;
245
249 public $substit = array();
250
254 public $substit_lines = array();
255
259 public $param = array();
260
264 public $withtouser = array();
268 public $withtoccuser = array();
269
273 public $lines_model;
274
278 public $withoptiononeemailperrecipient;
279
280
286 public function __construct($db)
287 {
288 $this->db = $db;
289
290 $this->withform = 1;
291
292 $this->withfrom = 1;
293 $this->withto = 1;
294 $this->withtofree = 1;
295 $this->withtocc = 1;
296 $this->withtoccc = '0';
297 $this->witherrorsto = 0;
298 $this->withtopic = 1;
299 $this->withfile = 0; // 1=Add section "Attached files". 2=Can add files.
300 $this->withmaindocfile = 0; // 1=Add a checkbox "Attach also main document" for mass actions (checked by default), -1=Add checkbox (not checked by default)
301 $this->withbody = 1;
302
303 $this->withfromreadonly = 1;
304 $this->withreplytoreadonly = 1;
305 $this->withtoreadonly = 0;
306 $this->withtoccreadonly = 0;
307 $this->withtocccreadonly = 0;
308 $this->witherrorstoreadonly = 0;
309 $this->withtopicreadonly = 0;
310 $this->withfilereadonly = 0;
311 $this->withbodyreadonly = 0;
312 $this->withdeliveryreceiptreadonly = 0;
313 $this->withfckeditor = -1; // -1 = Auto
314 }
315
316 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
322 public function clear_attached_files()
323 {
324 // phpcs:enable
325 global $conf, $user;
326 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
327
328 // Set tmp user directory
329 $vardir = $conf->user->dir_output."/".$user->id;
330 $upload_dir = $vardir.'/temp/'; // TODO Add $keytoavoidconflict in upload_dir path
331 if (is_dir($upload_dir)) {
332 dol_delete_dir_recursive($upload_dir);
333 }
334
335 $keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
336 unset($_SESSION["listofpaths".$keytoavoidconflict]);
337 unset($_SESSION["listofnames".$keytoavoidconflict]);
338 unset($_SESSION["listofmimes".$keytoavoidconflict]);
339 }
340
341 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
350 public function add_attached_files($path, $file = '', $type = '')
351 {
352 // phpcs:enable
353 $listofpaths = array();
354 $listofnames = array();
355 $listofmimes = array();
356
357 if (empty($file)) {
358 $file = basename($path);
359 }
360 if (empty($type)) {
361 $type = dol_mimetype($file);
362 }
363
364 $keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
365 if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
366 $listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
367 }
368 if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
369 $listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
370 }
371 if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
372 $listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
373 }
374 if (!in_array($file, $listofnames)) {
375 $listofpaths[] = $path;
376 $listofnames[] = $file;
377 $listofmimes[] = $type;
378 $_SESSION["listofpaths".$keytoavoidconflict] = implode(';', $listofpaths);
379 $_SESSION["listofnames".$keytoavoidconflict] = implode(';', $listofnames);
380 $_SESSION["listofmimes".$keytoavoidconflict] = implode(';', $listofmimes);
381 }
382 }
383
384 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
391 public function remove_attached_files($keytodelete)
392 {
393 // phpcs:enable
394 $listofpaths = array();
395 $listofnames = array();
396 $listofmimes = array();
397
398 $keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
399 if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
400 $listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
401 }
402 if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
403 $listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
404 }
405 if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
406 $listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
407 }
408 if ($keytodelete >= 0) {
409 unset($listofpaths[$keytodelete]);
410 unset($listofnames[$keytodelete]);
411 unset($listofmimes[$keytodelete]);
412 $_SESSION["listofpaths".$keytoavoidconflict] = implode(';', $listofpaths);
413 $_SESSION["listofnames".$keytoavoidconflict] = implode(';', $listofnames);
414 $_SESSION["listofmimes".$keytoavoidconflict] = implode(';', $listofmimes);
415 //var_dump($_SESSION['listofpaths']);
416 }
417 }
418
419 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
425 public function get_attached_files()
426 {
427 // phpcs:enable
428 $listofpaths = array();
429 $listofnames = array();
430 $listofmimes = array();
431
432 $keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
433 if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
434 $listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
435 }
436 if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
437 $listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
438 }
439 if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
440 $listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
441 }
442 return array('paths' => $listofpaths, 'names' => $listofnames, 'mimes' => $listofmimes);
443 }
444
445 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
456 public function show_form($addfileaction = 'addfile', $removefileaction = 'removefile')
457 {
458 // phpcs:enable
459 print $this->get_form($addfileaction, $removefileaction);
460 }
461
462 // phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
473 public function get_form($addfileaction = 'addfile', $removefileaction = 'removefile')
474 {
475 // phpcs:enable
476 global $conf, $langs, $user, $hookmanager, $form;
477
478 if (!is_object($form)) {
479 $form = new Form($this->db);
480 }
481
482 // Required to show editor assistants
483 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formfile.class.php';
484 $formfile = new FormFile($this->db);
485
486 require_once DOL_DOCUMENT_ROOT.'/core/class/html.formai.class.php';
487 $formai = new FormAI($this->db);
488
489 // Load translation files required by the page
490 $langs->loadLangs(array('other', 'mails', 'members'));
491
492 // Clear temp files. Must be done before call of triggers, at beginning (mode = init), or when we select a new template
493 if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelselected') && GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1')) {
494 $this->clear_attached_files();
495 }
496
497 // Call hook getFormMail
498 $hookmanager->initHooks(array('formmail'));
499
500 $parameters = array(
501 'addfileaction' => $addfileaction,
502 'removefileaction' => $removefileaction,
503 'trackid' => $this->trackid
504 );
505 $reshook = $hookmanager->executeHooks('getFormMail', $parameters, $this);
506
507 if (!empty($reshook)) {
508 return $hookmanager->resPrint;
509 } else {
510 $out = '';
511
512 $disablebademails = 1;
513
514 // Define output language
515 $outputlangs = $langs;
516 $newlang = '';
517 if (getDolGlobalInt('MAIN_MULTILANGS') && !empty($this->param['langsmodels'])) {
518 $newlang = $this->param['langsmodels'];
519 }
520 if (!empty($newlang)) {
521 $outputlangs = new Translate("", $conf);
522 $outputlangs->setDefaultLang($newlang);
523 $outputlangs->load('other');
524 }
525
526 // Get message template for $this->param["models"] into c_email_templates
527 $arraydefaultmessage = -1;
528 if ($this->param['models'] != 'none') {
529 $model_id = 0;
530 if (array_key_exists('models_id', $this->param)) {
531 $model_id = $this->param["models_id"];
532 }
533
534 $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
535 }
536
537 // Define list of attached files
538 $listofpaths = array();
539 $listofnames = array();
540 $listofmimes = array();
541 $keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
542
543 if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelselected') && GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1')) {
544 if (!empty($arraydefaultmessage->joinfiles) && !empty($this->param['fileinit']) && is_array($this->param['fileinit'])) {
545 foreach ($this->param['fileinit'] as $path) {
546 if (!empty($path)) {
547 $this->add_attached_files($path);
548 }
549 }
550 }
551 }
552
553 if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
554 $listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
555 }
556 if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
557 $listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
558 }
559 if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
560 $listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
561 }
562
563
564 $out .= "\n".'<!-- Begin form mail type='.$this->param["models"].' --><div id="mailformdiv"></div>'."\n";
565 if ($this->withform == 1) {
566 $out .= '<form method="POST" name="mailform" id="mailform" enctype="multipart/form-data" action="'.$this->param["returnurl"].'#formmail">'."\n";
567
568 $out .= '<a id="formmail" name="formmail"></a>';
569 $out .= '<input style="display:none" type="submit" id="sendmailhidden" name="sendmail">';
570 $out .= '<input type="hidden" name="token" value="'.newToken().'" />';
571 $out .= '<input type="hidden" name="page_y" value="" />';
572 $out .= '<input type="hidden" name="trackid" value="'.$this->trackid.'" />';
573 $out .= '<input type="hidden" name="inreplyto" value="'.$this->inreplyto.'" />';
574 }
575 if (!empty($this->withfrom)) {
576 if (!empty($this->withfromreadonly)) {
577 $out .= '<input type="hidden" id="fromname" name="fromname" value="'.$this->fromname.'" />';
578 $out .= '<input type="hidden" id="frommail" name="frommail" value="'.$this->frommail.'" />';
579 }
580 }
581 foreach ($this->param as $key => $value) {
582 if (is_array($value)) {
583 $out .= "<!-- param key=".$key." is array, we do not output input field for it -->\n";
584 } else {
585 $out .= '<input type="hidden" id="'.$key.'" name="'.$key.'" value="'.$value.'" />'."\n";
586 }
587 }
588
589 $modelmail_array = array();
590 $break = '';
591 if ($this->param['models'] != 'none') {
592 $result = $this->fetchAllEMailTemplate($this->param["models"], $user, $outputlangs);
593 if ($result < 0) {
594 setEventMessages($this->error, $this->errors, 'errors');
595 }
596
597 foreach ($this->lines_model as $line) {
598 $reg = array();
599 if (preg_match('/\‍((.*)\‍)/', $line->label, $reg)) {
600 $labeltouse = $langs->trans($reg[1]); // langs->trans when label is __(xxx)__
601 } else {
602 $labeltouse = $line->label;
603 }
604
605 if ($break != $line->lang) {
606 // New break for a new language, we add the break
607 $s = $line->lang;
608 $shtml = '----- '.$langs->trans("Language_".$line->lang).' -----';
609 $modelmail_array['separator_'.$line->lang] = array('label' => $s, 'data-html' => $shtml, 'disabled' => 'disabled');
610 }
611
612 // We escape the $labeltouse to store it into $modelmail_array.
613 $s = dol_escape_htmltag($labeltouse);
614 $shtml = dol_escape_htmltag($labeltouse);
615 if ($line->lang) {
616 $shtml = picto_from_langcode($line->lang).'</span> '.$shtml;
617 }
618 if ($line->private) {
619 $shtml .= ' - <span class="opacitymedium small">'.dol_escape_htmltag($langs->trans("Private")).'</span>';
620 }
621
622 $modelmail_array[$line->id] = array('label' => $s, 'data-html' => $shtml);
623 }
624 }
625
626 // Zone to select email template
627 if (count($modelmail_array) > 0) {
628 $model_mail_selected_id = GETPOSTISSET('modelmailselected') ? GETPOSTINT('modelmailselected') : ($arraydefaultmessage->id > 0 ? $arraydefaultmessage->id : 0);
629
630 // If list of template is filled
631 $out .= '<div class="center" style="padding: 0px 0 12px 0">'."\n";
632
633 $out .= $this->selectarray('modelmailselected', $modelmail_array, $model_mail_selected_id, $langs->trans('SelectMailModel'), 0, 0, '', 0, 0, 0, '', 'minwidth150', 1, '', 0, 1);
634 if ($user->admin) {
635 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFrom", $langs->transnoentitiesnoconv('Setup').' - '.$langs->transnoentitiesnoconv('EMails')), 1);
636 }
637
638 // Language selector for predefined message templates (only when multilang is enabled)
639 if (getDolGlobalInt('MAIN_MULTILANGS')) {
640 // This feature is in conflict with the existing one where all templates are show with the language in a flag so user
641 // can choose the template in the correct language.To avoid duplicate and conflict selection, we currently enable this on a hidden constant.
642 // 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.
643 if (getDolGlobalInt('MAIN_MULTILANGS_ASK_LANG_IN_SEPARATE_COMBO')) {
644 include_once DOL_DOCUMENT_ROOT.'/core/class/html.formadmin.class.php';
645 $formadmin = new FormAdmin($this->db);
646 $currentlang = (is_object($outputlangs) ? $outputlangs->defaultlang : $langs->defaultlang);
647 $out .= ' &nbsp; ';
648 $out .= $formadmin->select_language($currentlang, 'lang_id', 0, array(), 1, 0, 0, 'maxwidth150');
649 }
650 }
651
652 $out .= '<input type="submit" class="button reposition smallpaddingimp" value="'.$langs->trans('Apply').'" name="modelselected" id="modelselected">';
653 $out .= ' &nbsp; ';
654 $out .= '</div>';
655 } elseif (!empty($this->param['models']) && in_array($this->param['models'], array(
656 'propal_send', 'order_send', 'facture_send',
657 'shipping_send', 'reception_send', 'fichinter_send', 'supplier_proposal_send', 'order_supplier_send',
658 'invoice_supplier_send', 'supplier_payment_send', 'thirdparty', 'contract', 'user', 'recruitmentcandidature_send', 'product_send', 'all'
659 ))) {
660 // If list of template is empty
661 $out .= '<div class="center" style="padding: 0px 0 12px 0">'."\n";
662 $out .= '<span class="opacitymedium">'.$langs->trans('SelectMailModel').':</span> ';
663 $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.
664 if ($user->admin) {
665 $out .= info_admin($langs->trans("YouCanChangeValuesForThisListFrom", $langs->transnoentitiesnoconv('Setup').' - '.$langs->transnoentitiesnoconv('EMails')), 1);
666 }
667 $out .= ' &nbsp; ';
668 $out .= '<input type="submit" class="button reposition smallpaddingimp" value="'.$langs->trans('Apply').'" name="modelselected" disabled="disabled" id="modelselected">';
669 $out .= ' &nbsp; ';
670 $out .= '</div>';
671 } else {
672 $out .= '<!-- No template available for $this->param["models"] = '.$this->param['models'].' -->';
673 }
674
675
676 $out .= '<table class="tableforemailform boxtablenotop centpercent">'."\n";
677
678 // Substitution array/string
679 $helpforsubstitution = '';
680 if (is_array($this->substit) && count($this->substit)) {
681 $helpforsubstitution .= $langs->trans('AvailableVariables').' :<br><br><span class="small">'."\n";
682 foreach ($this->substit as $key => $val) {
683 // Do not show deprecated variables into the tooltip help of substitution variables
684 if (in_array($key, array('__NEWREF__', '__REFCLIENT__', '__REFSUPPLIER__', '__SUPPLIER_ORDER_DATE_DELIVERY__', '__SUPPLIER_ORDER_DELAY_DELIVERY__'))) {
685 continue;
686 }
687 if (is_array($val)) {
688 $val = implode(', ', $val);
689 } // key __MULTICURRENCY_CODE__ is an array and crashes dolGetFirstLineOfText function which accept only text
690 $helpforsubstitution .= $key.' -> '.$langs->trans(dol_string_nohtmltag(dolGetFirstLineOfText((string) $val))).'<br>';
691 }
692 $helpforsubstitution .= '</span>';
693 }
694
695 /*
696 if (!empty($this->withsubstit)) { // Unset or set ->withsubstit=0 to disable this.
697 $out .= '<tr><td colspan="2" class="right">';
698 if (is_numeric($this->withsubstit)) {
699 $out .= $form->textwithpicto($langs->trans("EMailTestSubstitutionReplacedByGenericValues"), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltip'); // Old usage
700 } else {
701 $out .= $form->textwithpicto($langs->trans('AvailableVariables'), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltip'); // New usage
702 }
703 $out .= "</td></tr>\n";
704 }*/
705
706 // From
707 if (!empty($this->withfrom)) {
708 if (!empty($this->withfromreadonly)) {
709 $out .= '<tr><td class="fieldrequired minwidth200">'.$langs->trans("MailFrom").'</td><td>';
710
711 // $this->fromtype is the default value to use to select sender
712 if (!($this->fromtype === 'user' && $this->fromid > 0)
713 && !($this->fromtype === 'company')
714 && !($this->fromtype === 'robot')
715 && !preg_match('/user_aliases/', $this->fromtype)
716 && !preg_match('/global_aliases/', $this->fromtype)
717 && !preg_match('/senderprofile/', $this->fromtype)
718 && !preg_match('/from_template_(\d+)/', $this->fromtype)
719 ) {
720 // Use this->fromname and this->frommail or error if not defined
721 $out .= $this->fromname;
722 if ($this->frommail) {
723 $out .= ' &lt;'.$this->frommail.'&gt;';
724 } else {
725 if ($this->fromtype) {
726 $langs->load('errors');
727 $out .= '<span class="warning"> &lt;'.$langs->trans('ErrorNoMailDefinedForThisUser').'&gt; </span>';
728 }
729 }
730 } else {
731 $liste = array();
732
733 // Add user email
734 if (empty($user->email)) {
735 $langs->load('errors');
736 $s = $user->getFullName($langs).' &lt;'.$langs->trans('ErrorNoMailDefinedForThisUser').'&gt;';
737 } else {
738 $s = $user->getFullName($langs).' &lt;'.$user->email.'&gt;';
739 }
740 $liste['user'] = array('label' => $s, 'data-html' => $s);
741
742 // Add also company main email
743 if (getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
744 $s = getDolGlobalString('MAIN_INFO_SOCIETE_NOM', getDolGlobalString('MAIN_INFO_SOCIETE_EMAIL')).' &lt;' . getDolGlobalString('MAIN_INFO_SOCIETE_MAIL').'&gt;';
745 $liste['company'] = array('label' => $s, 'data-html' => $s);
746 }
747
748 // Add also email aliases if there is some
749 $listaliases = array(
750 'global_aliases' => getDolGlobalString('MAIN_INFO_SOCIETE_MAIL_ALIASES'),
751 );
752
753 if (!empty($arraydefaultmessage->email_from) && !empty($arraydefaultmessage->id)) {
754 $templatemailfrom = ' &lt;'.$arraydefaultmessage->email_from.'&gt;';
755 $liste['from_template_'.((int) $arraydefaultmessage->id)] = array('label' => $templatemailfrom, 'data-html' => $templatemailfrom);
756 }
757
758 // Also add robot email
759 if (!empty($this->fromalsorobot)) {
760 if (getDolGlobalString('MAIN_MAIL_EMAIL_FROM') && getDolGlobalString('MAIN_MAIL_EMAIL_FROM') != getDolGlobalString('MAIN_INFO_SOCIETE_MAIL')) {
761 $s = getDolGlobalString('MAIN_MAIL_EMAIL_FROM');
762 if ($this->frommail) {
763 $s .= ' &lt;' . getDolGlobalString('MAIN_MAIL_EMAIL_FROM').'&gt;';
764 }
765 $liste['main_from'] = array('label' => $s, 'data-html' => $s);
766 }
767 }
768
769 // Add also email aliases from the c_email_senderprofile table
770 $sql = "SELECT rowid, label, email FROM ".$this->db->prefix()."c_email_senderprofile";
771 $sql .= " WHERE active = 1 AND (private = 0 OR private = ".((int) $user->id).") AND entity IN (".getEntity('c_email_senderprofile').")";
772 $sql .= " ORDER BY position";
773 $resql = $this->db->query($sql);
774 if ($resql) {
775 $num = $this->db->num_rows($resql);
776 $i = 0;
777 while ($i < $num) {
778 $obj = $this->db->fetch_object($resql);
779 if ($obj) {
780 $listaliases['senderprofile_'.$obj->rowid] = $obj->label.' <'.$obj->email.'>';
781 }
782 $i++;
783 }
784 } else {
785 dol_print_error($this->db);
786 }
787
788 foreach ($listaliases as $typealias => $listalias) {
789 $posalias = 0;
790 $listaliasarray = explode(',', $listalias);
791 foreach ($listaliasarray as $listaliasval) {
792 $posalias++;
793 $listaliasval = trim($listaliasval);
794 if ($listaliasval) {
795 $listaliasval = preg_replace('/</', '&lt;', $listaliasval);
796 $listaliasval = preg_replace('/>/', '&gt;', $listaliasval);
797 if (!preg_match('/&lt;/', $listaliasval)) {
798 $listaliasval = '&lt;'.$listaliasval.'&gt;';
799 }
800 $liste[$typealias.'_'.$posalias] = array('label' => $listaliasval, 'data-html' => $listaliasval);
801 }
802 }
803 }
804
805 // Using ajaxcombo here make the '<email>' no more visible on list because <emailofuser> is not a valid html tag,
806 // so we transform before each record into $liste to be printable with ajaxcombo by replacing <> into ()
807 // $liste['senderprofile_0_0'] = array('label'=>'rrr', 'data-html'=>'rrr &lt;aaaa&gt;');
808 foreach ($liste as $key => $val) {
809 if (!empty($liste[$key]['data-html'])) {
810 $liste[$key]['data-html'] = str_replace(array('&lt;', '<', '&gt;', '>'), array('__LTCHAR__', '__LTCHAR__', '__GTCHAR__', '__GTCHAR__'), $liste[$key]['data-html']);
811 $liste[$key]['data-html'] = str_replace(array('__LTCHAR__', '__GTCHAR__'), array('<span class="opacitymedium">(', ')</span>'), $liste[$key]['data-html']);
812 }
813 }
814 $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);
815 }
816
817 $out .= "</td></tr>\n";
818 } else {
819 $out .= '<tr><td class="fieldrequired width200">'.$langs->trans("MailFrom")."</td><td>";
820 $out .= $langs->trans("Name").':<input type="text" id="fromname" name="fromname" class="maxwidth200onsmartphone" value="'.$this->fromname.'" />';
821 $out .= '&nbsp; &nbsp; ';
822 $out .= $langs->trans("EMail").':&lt;<input type="text" id="frommail" name="frommail" class="maxwidth200onsmartphone" value="'.$this->frommail.'" />&gt;';
823 $out .= "</td></tr>\n";
824 }
825 }
826
827 // Hook to let a module render the whole recipients block (e.g. a modern tokenized To/CC/BCC field). If the hook handles it (returns > 0), the native recipient rows below are skipped.
828 $parameters = array();
829 $reshook = $hookmanager->executeHooks('printEmailRecipients', $parameters, $this);
830 if ($reshook < 0) {
831 setEventMessages($hookmanager->error, $hookmanager->errors, 'errors');
832 }
833 $out .= $hookmanager->resPrint;
834 if (empty($reshook)) {
835 // To
836 if (!empty($this->withto) || is_array($this->withto)) {
837 $out .= getDolGlobalString('MAIL_ENABLE_FREETAG_RECIPIENT_INPUT') ? $this->getHtmlForToNew() : $this->getHtmlForTo();
838 }
839
840 // To User
841 if (!empty($this->withtouser) && is_array($this->withtouser) && getDolGlobalString('MAIN_MAIL_ENABLED_USER_DEST_SELECT')) {
842 $out .= '<tr><td>';
843 $out .= $langs->trans("MailToUsers");
844 $out .= '</td><td>';
845
846 // multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time
847 $tmparray = $this->withtouser;
848 foreach ($tmparray as $key => $val) {
849 $tmparray[$key] = dol_htmlentities($tmparray[$key], 0, 'UTF-8', true);
850 }
851 $withtoselected = GETPOST("receiveruser", 'array'); // Array of selected value
852 if (empty($withtoselected) && count($tmparray) == 1 && GETPOST('action', 'aZ09') == 'presend') {
853 $withtoselected = array_keys($tmparray);
854 }
855 $out .= $form->multiselectarray("receiveruser", $tmparray, $withtoselected, 0, 0, 'inline-block minwidth500', 0, "");
856 $out .= "</td></tr>\n";
857 }
858
859 // With option for one email per recipient
860 if (!empty($this->withoptiononeemailperrecipient)) {
861 if (abs($this->withoptiononeemailperrecipient) == 1) {
862 $out .= '<tr><td class="minwidth200">';
863 $out .= $langs->trans("GroupEmails");
864 $out .= '</td><td>';
865 $out .= ' <input type="checkbox" id="oneemailperrecipient" value="1" name="oneemailperrecipient"'.($this->withoptiononeemailperrecipient > 0 ? ' checked="checked"' : '').'> ';
866 $out .= '<label for="oneemailperrecipient">';
867 $out .= $form->textwithpicto($langs->trans("OneEmailPerRecipient"), $langs->trans("WarningIfYouCheckOneRecipientPerEmail"), 1, 'help');
868 $out .= '</label>';
869 //$out .= '<span class="hideonsmartphone opacitymedium">';
870 //$out .= ' - ';
871 //$out .= $langs->trans("WarningIfYouCheckOneRecipientPerEmail");
872 //$out .= '</span>';
873 if (getDolGlobalString('MASS_ACTION_EMAIL_ON_DIFFERENT_THIRPARTIES_ADD_CUSTOM_EMAIL')) {
874 if (!empty($this->withto) && !is_array($this->withto)) {
875 $out .= ' <span class="opacitymedium">'.$langs->trans("or").'</span> <input type="email" name="emailto" value="">';
876 }
877 }
878 $out .= '</td></tr>';
879 } else {
880 $out .= '<tr><td><input type="hidden" name="oneemailperrecipient" value="1"></td><td></td></tr>';
881 }
882 }
883
884 // CC
885 if (!empty($this->withtocc) || is_array($this->withtocc)) {
886 $out .= getDolGlobalString('MAIL_ENABLE_FREETAG_RECIPIENT_INPUT') ? $this->getHtmlForCcNew() : $this->getHtmlForCc();
887 }
888
889 // To User cc
890 if (!empty($this->withtoccuser) && is_array($this->withtoccuser) && getDolGlobalString('MAIN_MAIL_ENABLED_USER_DEST_SELECT')) {
891 $out .= '<tr><td>';
892 $out .= $langs->trans("MailToCCUsers");
893 $out .= '</td><td>';
894
895 // multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time
896 $tmparray = $this->withtoccuser;
897 foreach ($tmparray as $key => $val) {
898 $tmparray[$key] = dol_htmlentities($tmparray[$key], 0, 'UTF-8', true);
899 }
900 $withtoselected = GETPOST("receiverccuser", 'array'); // Array of selected value
901 if (empty($withtoselected) && count($tmparray) == 1 && GETPOST('action', 'aZ09') == 'presend') {
902 $withtoselected = array_keys($tmparray);
903 }
904 $out .= $form->multiselectarray("receiverccuser", $tmparray, $withtoselected, 0, 0, 'inline-block minwidth500', 0, "");
905 $out .= "</td></tr>\n";
906 }
907
908 // CCC
909 if (!empty($this->withtoccc) || is_array($this->withtoccc)) {
910 $out .= getDolGlobalString('MAIL_ENABLE_FREETAG_RECIPIENT_INPUT') ? $this->getHtmlForWithCccNew() : $this->getHtmlForWithCcc();
911 }
912 }
913
914 // Replyto
915 if (!empty($this->withreplyto)) {
916 if ($this->withreplytoreadonly) {
917 $out .= '<input type="hidden" id="replyname" name="replyname" value="'.$this->replytoname.'" />';
918 $out .= '<input type="hidden" id="replymail" name="replymail" value="'.$this->replytomail.'" />';
919 $out .= "<tr><td>".$langs->trans("MailReply")."</td><td>".$this->replytoname.($this->replytomail ? (" &lt;".$this->replytomail."&gt;") : "");
920 $out .= "</td></tr>\n";
921 }
922 }
923
924 // Errorsto
925 if (!empty($this->witherrorsto)) {
926 $out .= $this->getHtmlForWithErrorsTo();
927 }
928
929 // Ask delivery receipt
930 if (!empty($this->withdeliveryreceipt) && getDolGlobalInt('MAIN_EMAIL_SUPPORT_ACK')) {
931 $out .= $this->getHtmlForDeliveryreceipt();
932 }
933
934 // Topic
935 if (!empty($this->withtopic)) {
936 $out .= $this->getHtmlForTopic($arraydefaultmessage, $helpforsubstitution);
937 }
938
939 // Attached files
940 if (!empty($this->withfile)) {
941 $out .= '<tr>';
942 $out .= '<td class="tdtop">'.$langs->trans("MailFile").'</td>';
943
944 $out .= '<td>';
945
946 if ($this->withmaindocfile) {
947 // withmaindocfile is set to 1 or -1 to show the checkbox (-1 = checked or 1 = not checked)
948 if (GETPOSTISSET('sendmail')) {
949 $this->withmaindocfile = (GETPOST('addmaindocfile', 'alpha') ? -1 : 1);
950 } elseif (is_object($arraydefaultmessage) && $arraydefaultmessage->id > 0) {
951 // If a template was selected, we use setup of template to define if join file checkbox is selected or not.
952 $this->withmaindocfile = ($arraydefaultmessage->joinfiles ? -1 : 1);
953 }
954 }
955
956 if (!empty($this->withmaindocfile)) {
957 if ($this->withmaindocfile == 1) {
958 $out .= '<input type="checkbox" id="addmaindocfile" name="addmaindocfile" value="1" />';
959 } elseif ($this->withmaindocfile == -1) {
960 $out .= '<input type="checkbox" id="addmaindocfile" name="addmaindocfile" value="1" checked="checked" />';
961 }
962 if (getDolGlobalString('MAIL_MASS_ACTION_ADD_LAST_IF_MAIN_DOC_NOT_FOUND')) {
963 $out .= ' <label for="addmaindocfile">'.$langs->trans("JoinMainDocOrLastGenerated").'.</label><br>';
964 } else {
965 $out .= ' <label for="addmaindocfile">'.$langs->trans("JoinMainDoc").'.</label><br>';
966 }
967 }
968
969 if (is_numeric($this->withfile)) {
970 // TODO Trick to have param removedfile containing nb of file to delete. But this does not works without javascript
971 $out .= '<input type="hidden" class="removedfilehidden" name="removedfile" value="">'."\n";
972 $out .= '<script nonce="'.getNonce().'" type="text/javascript">';
973 $out .= 'jQuery(document).ready(function () {';
974 $out .= ' jQuery(".removedfile").click(function() {';
975 $out .= ' jQuery(".removedfilehidden").val(jQuery(this).val());';
976 $out .= ' });';
977 $out .= '})';
978 $out .= '</script>'."\n";
979 if (count($listofpaths)) {
980 foreach ($listofpaths as $key => $val) {
981 $relativepathtofile = substr($val, (strlen(DOL_DATA_ROOT) - strlen($val)));
982
983 $entity = (isset($this->param['object_entity']) ? $this->param['object_entity'] : $conf->entity);
984 if ($entity > 1) {
985 $relativepathtofile = str_replace('/'.$entity.'/', '/', $relativepathtofile);
986 }
987 // Try to extract data from full path
988 $formfile_params = array();
989 preg_match('#^(/)(\w+)(/)(.+)$#', $relativepathtofile, $formfile_params);
990
991 $out .= '<div id="attachfile_'.$key.'">';
992 // Preview of attachment
993 $out .= img_mime($listofnames[$key]).$listofnames[$key];
994
995 $out .= ' '.$formfile->showPreview(array('fullname' => $val,'name' => basename($val)), $formfile_params[2], $formfile_params[4], 0, ($entity == 1 ? '' : 'entity='.((int) $entity)));
996
997 if (!$this->withfilereadonly) {
998 $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.'" />';
999 //$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>';
1000 }
1001 $out .= '<br></div>';
1002 }
1003 } /*elseif (empty($this->withmaindocfile)) {
1004 //$out .= '<span class="opacitymedium">'.$langs->trans("NoAttachedFiles").'</span><br>';
1005 }*/
1006 if ($this->withfile == 2) {
1007 $maxfilesizearray = getMaxFileSizeArray();
1008 $maxmin = $maxfilesizearray['maxmin'];
1009 if ($maxmin > 0) {
1010 $out .= '<input type="hidden" name="MAX_FILE_SIZE" value="'.($maxmin * 1024).'">'; // MAX_FILE_SIZE must precede the field type=file
1011 }
1012 // Can add other files
1013 if (!getDolGlobalString('FROM_MAIL_DONT_USE_INPUT_FILE_MULTIPLE')) {
1014 $out .= '<input type="file" class="flat" id="addedfile" name="addedfile[]" value="'.$langs->trans("Upload").'" multiple />';
1015 } else {
1016 $out .= '<input type="file" class="flat" id="addedfile" name="addedfile" value="'.$langs->trans("Upload").'" />';
1017 }
1018 $out .= ' ';
1019 $out .= '<input type="submit" class="button smallpaddingimp" id="'.$addfileaction.'" name="'.$addfileaction.'" value="'.$langs->trans("MailingAddFile").'" />';
1020 }
1021 } else {
1022 $out .= $this->withfile;
1023 }
1024
1025 $out .= "</td></tr>\n";
1026 }
1027
1028 // Message (+ Links to choose layout or ai prompt)
1029 if (!empty($this->withbody)) {
1030 $defaultmessage = GETPOST('message', 'restricthtml');
1031 if (!GETPOST('modelselected', 'alpha') || GETPOST('modelmailselected') != '-1') {
1032 if ($arraydefaultmessage && $arraydefaultmessage->content) {
1033 $defaultmessage = (string) $arraydefaultmessage->content;
1034 } elseif (!is_numeric($this->withbody)) {
1035 $defaultmessage = $this->withbody;
1036 }
1037 }
1038
1039 // Complete substitution array with the url to make online payment
1040 $paymenturl = '';
1041 // Set the online payment url link into __ONLINE_PAYMENT_URL__ key
1042 require_once DOL_DOCUMENT_ROOT.'/core/lib/payments.lib.php';
1043 $validpaymentmethod = getValidOnlinePaymentMethods('');
1044
1045 if (empty($this->substit['__REF__'])) { // @phan-suppress-current-line PhanTypeMismatchProperty
1046 $paymenturl = '';
1047 } else {
1048 $langs->loadLangs(array('paypal', 'other'));
1049 $typeforonlinepayment = 'free';
1050 if ($this->param["models"] == 'order' || $this->param["models"] == 'order_send') {
1051 $typeforonlinepayment = 'order'; // TODO use detection on something else than template
1052 }
1053 if ($this->param["models"] == 'invoice' || $this->param["models"] == 'facture_send') {
1054 $typeforonlinepayment = 'invoice'; // TODO use detection on something else than template
1055 }
1056 if ($this->param["models"] == 'member') {
1057 $typeforonlinepayment = 'member'; // TODO use detection on something else than template
1058 }
1059 $url = getOnlinePaymentUrl(0, $typeforonlinepayment, $this->substit['__REF__']);
1060 $paymenturl = $url;
1061 }
1062
1063 if (count($validpaymentmethod) > 0 && $paymenturl) {
1064 $langs->load('other');
1065 $this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'] = str_replace('\n', "\n", $langs->transnoentities("PredefinedMailContentLink", $paymenturl));
1066 $this->substit['__ONLINE_PAYMENT_URL__'] = $paymenturl;
1067 } elseif (count($validpaymentmethod) > 0) {
1068 $this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'] = '__ONLINE_PAYMENT_TEXT_AND_URL__';
1069 $this->substit['__ONLINE_PAYMENT_URL__'] = '__ONLINE_PAYMENT_URL__';
1070 } else {
1071 $this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'] = '';
1072 $this->substit['__ONLINE_PAYMENT_URL__'] = '';
1073 }
1074
1075 $this->substit['__ONLINE_INTERVIEW_SCHEDULER_TEXT_AND_URL__'] = '';
1076
1077 // Generate the string with the template for lines repeated and filled for each line
1078 $lines = '';
1079 $defaultlines = $arraydefaultmessage->content_lines;
1080 if (isset($defaultlines)) {
1081 foreach ($this->substit_lines as $lineid => $substit_line) {
1082 $lines .= make_substitutions($defaultlines, $substit_line, $outputlangs)."\n";
1083 }
1084 }
1085 $this->substit['__LINES__'] = $lines;
1086
1087 $defaultmessage = str_replace('\n', "\n", $defaultmessage);
1088
1089 // Deal with format differences between message and some substitution variables (text / HTML)
1090 $atleastonecomponentishtml = 0;
1091 if (strpos($defaultmessage, '__USER_SIGNATURE__') !== false && dol_textishtml($this->substit['__USER_SIGNATURE__'])) {
1092 $atleastonecomponentishtml++;
1093 }
1094 if (strpos($defaultmessage, '__SENDEREMAIL_SIGNATURE__') !== false && dol_textishtml($this->substit['__SENDEREMAIL_SIGNATURE__'])) {
1095 $atleastonecomponentishtml++;
1096 }
1097 if (strpos($defaultmessage, '__ONLINE_PAYMENT_TEXT_AND_URL__') !== false && dol_textishtml($this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'])) {
1098 $atleastonecomponentishtml++;
1099 }
1100 if (strpos($defaultmessage, '__ONLINE_INTERVIEW_SCHEDULER_TEXT_AND_URL__') !== false && dol_textishtml($this->substit['__ONLINE_INTERVIEW_SCHEDULER_TEXT_AND_URL__'])) {
1101 $atleastonecomponentishtml++;
1102 }
1103 if (dol_textishtml($defaultmessage)) {
1104 $atleastonecomponentishtml++;
1105 }
1106 if ($atleastonecomponentishtml) {
1107 if (!dol_textishtml($this->substit['__USER_SIGNATURE__'])) {
1108 $this->substit['__USER_SIGNATURE__'] = dol_nl2br($this->substit['__USER_SIGNATURE__']);
1109 }
1110 if (!dol_textishtml($this->substit['__SENDEREMAIL_SIGNATURE__'])) {
1111 $this->substit['__SENDEREMAIL_SIGNATURE__'] = dol_nl2br($this->substit['__SENDEREMAIL_SIGNATURE__']);
1112 }
1113 if (!dol_textishtml($this->substit['__LINES__'])) {
1114 $this->substit['__LINES__'] = dol_nl2br($this->substit['__LINES__']);
1115 }
1116 if (!dol_textishtml($this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'])) {
1117 $this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__'] = dol_nl2br($this->substit['__ONLINE_PAYMENT_TEXT_AND_URL__']);
1118 }
1119 if (!dol_textishtml($defaultmessage)) {
1120 $defaultmessage = dol_nl2br($defaultmessage);
1121 }
1122 }
1123
1124 if (GETPOSTISSET("message") && !GETPOST('modelselected')) {
1125 $defaultmessage = GETPOST("message", "restricthtml");
1126 } else {
1127 // Pass $outputlangs so __(TranslationKey)__ in the template body is resolved
1128 // in the language of the selected email template, not the operator's language
1129 // (see issue #34540).
1130 $defaultmessage = make_substitutions($defaultmessage, $this->substit, $outputlangs);
1131 // Clean first \n and br (to avoid empty line when CONTACTCIVNAME is empty)
1132 $defaultmessage = preg_replace("/^(<br>)+/", "", $defaultmessage);
1133 $defaultmessage = preg_replace("/^\n+/", "", $defaultmessage);
1134 }
1135
1136 $out .= '<!-- Message line from get_form -->';
1137 $out .= '<tr>';
1138 $out .= '<td class="tdtop">';
1139 $out .= $form->textwithpicto($langs->trans('MailText'), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltipfrombody');
1140 $out .= '</td>';
1141 $out .= '<td class="tdtop">';
1142
1143 $formmail = $this;
1144 $showlinktolayout = ($formmail->withfckeditor && getDolGlobalInt('MAIN_EMAIL_USE_LAYOUT')) ? $formmail->withlayout : '';
1145 $showlinktolayoutlabel = $langs->trans("FillMessageWithALayout");
1146 $showlinktoai = ($formmail->withaiprompt && isModEnabled('ai')) ? 'textgenerationemail' : '';
1147 $showlinktoailabel = $langs->trans("AIEnhancements");
1148 $formatforouput = '';
1149 $htmlname = 'message';
1150
1151 $formai->substit = $this->substit;
1152 $formai->substit_lines = $this->substit_lines;
1153
1154 // Fill $out
1155 $db = $this->db;
1156 include DOL_DOCUMENT_ROOT.'/core/tpl/formlayoutai.tpl.php';
1157
1158 $out .= '</td>';
1159 $out .= '</tr>';
1160
1161 $out .= '<tr>';
1162 $out .= '<td colspan="2">';
1163 if ($this->withbodyreadonly) {
1164 $out .= nl2br($defaultmessage);
1165 $out .= '<input type="hidden" id="message" name="message" disabled value="'.$defaultmessage.'" />';
1166 } else {
1167 if (!isset($this->ckeditortoolbar)) {
1168 $this->ckeditortoolbar = 'dolibarr_mailings';
1169 }
1170
1171 // Editor wysiwyg
1172 require_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1173 if ($this->withfckeditor == -1) {
1174 if (getDolGlobalString('FCKEDITOR_ENABLE_MAIL')) {
1175 $this->withfckeditor = 1;
1176 } else {
1177 $this->withfckeditor = 0;
1178 }
1179 }
1180
1181 $uselocalbrowser = getDolGlobalBool('FCKEDITOR_ENABLE_IMAGE_UPLOAD');
1182 // $uselocalbrowser = true;
1183
1184 $doleditor = new DolEditor('message', $defaultmessage, '', 280, $this->ckeditortoolbar, 'In', true, $uselocalbrowser, $this->withfckeditor, 8, '95%');
1185 $out .= $doleditor->Create(1);
1186 }
1187 $out .= "</td></tr>\n";
1188 }
1189
1190 $out .= '</table>'."\n";
1191
1192 if ($this->withform == 1 || $this->withform == -1) {
1193 $out .= '<div class="center">';
1194 $out .= '<input type="submit" class="button button-add" id="sendmail" name="sendmail" value="'.$langs->trans("SendMail").'"';
1195 // Add a javascript test to avoid to forget to submit file before sending email
1196 if ($this->withfile == 2 && $conf->use_javascript_ajax) {
1197 $out .= ' onClick="if (document.mailform.addedfile.value != \'\') { alert(\''.dol_escape_js($langs->trans("FileWasNotUploaded")).'\'); return false; } else { return true; }"';
1198 }
1199 $out .= ' />';
1200 if ($this->withcancel) {
1201 $out .= '<input class="button button-cancel" type="submit" id="cancel" name="cancel" value="'.$langs->trans("Cancel").'" />';
1202 }
1203 $out .= '</div>'."\n";
1204 }
1205
1206 if ($this->withform == 1) {
1207 $out .= '</form>'."\n";
1208 }
1209
1210 // Disable enter key if option MAIN_MAILFORM_DISABLE_ENTERKEY is set
1211 if (getDolGlobalString('MAIN_MAILFORM_DISABLE_ENTERKEY')) {
1212 $out .= '<script nonce="'.getNonce().'" type="text/javascript">';
1213 $out .= 'jQuery(document).ready(function () {';
1214 $out .= ' $(document).on("keypress", \'#mailform\', function (e) { /* Note this is called at every key pressed ! */
1215 var code = e.keyCode || e.which;
1216 if (code == 13) {
1217 console.log("Enter was intercepted and blocked");
1218 e.preventDefault();
1219 return false;
1220 }
1221 });';
1222 $out .= ' })';
1223 $out .= '</script>';
1224 }
1225
1226 $out .= "<!-- End form mail -->\n";
1227
1228 return $out;
1229 }
1230 }
1231
1248 private function getHtmlForFreetagRecipient($htmlname, array $sourcearray, array $selected)
1249 {
1250 global $form;
1251
1252 $tmparray = array();
1253 foreach ($sourcearray as $key => $val) {
1254 $label = is_array($val) ? $val['label'] : $val;
1255 $label = str_replace(array('<', '>'), array('(', ')'), $label);
1256 // multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time
1257 $tmparray[$key] = dol_htmlentities($label, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', true);
1258 }
1259
1260 $morecss = 'inline-block minwidth500';
1261
1262 // $addjscombo = 0: skip multiselectarray()'s generic select2 init script, we init it ourselves below with tags/createTag.
1263 $out = $form->multiselectarray($htmlname, $tmparray, $selected, 0, 0, $morecss, 0, 0, '', '', '', 0);
1264
1265 $out .= "\n".'<script nonce="'.getNonce().'">'."\n";
1266 $out .= '$(document).ready(function () {'."\n";
1267 $out .= ' $(\''.dol_escape_js('#'.$htmlname).'\').select2({'."\n";
1268 // Nowdoc for the static part of the select2 config (no PHP value is interpolated in here, so unlike
1269 // a heredoc none of this JS's own '$(' / '$.' / trailing regex '$' anchors need checking against
1270 // PHP's variable-interpolation rules): the two lines around it that need an escaped PHP value stay
1271 // as plain concatenation, same as multiselectarray()'s own script-building style.
1272 $out .= "
1273 dir: 'ltr',
1274 theme: 'default',
1275 width: 'resolve',
1276 language: (typeof select2arrayoflanguage === 'undefined') ? 'en' : select2arrayoflanguage,
1277 tags: true,
1278 createTag: function (params) {
1279 var REGEX_EMAIL = '([a-z0-9!#$%&\'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&\'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)';
1280 var term = $.trim(params.term);
1281 if (term.indexOf('@') === -1) {
1282 return null;
1283 }
1284 var matchwithname = term.match(new RegExp('^([^<]*)<' + REGEX_EMAIL + '>$', 'i'));
1285 if (matchwithname !== null) {
1286 var valuewithname = $.trim(matchwithname[1]) + ' <' + matchwithname[2] + '>';
1287 return { id: valuewithname, text: valuewithname };
1288 }
1289 if (term.indexOf('<') >= 0 || term.indexOf('>') >= 0) {
1290 return null;
1291 }
1292 var matchbare = term.match(new RegExp('^' + REGEX_EMAIL + '$', 'i'));
1293 if (matchbare !== null) {
1294 return { id: matchbare[1], text: matchbare[1] };
1295 }
1296 return null;
1297 }
1298 });
1299 ";
1300 $out .= ' $(\''.dol_escape_js('#'.$htmlname.' + .select2').'\').addClass(\''.dol_escape_js($morecss).'\');'."\n";
1301 $out .= '});'."\n";
1302 $out .= '</script>'."\n";
1303
1304 return $out;
1305 }
1306
1312 public function getHtmlForTo()
1313 {
1314 global $langs, $form;
1315 $out = '<tr><td class="fieldrequired">';
1316 if ($this->withtofree) {
1317 $out .= $form->textwithpicto($langs->trans("MailTo"), $langs->trans("YouCanUseCommaSeparatorForSeveralRecipients"));
1318 } else {
1319 $out .= $langs->trans("MailTo");
1320 }
1321 $out .= '</td><td>';
1322 if ($this->withtoreadonly) {
1323 if (!empty($this->toname) && !empty($this->tomail)) {
1324 $out .= '<input type="hidden" id="toname" name="toname" value="'.$this->toname.'" />';
1325 $out .= '<input type="hidden" id="tomail" name="tomail" value="'.$this->tomail.'" />';
1326 if ($this->totype == 'thirdparty') {
1327 $soc = new Societe($this->db);
1328 $soc->fetch($this->toid);
1329 $out .= $soc->getNomUrl(1);
1330 } elseif ($this->totype == 'contact') {
1331 $contact = new Contact($this->db);
1332 $contact->fetch($this->toid);
1333 $out .= $contact->getNomUrl(1);
1334 } else {
1335 $out .= $this->toname;
1336 }
1337 $out .= ' &lt;'.$this->tomail.'&gt;';
1338 if ($this->withtofree) {
1339 $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) : "").'" />';
1340 }
1341 } else {
1342 // Note withto may be a text like 'AllRecipientSelected'
1343 $out .= (!is_array($this->withto) && !is_numeric($this->withto)) ? $this->withto : "";
1344 }
1345 } else {
1346 // The free input of email
1347 if (!empty($this->withtofree)) {
1348 $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) : "")).'" />';
1349 }
1350 // The select combo
1351 if (!empty($this->withto) && is_array($this->withto)) {
1352 if (!empty($this->withtofree)) {
1353 $out .= ' <span class="opacitymedium">'.$langs->trans("and")."/".$langs->trans("or")."</span> ";
1354 }
1355
1356 $tmparray = $this->withto;
1357 foreach ($tmparray as $key => $val) {
1358 if (is_array($val)) {
1359 $label = $val['label'];
1360 } else {
1361 $label = $val;
1362 }
1363
1364 $tmparray[$key] = array();
1365 $tmparray[$key]['id'] = $key;
1366
1367 $tmparray[$key]['label'] = $label;
1368 $tmparray[$key]['label'] = str_replace(array('<', '>'), array('(', ')'), $tmparray[$key]['label']);
1369 // multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time
1370 $tmparray[$key]['label'] = dol_htmlentities($tmparray[$key]['label'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', true);
1371
1372 $tmparray[$key]['labelhtml'] = $label;
1373 $tmparray[$key]['labelhtml'] = str_replace(array('&lt;', '<', '&gt;', '>'), array('__LTCHAR__', '__LTCHAR__', '__GTCHAR__', '__GTCHAR__'), $tmparray[$key]['labelhtml']);
1374 $tmparray[$key]['labelhtml'] = str_replace(array('__LTCHAR__', '__GTCHAR__'), array('<span class="opacitymedium">(', ')</span>'), $tmparray[$key]['labelhtml']);
1375 }
1376
1377 $withtoselected = GETPOST("receiver", 'array'); // Array of selected value
1378 if (!getDolGlobalInt('MAIN_MAIL_NO_WITH_TO_SELECTED')) {
1379 if (empty($withtoselected) && count($tmparray) == 1 && GETPOST('action', 'aZ09') == 'presend') {
1380 $withtoselected = array_keys($tmparray);
1381 }
1382 }
1383
1384 $out .= $form->multiselectarray("receiver", $tmparray, $withtoselected, 0, 0, 'inline-block minwidth500', 0, 0);
1385 }
1386 }
1387 $out .= "</td></tr>\n";
1388 return $out;
1389 }
1390
1397 public function getHtmlForToNew()
1398 {
1399 global $langs, $form;
1400
1401 $out = '<tr><td class="fieldrequired">';
1402 if ($this->withtofree) {
1403 $out .= $form->textwithpicto($langs->trans("MailTo"), $langs->trans("YouCanUseFreeEmailsForRecipients"));
1404 } else {
1405 $out .= $langs->trans("MailTo");
1406 }
1407 $out .= '</td><td>';
1408
1409 if ($this->withtoreadonly) {
1410 if (!empty($this->toname) && !empty($this->tomail)) {
1411 $out .= '<input type="hidden" id="toname" name="toname" value="'.$this->toname.'" />';
1412 $out .= '<input type="hidden" id="tomail" name="tomail" value="'.$this->tomail.'" />';
1413 if ($this->totype == 'thirdparty') {
1414 $soc = new Societe($this->db);
1415 $soc->fetch($this->toid);
1416 $out .= $soc->getNomUrl(1);
1417 } elseif ($this->totype == 'contact') {
1418 $contact = new Contact($this->db);
1419 $contact->fetch($this->toid);
1420 $out .= $contact->getNomUrl(1);
1421 } else {
1422 $out .= $this->toname;
1423 }
1424 $out .= ' &lt;'.$this->tomail.'&gt;';
1425 if ($this->withtofree) {
1426 $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) : "").'" />';
1427 }
1428 } else {
1429 $out .= (!is_array($this->withto) && !is_numeric($this->withto)) ? $this->withto : "";
1430 }
1431 $out .= "</td></tr>\n";
1432 return $out;
1433 }
1434
1435 $tmparray = is_array($this->withto) ? $this->withto : array();
1436 if (!empty($this->withtofree)) {
1437 $keyval = (($this->withtofree && !is_numeric($this->withtofree)) ? $this->withtofree : (!is_array($this->withto) && !is_numeric($this->withto) ? (GETPOSTISSET("sendto") ? GETPOST("sendto") : $this->withto) : ""));
1438 if ($keyval !== '' && !array_key_exists($keyval, $tmparray)) {
1439 $tmparray[$keyval] = $keyval;
1440 }
1441 }
1442
1443 $withtoselected = GETPOST("receiver", 'array');
1444 if (!getDolGlobalInt('MAIN_MAIL_NO_WITH_TO_SELECTED')) {
1445 if (empty($withtoselected) && count($tmparray) == 1 && GETPOST('action', 'aZ09') == 'presend') {
1446 $withtoselected = array_keys($tmparray);
1447 }
1448 }
1449
1450 $out .= $this->getHtmlForFreetagRecipient('receiver', $tmparray, $withtoselected);
1451 $out .= "</td></tr>\n";
1452 return $out;
1453 }
1454
1460 public function getHtmlForCc()
1461 {
1462 global $langs, $form;
1463 $out = '<tr><td>';
1464 $out .= $form->textwithpicto($langs->trans("MailCC"), $langs->trans("YouCanUseCommaSeparatorForSeveralRecipients"));
1465 $out .= '</td><td>';
1466 if ($this->withtoccreadonly) {
1467 $out .= (!is_array($this->withtocc) && !is_numeric($this->withtocc)) ? $this->withtocc : "";
1468 } else {
1469 $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 : '')).'" />';
1470 if (!empty($this->withtocc) && is_array($this->withtocc)) {
1471 $out .= ' <span class="opacitymedium">'.$langs->trans("and")."/".$langs->trans("or")."</span> ";
1472
1473 $tmparray = $this->withtocc;
1474 foreach ($tmparray as $key => $val) {
1475 if (is_array($val)) {
1476 $label = $val['label'];
1477 } else {
1478 $label = $val;
1479 }
1480
1481 $tmparray[$key] = array();
1482 $tmparray[$key]['id'] = $key;
1483
1484 $tmparray[$key]['label'] = $label;
1485 $tmparray[$key]['label'] = str_replace(array('<', '>'), array('(', ')'), $tmparray[$key]['label']);
1486 // multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time
1487 $tmparray[$key]['label'] = dol_htmlentities($tmparray[$key]['label'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', true);
1488
1489 $tmparray[$key]['labelhtml'] = $label;
1490 $tmparray[$key]['labelhtml'] = str_replace(array('&lt;', '<', '&gt;', '>'), array('__LTCHAR__', '__LTCHAR__', '__GTCHAR__', '__GTCHAR__'), $tmparray[$key]['labelhtml']);
1491 $tmparray[$key]['labelhtml'] = str_replace(array('__LTCHAR__', '__GTCHAR__'), array('<span class="opacitymedium">(', ')</span>'), $tmparray[$key]['labelhtml']);
1492 }
1493
1494 $withtoccselected = GETPOST("receivercc", 'array'); // Array of selected value
1495
1496 $out .= $form->multiselectarray("receivercc", $tmparray, $withtoccselected, 0, 0, 'inline-block minwidth500', 0, 0);
1497 }
1498 }
1499 $out .= "</td></tr>\n";
1500 return $out;
1501 }
1502
1509 public function getHtmlForCcNew()
1510 {
1511 global $langs, $form;
1512
1513 $out = '<tr><td>';
1514 $out .= $form->textwithpicto($langs->trans("MailCC"), $langs->trans("YouCanUseFreeEmailsForRecipients"));
1515 $out .= '</td><td>';
1516
1517 if ($this->withtoccreadonly) {
1518 $out .= (!is_array($this->withtocc) && !is_numeric($this->withtocc)) ? $this->withtocc : "";
1519 $out .= "</td></tr>\n";
1520 return $out;
1521 }
1522
1523 $tmparray = is_array($this->withtocc) ? $this->withtocc : array();
1524 $keyval = (GETPOST("sendtocc", "alpha") ? GETPOST("sendtocc", "alpha") : ((!is_array($this->withtocc) && !is_numeric($this->withtocc)) ? $this->withtocc : ''));
1525 if ($keyval !== '' && !array_key_exists($keyval, $tmparray)) {
1526 $tmparray[$keyval] = $keyval;
1527 }
1528
1529 $withtoccselected = GETPOST("receivercc", 'array');
1530
1531 $out .= $this->getHtmlForFreetagRecipient('receivercc', $tmparray, $withtoccselected);
1532 $out .= "</td></tr>\n";
1533 return $out;
1534 }
1535
1542 public function getHtmlForWithCcc()
1543 {
1544 global $langs, $form;
1545
1546 $out = '<tr><td>';
1547 $out .= $form->textwithpicto($langs->trans("MailCCC"), $langs->trans("YouCanUseCommaSeparatorForSeveralRecipients"));
1548 $out .= '</td><td>';
1549 if (!empty($this->withtocccreadonly)) {
1550 $out .= (!is_array($this->withtoccc) && !is_numeric($this->withtoccc)) ? $this->withtoccc : "";
1551 } else {
1552 $out .= '<input class="minwidth200" id="sendtoccc" name="sendtoccc" value="'.(GETPOSTISSET("sendtoccc") ? GETPOST("sendtoccc", "alpha") : ((!is_array($this->withtoccc) && !is_numeric($this->withtoccc)) ? $this->withtoccc : '')).'" />';
1553 if (!empty($this->withtoccc) && is_array($this->withtoccc)) {
1554 $out .= ' <span class="opacitymedium">'.$langs->trans("and")."/".$langs->trans("or")."</span> ";
1555
1556 $tmparray = $this->withtoccc;
1557 foreach ($tmparray as $key => $val) {
1558 if (is_array($val)) {
1559 $label = $val['label'];
1560 } else {
1561 $label = $val;
1562 }
1563 $tmparray[$key] = array();
1564 $tmparray[$key]['id'] = $key;
1565
1566 $tmparray[$key]['label'] = $label;
1567 $tmparray[$key]['label'] = str_replace(array('<', '>'), array('(', ')'), $tmparray[$key]['label']);
1568 // multiselect array convert html entities into options tags, even if we don't want this, so we encode them a second time
1569 $tmparray[$key]['label'] = dol_htmlentities($tmparray[$key]['label'], ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8', true);
1570
1571 $tmparray[$key]['labelhtml'] = $label;
1572 $tmparray[$key]['labelhtml'] = str_replace(array('&lt;', '<', '&gt;', '>'), array('__LTCHAR__', '__LTCHAR__', '__GTCHAR__', '__GTCHAR__'), $tmparray[$key]['labelhtml']);
1573 $tmparray[$key]['labelhtml'] = str_replace(array('__LTCHAR__', '__GTCHAR__'), array('<span class="opacitymedium">(', ')</span>'), $tmparray[$key]['labelhtml']);
1574 }
1575
1576 $withtocccselected = GETPOST("receiverccc", 'array'); // Array of selected value
1577
1578 $out .= $form->multiselectarray("receiverccc", $tmparray, $withtocccselected, 0, 0, 'inline-block minwidth500', 0, 0);
1579 }
1580 }
1581
1582 $showinfobcc = '';
1583 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_PROPOSAL_TO') && !empty($this->param['models']) && $this->param['models'] == 'propal_send') {
1584 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_PROPOSAL_TO');
1585 }
1586 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_ORDER_TO') && !empty($this->param['models']) && $this->param['models'] == 'order_send') {
1587 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_ORDER_TO');
1588 }
1589 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_INVOICE_TO') && !empty($this->param['models']) && $this->param['models'] == 'facture_send') {
1590 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_INVOICE_TO');
1591 }
1592 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_PROPOSAL_TO') && !empty($this->param['models']) && $this->param['models'] == 'supplier_proposal_send') {
1593 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_PROPOSAL_TO');
1594 }
1595 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_ORDER_TO') && !empty($this->param['models']) && $this->param['models'] == 'order_supplier_send') {
1596 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_ORDER_TO');
1597 }
1598 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_INVOICE_TO') && !empty($this->param['models']) && $this->param['models'] == 'invoice_supplier_send') {
1599 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_INVOICE_TO');
1600 }
1601 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.
1602 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_PROJECT_TO');
1603 }
1604 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SHIPMENT_TO') && !empty($this->param['models']) && $this->param['models'] == 'shipping_send') {
1605 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SHIPMENT_TO');
1606 }
1607 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_RECEPTION_TO') && !empty($this->param['models']) && $this->param['models'] == 'reception_send') {
1608 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_RECEPTION_TO');
1609 }
1610 if ($showinfobcc) {
1611 $out .= ' + '.$showinfobcc;
1612 }
1613 $out .= "</td></tr>\n";
1614 return $out;
1615 }
1616
1624 public function getHtmlForWithCccNew()
1625 {
1626 global $langs, $form;
1627
1628 $out = '<tr><td>';
1629 $out .= $form->textwithpicto($langs->trans("MailCCC"), $langs->trans("YouCanUseFreeEmailsForRecipients"));
1630 $out .= '</td><td>';
1631
1632 if (!empty($this->withtocccreadonly)) {
1633 $out .= (!is_array($this->withtoccc) && !is_numeric($this->withtoccc)) ? $this->withtoccc : "";
1634 } else {
1635 $tmparray = is_array($this->withtoccc) ? $this->withtoccc : array();
1636 $keyval = (GETPOSTISSET("sendtoccc") ? GETPOST("sendtoccc", "alpha") : ((!is_array($this->withtoccc) && !is_numeric($this->withtoccc)) ? $this->withtoccc : ''));
1637 if ($keyval !== '' && !array_key_exists($keyval, $tmparray)) {
1638 $tmparray[$keyval] = $keyval;
1639 }
1640
1641 $withtocccselected = GETPOST("receiverccc", 'array');
1642
1643 $out .= $this->getHtmlForFreetagRecipient('receiverccc', $tmparray, $withtocccselected);
1644 }
1645
1646 $showinfobcc = '';
1647 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_PROPOSAL_TO') && !empty($this->param['models']) && $this->param['models'] == 'propal_send') {
1648 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_PROPOSAL_TO');
1649 }
1650 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_ORDER_TO') && !empty($this->param['models']) && $this->param['models'] == 'order_send') {
1651 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_ORDER_TO');
1652 }
1653 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_INVOICE_TO') && !empty($this->param['models']) && $this->param['models'] == 'facture_send') {
1654 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_INVOICE_TO');
1655 }
1656 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_PROPOSAL_TO') && !empty($this->param['models']) && $this->param['models'] == 'supplier_proposal_send') {
1657 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_PROPOSAL_TO');
1658 }
1659 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_ORDER_TO') && !empty($this->param['models']) && $this->param['models'] == 'order_supplier_send') {
1660 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_ORDER_TO');
1661 }
1662 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_INVOICE_TO') && !empty($this->param['models']) && $this->param['models'] == 'invoice_supplier_send') {
1663 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SUPPLIER_INVOICE_TO');
1664 }
1665 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.
1666 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_PROJECT_TO');
1667 }
1668 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_SHIPMENT_TO') && !empty($this->param['models']) && $this->param['models'] == 'shipping_send') {
1669 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_SHIPMENT_TO');
1670 }
1671 if (getDolGlobalString('MAIN_MAIL_AUTOCOPY_RECEPTION_TO') && !empty($this->param['models']) && $this->param['models'] == 'reception_send') {
1672 $showinfobcc = getDolGlobalString('MAIN_MAIL_AUTOCOPY_RECEPTION_TO');
1673 }
1674 if ($showinfobcc) {
1675 $out .= ' + '.$showinfobcc;
1676 }
1677
1678 $out .= "</td></tr>\n";
1679 return $out;
1680 }
1681
1687 public function getHtmlForWithErrorsTo()
1688 {
1689 global $langs;
1690
1691 //if (! $this->errorstomail) $this->errorstomail=$this->frommail;
1692 $errorstomail = getDolGlobalString('MAIN_MAIL_ERRORS_TO', (!empty($this->errorstomail) ? $this->errorstomail : ''));
1693 if ($this->witherrorstoreadonly) {
1694 $out = '<tr><td>'.$langs->trans("MailErrorsTo").'</td><td>';
1695 $out .= '<input type="hidden" id="errorstomail" name="errorstomail" value="'.$errorstomail.'" />';
1696 $out .= $errorstomail;
1697 $out .= "</td></tr>\n";
1698 } else {
1699 $out = '<tr><td>'.$langs->trans("MailErrorsTo").'</td><td>';
1700 $out .= '<input class="minwidth200" id="errorstomail" name="errorstomail" value="'.$errorstomail.'" />';
1701 $out .= "</td></tr>\n";
1702 }
1703 return $out;
1704 }
1705
1711 public function getHtmlForDeliveryreceipt()
1712 {
1713 global $langs;
1714
1715 $out = '<tr><td><label for="deliveryreceipt">'.$langs->trans("DeliveryReceipt").'</label></td><td>';
1716
1717 if (!empty($this->withdeliveryreceiptreadonly)) {
1718 $out .= yn($this->withdeliveryreceipt);
1719 } else {
1720 $defaultvaluefordeliveryreceipt = 0;
1721 if (getDolGlobalString('MAIL_FORCE_DELIVERY_RECEIPT_PROPAL') && !empty($this->param['models']) && $this->param['models'] == 'propal_send') {
1722 $defaultvaluefordeliveryreceipt = 1;
1723 }
1724 if (getDolGlobalString('MAIL_FORCE_DELIVERY_RECEIPT_SUPPLIER_PROPOSAL') && !empty($this->param['models']) && $this->param['models'] == 'supplier_proposal_send') {
1725 $defaultvaluefordeliveryreceipt = 1;
1726 }
1727 if (getDolGlobalString('MAIL_FORCE_DELIVERY_RECEIPT_ORDER') && !empty($this->param['models']) && $this->param['models'] == 'order_send') {
1728 $defaultvaluefordeliveryreceipt = 1;
1729 }
1730 if (getDolGlobalString('MAIL_FORCE_DELIVERY_RECEIPT_INVOICE') && !empty($this->param['models']) && $this->param['models'] == 'facture_send') {
1731 $defaultvaluefordeliveryreceipt = 1;
1732 }
1733 if (getDolGlobalString('MAIL_FORCE_DELIVERY_RECEIPT_SUPPLIER_ORDER') && !empty($this->param['models']) && $this->param['models'] == 'order_supplier_send') {
1734 $defaultvaluefordeliveryreceipt = 1;
1735 }
1736 //$out .= $form->selectyesno('deliveryreceipt', (GETPOSTISSET("deliveryreceipt") ? GETPOST("deliveryreceipt") : $defaultvaluefordeliveryreceipt), 1);
1737 $out .= '<input type="checkbox" id="deliveryreceipt" name="deliveryreceipt" value="1"'.((GETPOSTISSET("deliveryreceipt") ? GETPOST("deliveryreceipt") : $defaultvaluefordeliveryreceipt) ? ' checked="checked"' : '').'>';
1738 }
1739 $out .= "</td></tr>\n";
1740 return $out;
1741 }
1742
1750 public function getHtmlForTopic($arraydefaultmessage, $helpforsubstitution)
1751 {
1752 global $conf, $langs, $form;
1753
1754 $defaulttopic = GETPOST('subject', 'restricthtml');
1755
1756 if (!GETPOST('modelselected', 'alpha') || GETPOST('modelmailselected') != '-1') {
1757 if ($arraydefaultmessage && $arraydefaultmessage->topic) {
1758 $defaulttopic = $arraydefaultmessage->topic;
1759 } elseif (!is_numeric($this->withtopic)) {
1760 $defaulttopic = $this->withtopic;
1761 }
1762 }
1763
1764 // Resolve __(TranslationKey)__ in the language of the selected template
1765 // (see issue #34540). Falls back to the caller's language when the template
1766 // has no explicit language pinned.
1767 $outputlangs = $langs;
1768 if (is_object($arraydefaultmessage) && !empty($arraydefaultmessage->lang)) {
1769 $outputlangs = new Translate("", $conf);
1770 $outputlangs->setDefaultLang($arraydefaultmessage->lang);
1771 $outputlangs->load('other');
1772 }
1773
1774 $defaulttopic = make_substitutions($defaulttopic, $this->substit, $outputlangs);
1775
1776 $out = '<tr>';
1777 $out .= '<td class="fieldrequired">';
1778 $out .= $form->textwithpicto($langs->trans('MailTopicShort'), $helpforsubstitution, 1, 'help', '', 0, 2, 'substittooltipfromtopic');
1779 $out .= '</td>';
1780 $out .= '<td>';
1781 if ($this->withtopicreadonly) {
1782 $out .= $defaulttopic;
1783 $out .= '<input type="hidden" class="quatrevingtpercent" id="subject" name="subject" value="'.$defaulttopic.'" spellcheck="false">';
1784 } else {
1785 $out .= '<input type="text" class="quatrevingtpercent" id="subject" name="subject" value="'.((GETPOSTISSET("subject") && !GETPOST('modelselected')) ? GETPOST("subject") : ($defaulttopic ? $defaulttopic : '')).'" spellcheck="false">';
1786 }
1787 $out .= "</td></tr>\n";
1788 return $out;
1789 }
1790
1799 public function getEmailLayoutSelector($htmlContent = 'message', $showlinktolayout = 'email')
1800 {
1801 global $conf, $db, $websitepage, $langs;
1802
1803 require_once DOL_DOCUMENT_ROOT.'/core/lib/emaillayout.lib.php';
1804 require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
1805 require_once DOL_DOCUMENT_ROOT.'/website/class/website.class.php';
1806 require_once DOL_DOCUMENT_ROOT.'/website/class/websitepage.class.php';
1807
1808 $out = '<div id="template-selector" class="template-selector email-layout-container hidden" style="display:none;">';
1809 $out .= '<div>';
1810
1811 // Define list of email layouts to use
1812 $layouts = array(
1813 'none' => 'None',
1814 );
1815 // Add layouts found on disk in install/doctemplates/maillayout directory
1816 $arrayoflayoutemplates = dol_dir_list(DOL_DOCUMENT_ROOT.'/install/doctemplates/maillayout/', 'files', 0, '\.html$');
1817 foreach ($arrayoflayoutemplates as $layouttemplatefile) {
1818 $layoutname = preg_replace('/\.html$/i', '', $layouttemplatefile['name']);
1819
1820 // Exclude some layouts for some use cases
1821 if ($layoutname == 'news' && (!in_array($showlinktolayout, array('emailing', 'websitepage')) || !isModEnabled('website'))) {
1822 continue;
1823 }
1824 if ($layoutname == 'product' && (!in_array($showlinktolayout, array('emailing', 'websitepage')) || (!isModEnabled('product') && !isModEnabled('service')))) {
1825 continue;
1826 }
1827
1828 $layouts[$layoutname] = ucfirst($layoutname);
1829 }
1830 //}
1831 // TODO Add a hook to allow to complete the list
1832 foreach ($layouts as $layout => $templateFunction) {
1833 $contentHtml = getHtmlOfLayout($layout);
1834
1835 $out .= '<div class="template-option" data-template="'.$layout.'" data-content="'.htmlentities($contentHtml).'">';
1836 $out .= '<img class="maillayout" alt="'.$layout.'" src="'.DOL_URL_ROOT.'/theme/common/maillayout/'.$layout.'.png" />';
1837 $out .= '<span class="template-option-text">'.$langs->trans($templateFunction).'</span>';
1838 $out .= '</div>';
1839 }
1840 $out .= '</div>';
1841
1842 // Prepare the array for multiselect
1843
1844 // Fetch blogs
1845 $blogArray = array();
1846 if (isModEnabled('website')) {
1847 $websitepage = new WebsitePage($this->db);
1848 $arrayofblogs = $websitepage->fetchAll('', 'ASC,DESC', 'fk_website,date_creation', 0, 0, array('type_container' => 'blogpost'));
1849
1850 if (empty($conf->cache['websiteurl'])) {
1851 $conf->cache['websiteurl'] = array();
1852 }
1853
1854 if (!empty($arrayofblogs)) {
1855 foreach ($arrayofblogs as $blog) {
1856 if (!isset($conf->cache['websiteurl'][$blog->id])) {
1857 $tmpwebsite = new Website($db);
1858 $tmpwebsite->fetch($blog->fk_website);
1859 $conf->cache['websiteurl'][$blog->fk_website] = (empty($tmpwebsite->virtualhost) ? $tmpwebsite->ref : $tmpwebsite->virtualhost);
1860 }
1861
1862 $labelwebsite = $conf->cache['websiteurl'][$blog->fk_website];
1863 //$blog->fk_website
1864
1865 $blogArray[$blog->id] = array(
1866 'id' => $blog->id,
1867 'label' => '['.$labelwebsite.' '.$blog->type_container.' '.$blog->id.'] '.dol_trunc($blog->title, 40),
1868 'labelhtml' => '<span class="opacitymedium">['.$labelwebsite.' '.$blog->type_container.' '.$blog->id.']</span> '.dol_trunc($blog->title, 40),
1869 );
1870 }
1871 }
1872 }
1873
1874 // Fetch Product / Services
1875 /* to use with multiselectarray but consume too much memory so replaced
1876 if (in_array('product', array_keys($layouts))) {
1877 $productArray = array();
1878 if (isModEnabled('product') || isModEnabled('service')) {
1879 include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
1880 $form = new Form($this->db);
1881 $arrayofproduct = $form->select_produits_list(0, 'product-select', '', 0, 0, '', 1, 2, 1);
1882 if (!empty($arrayofproduct)) {
1883 foreach ($arrayofproduct as $product) {
1884 $productArray[$product["key"]] = array(
1885 'id' => $product["key"],
1886 'label' => $product["value"].' - '.dol_trunc($product["label2"], 40),
1887 'labelhtml' => $product["value"].' - '.dol_trunc($product["label2"], 40),
1888 );
1889 }
1890 }
1891 }
1892 }
1893 */
1894
1895 // Use the multiselect array function to create the dropdown
1896 if (in_array('news', array_keys($layouts)) && (isModEnabled('product') || isModEnabled('service'))) {
1897 $out .= '<div id="post-dropdown-container" class="email-layout-container hidden" style="margin-top: 8px; display:none;">';
1898 $out .= '<label for="blogpost-select">Select Posts: </label>';
1899 $out .= '<!-- select component for selection of blog posts -->'."\n";
1900 // TODO WARNING: multiselectarray is ok only for very small list
1901 $out .= self::multiselectarray('blogpost-select', $blogArray, array(), 0, 0, 'minwidth200 select-template');
1902 $out .= ' <input type="submit" class="smallpaddingimp button reposition" name="submit" id="post-submit" value="'.dolPrintHTMLForAttribute($langs->trans("Select")).'">';
1903 $out .= '</div>';
1904 }
1905 if (in_array('product', array_keys($layouts)) && (isModEnabled('product') || isModEnabled('service'))) {
1906 include_once DOL_DOCUMENT_ROOT.'/core/class/html.form.class.php';
1907 $form = new Form($this->db);
1908 $out .= '<div id="product-dropdown-container" class="email-layout-container hidden" style="margin-top: 8px; display:none;">';
1909 $out .= '<label for="product-select">'.img_picto('', 'product', 'class="pictofixedwidth"').$langs->trans("Product").' : </label>';
1910 $out .= '<!-- select component for selection of product -->'."\n";
1911 $out .= $form->select_produits(0, 'product-select', '', 0, 0, -1, 2, '', 0, array(), 0, '1', 0, 'inline-block valignmiddle', 0, '', null, 1);
1912 // TODO multiselectarray is ok only for very small list but is ok for multiselect. We need a multiselect ok with ajax for long list
1913 //$out .= self::multiselectarray('product-select', $productArray, array(), 0, 0, 'minwidth200 select-template');
1914 $out .= ' <input type="submit" class="smallpaddingimp button reposition" name="submit" id="product-submit" value="'.dolPrintHTMLForAttribute($langs->trans("Select")).'">';
1915 $out .= '</div>';
1916 }
1917
1918 $out .= '</div>';
1919
1920 $out .= '<!-- Js code to manage choice of an email layout -->'."\n";
1921 $out .= '<script type="text/javascript">
1922 $(document).ready(function() {
1923 $(".template-option").click(function() {
1924 var template = $(this).data("template");
1925 var subject = jQuery("#subject").val();
1926 var fromtype = jQuery("#fromtype").val();
1927 var sendto = jQuery("#sendto").val();
1928 var sendtocc = jQuery("#sendtocc").val();
1929 var sendtoccc = jQuery("#sendtoccc").val();
1930
1931 console.log("We choose a layout for email template=" + template + ", subject="+subject);
1932
1933 $(".template-option").removeClass("selected");
1934 $(this).addClass("selected");
1935 $(".select-template").val("").trigger("change");
1936
1937 if (template === "news") {
1938 $("#post-dropdown-container").show();
1939 $("#product-dropdown-container").hide();
1940 console.log("Displaying dropdown for news selection");
1941 } else if (template === "product") {
1942 $("#product-dropdown-container").show();
1943 $("#post-dropdown-container").hide();
1944 console.log("Displaying dropdown for product selection");
1945 } else {
1946 $("#post-dropdown-container").hide();
1947 $("#product-dropdown-container").hide();
1948 }
1949
1950 var csrfToken = "' .newToken().'";
1951 $.ajax({
1952 type: "POST",
1953 url: "'.DOL_URL_ROOT.'/core/ajax/mailtemplate.php",
1954 data: {
1955 token: csrfToken,
1956 template: template,
1957 subject: subject,
1958 fromtype: fromtype,
1959 sendto: sendto,
1960 sendtocc: sendtocc,
1961 sendtoccc: sendtoccc,
1962 selectedPosts: "[]"
1963 },
1964 success: function(response) {
1965 jQuery("#'.$htmlContent.'").val(response);
1966 var editorInstance = CKEDITOR.instances["'.$htmlContent.'"];
1967 if (editorInstance) {
1968 editorInstance.setData(response);
1969 }
1970 },
1971 error: function(xhr, status, error) {
1972 console.error("An error occurred: " + xhr.responseText);
1973 }
1974 });
1975 });
1976
1977 $("#blogpost-select").change(function() {
1978 var selectedIds = $(this).val();
1979 var contentHtml = $(".template-option.selected").data("content");
1980
1981 updateSelectedPostsContent(contentHtml, selectedIds);
1982 });
1983 $("#product-select").change(function() {
1984 var selectedIds = $(this).val();
1985 var contentHtml = $(".template-option.selected").data("content");
1986
1987 updateSelectedPostsContent(contentHtml, selectedIds);
1988 });
1989
1990 function updateSelectedPostsContent(contentHtml, selectedIds) {
1991 var csrfToken = "' .newToken().'";
1992 template = $(".template-option.selected").data("template");
1993 var subject = $("#subject").val();
1994 $.ajax({
1995 type: "POST",
1996 url: "'.dol_buildpath('/core/ajax/mailtemplate.php', 1).'",
1997 data: {
1998 token: csrfToken,
1999 template: template,
2000 subject: subject,
2001 selectedPosts: JSON.stringify(selectedIds)
2002 },
2003 success: function(response) {
2004 jQuery("#'.$htmlContent.'").val(response);
2005 var editorInstance = CKEDITOR.instances["'.$htmlContent.'"];
2006 if (editorInstance) {
2007 editorInstance.setData(response);
2008 }
2009 },
2010 error: function(xhr, status, error) {
2011 console.error("An error occurred: " + xhr.responseText);
2012 }
2013 });
2014
2015 }
2016 });
2017 </script>';
2018
2019 return $out;
2020 }
2021
2039 public function getEMailTemplate($dbs, $type_template, $user, $outputlangs, $id = 0, $active = 1, $label = '', $defaultfortype = -1)
2040 {
2041 global $conf;
2042
2043 if ($id == -2 && empty($label)) {
2044 $this->error = 'LabelIsMandatoryWhenIdIs-2or-3';
2045 return -1;
2046 }
2047 if ($type_template === 'societe') {
2048 $type_template = 'thirdparty';
2049 } elseif ($type_template === 'action') { // Object type used by notifications for an event of the agenda
2050 $type_template = 'actioncomm';
2051 }
2052 $ret = new CEmailTemplate($dbs);
2053
2054 $languagetosearch = (is_object($outputlangs) ? $outputlangs->defaultlang : '');
2055 // Define $languagetosearchmain to fall back on main language (for example to get 'es_ES' for 'es_MX')
2056 $tmparray = explode('_', $languagetosearch);
2057 $languagetosearchmain = $tmparray[0].'_'.strtoupper($tmparray[0]);
2058 if ($languagetosearchmain == $languagetosearch) {
2059 $languagetosearchmain = '';
2060 }
2061
2062 $sql = "SELECT rowid, entity, module, label, type_template, topic, email_from, joinfiles, content, content_lines, lang, email_from, email_to, email_tocc, email_tobcc";
2063 $sql .= " FROM ".$dbs->prefix().'c_email_templates';
2064 $sql .= " WHERE (type_template = '".$dbs->escape($type_template)."' OR type_template = '".$dbs->escape($type_template)."_send' OR type_template = 'all')";
2065 $sql .= " AND entity IN (".getEntity('c_email_templates').")";
2066 $sql .= " AND (private = 0 OR fk_user = ".((int) $user->id).")"; // Get all public or private owned
2067 if ($active >= 0) {
2068 $sql .= " AND active = ".((int) $active);
2069 }
2070 if ($defaultfortype >= 0) {
2071 $sql .= " AND defaultfortype = ".((int) $defaultfortype);
2072 }
2073 if ($label) {
2074 $sql .= " AND label = '".$dbs->escape($label)."'";
2075 }
2076 if (!($id > 0) && $languagetosearch) {
2077 $sql .= " AND (lang = '".$dbs->escape($languagetosearch)."'".($languagetosearchmain ? " OR lang = '".$dbs->escape($languagetosearchmain)."'" : "")." OR lang IS NULL OR lang = '')";
2078 }
2079 if ($id > 0) {
2080 $sql .= " AND rowid = ".(int) $id;
2081 }
2082 if ($id == -1) {
2083 $sql .= " AND position = 0";
2084 }
2085 $sql .= " AND entity IN(".getEntity('c_email_templates', 1).")";
2086 if ($languagetosearch) {
2087 $sql .= $dbs->order("position,lang,label", "ASC,DESC,ASC"); // We want line with lang set first, then with lang null or ''
2088 } else {
2089 $sql .= $dbs->order("position,lang,label", "ASC,ASC,ASC"); // If no language provided, we give priority to lang not defined
2090 }
2091 //$sql .= $dbs->plimit(1);
2092 //print $sql;
2093
2094 $resql = $dbs->query($sql);
2095 if (!$resql) {
2096 dol_print_error($dbs);
2097 return -1;
2098 }
2099
2100 // Get first found
2101 while (1) {
2102 $obj = $dbs->fetch_object($resql);
2103
2104 if ($obj) {
2105 // If template is for a module, check module is enabled; if not, take next template
2106 if ($obj->module) {
2107 $tempmodulekey = $obj->module;
2108 if (empty($conf->$tempmodulekey) || !isModEnabled($tempmodulekey)) {
2109 continue;
2110 }
2111 }
2112
2113 // If a record was found
2114 $ret->id = (int) $obj->rowid;
2115 $ret->module = (string) $obj->module;
2116 $ret->label = (string) $obj->label;
2117 $ret->lang = $obj->lang;
2118 $ret->topic = $obj->topic;
2119 $ret->content = (string) $obj->content;
2120 $ret->content_lines = (string) $obj->content_lines;
2121 $ret->joinfiles = $obj->joinfiles;
2122 $ret->email_from = (string) $obj->email_from;
2123 $ret->email_tocc = (string) $obj->email_tocc;
2124 $ret->email_tobcc = (string) $obj->email_tobcc;
2125
2126 break;
2127 } else {
2128 // If no record found
2129 if ($id == -2) {
2130 // Not found with the provided label
2131 return -1;
2132 } else {
2133 // If there is no template at all
2134 $defaultmessage = '';
2135
2136 if ($type_template == 'body') {
2137 // Special case to use this->withbody as content
2138 $defaultmessage = (string) $this->withbody;
2139 } elseif ($type_template == 'facture_send' || $type_template == 'facture' || $type_template == 'facture_relance') {
2140 $defaultmessage = $outputlangs->transnoentitiesnoconv("PredefinedMailContentSendInvoice");
2141 } elseif ($type_template == 'propal_send' || $type_template == 'propal') {
2142 $defaultmessage = $outputlangs->transnoentitiesnoconv("PredefinedMailContentSendProposal");
2143 } elseif ($type_template == 'supplier_proposal_send' || $type_template == 'supplier_proposal') {
2144 $defaultmessage = $outputlangs->transnoentitiesnoconv("PredefinedMailContentSendSupplierProposal");
2145 } elseif ($type_template == 'order_send' || $type_template == 'order') {
2146 $defaultmessage = $outputlangs->transnoentitiesnoconv("PredefinedMailContentSendOrder");
2147 } elseif ($type_template == 'order_supplier_send' || $type_template == 'order_supplier') {
2148 $defaultmessage = $outputlangs->transnoentitiesnoconv("PredefinedMailContentSendSupplierOrder");
2149 } elseif ($type_template == 'invoice_supplier_send' || $type_template == 'invoice_supplier') {
2150 $defaultmessage = $outputlangs->transnoentitiesnoconv("PredefinedMailContentSendSupplierInvoice");
2151 } elseif ($type_template == 'shipping_send' || $type_template == 'shipping') {
2152 $defaultmessage = $outputlangs->transnoentitiesnoconv("PredefinedMailContentSendShipping");
2153 } elseif ($type_template == 'reception_send' || $type_template == 'reception') {
2154 $defaultmessage = $outputlangs->transnoentitiesnoconv("PredefinedMailContentSendReception");
2155 } elseif ($type_template == 'fichinter_send' || $type_template == 'fichinter') {
2156 $defaultmessage = $outputlangs->transnoentitiesnoconv("PredefinedMailContentSendFichInter");
2157 } elseif ($type_template == 'actioncomm_send' || $type_template == 'actioncomm') {
2158 $defaultmessage = $outputlangs->transnoentitiesnoconv("PredefinedMailContentSendActionComm");
2159 } elseif (!empty($type_template)) {
2160 $defaultmessage = $outputlangs->transnoentitiesnoconv("PredefinedMailContentGeneric");
2161 }
2162
2163 $ret->label = 'default';
2164 $ret->lang = $outputlangs->defaultlang;
2165 $ret->topic = '';
2166 $ret->joinfiles = 1;
2167 $ret->content = $defaultmessage;
2168 $ret->content_lines = '';
2169
2170 break;
2171 }
2172 }
2173 }
2174
2175 $dbs->free($resql);
2176
2177 return $ret;
2178 }
2179
2189 public function isEMailTemplate($type_template, $user, $outputlangs)
2190 {
2191 $sql = "SELECT label, topic, content, lang";
2192 $sql .= " FROM ".$this->db->prefix().'c_email_templates';
2193 $sql .= " WHERE type_template='".$this->db->escape($type_template)."'";
2194 $sql .= " AND entity IN (".getEntity('c_email_templates').")";
2195 $sql .= " AND (fk_user is NULL or fk_user = 0 or fk_user = ".((int) $user->id).")";
2196 if (is_object($outputlangs)) {
2197 $sql .= " AND (lang = '".$this->db->escape($outputlangs->defaultlang)."' OR lang IS NULL OR lang = '')";
2198 }
2199 $sql .= $this->db->order("lang,label", "ASC");
2200 //print $sql;
2201
2202 $resql = $this->db->query($sql);
2203 if ($resql) {
2204 $num = $this->db->num_rows($resql);
2205 $this->db->free($resql);
2206 return $num;
2207 } else {
2208 $this->error = get_class($this).' '.__METHOD__.' ERROR:'.$this->db->lasterror();
2209 return -1;
2210 }
2211 }
2212
2223 public function fetchAllEMailTemplate($type_template, $user, $outputlangs, $active = 1)
2224 {
2225 global $db, $conf;
2226
2227 $sql = "SELECT rowid, module, label, topic, content, content_lines, lang, fk_user, private, position";
2228 $sql .= " FROM ".$this->db->prefix().'c_email_templates';
2229 $sql .= " WHERE type_template IN ('".$this->db->escape($type_template)."', 'all')";
2230 $sql .= " AND entity IN (".getEntity('c_email_templates').")";
2231 $sql .= " AND (private = 0 OR fk_user = ".((int) $user->id).")"; // See all public templates or templates I own.
2232 if ($active >= 0) {
2233 $sql .= " AND active = ".((int) $active);
2234 }
2235 //if (is_object($outputlangs)) $sql.= " AND (lang = '".$this->db->escape($outputlangs->defaultlang)."' OR lang IS NULL OR lang = '')"; // Return all languages
2236 $sql .= $this->db->order("lang,position,label", "ASC");
2237 //print $sql;
2238
2239 $resql = $this->db->query($sql);
2240 if ($resql) {
2241 $num = $this->db->num_rows($resql);
2242 $this->lines_model = array();
2243 while ($obj = $this->db->fetch_object($resql)) {
2244 // If template is for a module, check module is enabled.
2245 if ($obj->module) {
2246 $tempmodulekey = $obj->module;
2247 if (empty($conf->$tempmodulekey) || !isModEnabled($tempmodulekey)) {
2248 continue;
2249 }
2250 }
2251
2252 $line = new CEmailTemplate($db);
2253 $line->id = (int) $obj->rowid;
2254 $line->label = (string) $obj->label;
2255 $line->lang = $obj->lang;
2256 $line->fk_user = $obj->fk_user;
2257 $line->private = $obj->private;
2258 $line->position = $obj->position;
2259 $line->topic = $obj->topic;
2260 $line->content = $obj->content;
2261 $line->content_lines = $obj->content_lines;
2262
2263 $this->lines_model[] = $line;
2264 }
2265 $this->db->free($resql);
2266 return $num;
2267 } else {
2268 $this->error = get_class($this).' '.__METHOD__.' ERROR:'.$this->db->lasterror();
2269 return -1;
2270 }
2271 }
2272
2278 private static function normalizeTextForComparison($value)
2279 {
2280 $value = dol_string_nohtmltag((string) $value);
2281 $value = preg_replace('/\s+/', ' ', $value);
2282 return trim((string) $value);
2283 }
2284
2290 private static function getLangPrefix($langcode)
2291 {
2292 $langcode = trim((string) $langcode);
2293 if ($langcode === '') {
2294 return '';
2295 }
2296
2297 $prefix = preg_replace('/[_-].*$/', '', $langcode);
2298 $prefix = strtolower((string) $prefix);
2299 return preg_replace('/[^a-z]/', '', $prefix);
2300 }
2301
2309 private static function getBestProductTranslation($multilangs, $langcode)
2310 {
2311 $langcode = trim((string) $langcode);
2312 if ($langcode === '' || !is_array($multilangs) || empty($multilangs)) {
2313 return array('label' => '', 'description' => '');
2314 }
2315
2316 $prefix = self::getLangPrefix($langcode);
2317 $candidates = array($langcode);
2318 if ($prefix !== '' && $prefix !== $langcode) {
2319 $candidates[] = $prefix;
2320 }
2321
2322 foreach ($candidates as $candidate) {
2323 if (empty($multilangs[$candidate]) || !is_array($multilangs[$candidate])) {
2324 continue;
2325 }
2326 $label = trim((string) (isset($multilangs[$candidate]['label']) ? $multilangs[$candidate]['label'] : ''));
2327 $description = trim((string) (isset($multilangs[$candidate]['description']) ? $multilangs[$candidate]['description'] : ''));
2328 if ($label !== '' || $description !== '') {
2329 return array('label' => $label, 'description' => $description);
2330 }
2331 }
2332
2333 if ($prefix !== '') {
2334 foreach ($multilangs as $code => $row) {
2335 if (!is_array($row)) {
2336 continue;
2337 }
2338 if (!(strpos($code, $prefix.'_') === 0 || strpos($code, $prefix.'-') === 0)) {
2339 continue;
2340 }
2341 $label = trim((string) (isset($row['label']) ? $row['label'] : ''));
2342 $description = trim((string) (isset($row['description']) ? $row['description'] : ''));
2343 if ($label !== '' || $description !== '') {
2344 return array('label' => $label, 'description' => $description);
2345 }
2346 }
2347 }
2348
2349 return array('label' => '', 'description' => '');
2350 }
2351
2359 public function setSubstitFromObject($object, $outputlangs)
2360 {
2361 global $extrafields;
2362
2363 $parameters = array();
2364 $tmparray = getCommonSubstitutionArray($outputlangs, 0, null, $object);
2365 complete_substitutions_array($tmparray, $outputlangs, $object, $parameters);
2366
2367 $this->substit = $tmparray;
2368 $targetLang = '';
2369 if (is_object($outputlangs) && !empty($outputlangs->defaultlang)) {
2370 $targetLang = trim((string) $outputlangs->defaultlang);
2371 }
2372
2373 // Fill substit_lines with each object lines content
2374 if (is_array($object->lines)) {
2375 foreach ($object->lines as $line) {
2376 $substit_line = array(
2377 '__PRODUCT_REF__' => isset($line->product_ref) ? $line->product_ref : '',
2378 '__PRODUCT_LABEL__' => isset($line->product_label) ? $line->product_label : '',
2379 '__PRODUCT_DESCRIPTION__' => isset($line->product_desc) ? $line->product_desc : '',
2380 '__LABEL__' => isset($line->label) ? $line->label : '',
2381 '__DESCRIPTION__' => isset($line->desc) ? $line->desc : '',
2382 '__DATE_START_YMD__' => dol_print_date($line->date_start, 'day', false, $outputlangs),
2383 '__DATE_END_YMD__' => dol_print_date($line->date_end, 'day', false, $outputlangs),
2384 '__QUANTITY__' => $line->qty,
2385 '__SUBPRICE__' => price($line->subprice),
2386 '__AMOUNT__' => price($line->total_ttc),
2387 '__AMOUNT_EXCL_TAX__' => price($line->total_ht)
2388 );
2389
2390 // Create dynamic tags for __PRODUCT_EXTRAFIELD_FIELD__
2391 if (!empty($line->fk_product)) {
2392 if (!is_object($extrafields)) {
2393 $extrafields = new ExtraFields($this->db);
2394 }
2395 $product = new Product($this->db);
2396 $product->fetch($line->fk_product, '', '', '1');
2397 $product->fetch_optionals();
2398
2399 if (getDolGlobalInt('MAIN_MULTILANGS') && $targetLang !== '' && !empty($product->multilangs) && is_array($product->multilangs)) {
2400 $translated = self::getBestProductTranslation($product->multilangs, $targetLang);
2401 $translatedLabel = trim((string) (isset($translated['label']) ? $translated['label'] : ''));
2402 $translatedDescription = trim((string) (isset($translated['description']) ? $translated['description'] : ''));
2403
2404 $currentLabelNorm = self::normalizeTextForComparison($substit_line['__PRODUCT_LABEL__']);
2405 $currentProductDescriptionNorm = self::normalizeTextForComparison($substit_line['__PRODUCT_DESCRIPTION__']);
2406 $currentLineDescriptionNorm = self::normalizeTextForComparison($substit_line['__DESCRIPTION__']);
2407 $productLabelNorm = self::normalizeTextForComparison($product->label);
2408 $productDescriptionNorm = self::normalizeTextForComparison($product->description);
2409
2410 if ($translatedLabel !== '' && ($currentLabelNorm === '' || $currentLabelNorm === $productLabelNorm)) {
2411 $substit_line['__PRODUCT_LABEL__'] = $translatedLabel;
2412 }
2413 if ($translatedDescription !== '' && ($currentProductDescriptionNorm === '' || $currentProductDescriptionNorm === $productDescriptionNorm)) {
2414 $substit_line['__PRODUCT_DESCRIPTION__'] = $translatedDescription;
2415 }
2416 if ($translatedDescription !== '' && ($currentLineDescriptionNorm === '' || $currentLineDescriptionNorm === $productDescriptionNorm || $currentLineDescriptionNorm === $currentProductDescriptionNorm)) {
2417 $substit_line['__DESCRIPTION__'] = $translatedDescription;
2418 }
2419 }
2420
2421 $extrafields->fetch_name_optionals_label($product->table_element, true);
2422
2423 if (!empty($extrafields->attributes[$product->table_element]['label']) && is_array($extrafields->attributes[$product->table_element]['label']) && count($extrafields->attributes[$product->table_element]['label']) > 0) {
2424 foreach ($extrafields->attributes[$product->table_element]['label'] as $key => $label) {
2425 $substit_line['__PRODUCT_EXTRAFIELD_'.strtoupper($key).'__'] = isset($product->array_options['options_'.$key]) ? $product->array_options['options_'.$key] : '';
2426 }
2427 }
2428 }
2429
2430 $this->substit_lines[$line->id] = $substit_line; // @phan-suppress-current-line PhanTypeMismatchProperty
2431 }
2432 }
2433 }
2434
2442 public static function getAvailableSubstitKey($mode = 'formemail', $object = null)
2443 {
2444 global $langs;
2445
2446 $tmparray = array();
2447 if ($mode == 'formemail' || $mode == 'formemailwithlines' || $mode == 'formemailforlines') {
2448 $parameters = array('mode' => $mode);
2449 $tmparray = getCommonSubstitutionArray($langs, 2, null, $object); // Note: On email template creation, this may be null because it is related to all type of objects
2450 complete_substitutions_array($tmparray, $langs, null, $parameters);
2451
2452 if ($mode == 'formemailwithlines') {
2453 $tmparray['__LINES__'] = '__LINES__'; // Will be set by the get_form function
2454 }
2455 if ($mode == 'formemailforlines') {
2456 $tmparray['__QUANTITY__'] = '__QUANTITY__'; // Will be set by the get_form function
2457 }
2458 }
2459
2460 if ($mode == 'emailing') {
2461 $parameters = array('mode' => $mode);
2462 $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
2463 complete_substitutions_array($tmparray, $langs, null, $parameters);
2464
2465 // For mass emailing, we have different keys specific to the data into tagerts list
2466 $tmparray['__ID__'] = 'IdRecord';
2467 $tmparray['__EMAIL__'] = 'EMailRecipient';
2468 $tmparray['__LASTNAME__'] = 'Lastname';
2469 $tmparray['__FIRSTNAME__'] = 'Firstname';
2470 $tmparray['__MAILTOEMAIL__'] = 'TagMailtoEmail';
2471 $tmparray['__OTHER1__'] = 'Other1';
2472 $tmparray['__OTHER2__'] = 'Other2';
2473 $tmparray['__OTHER3__'] = 'Other3';
2474 $tmparray['__OTHER4__'] = 'Other4';
2475 $tmparray['__OTHER5__'] = 'Other5';
2476
2477 $tmparray['__THIRDPARTY_CUSTOMER_CODE__'] = 'CustomerCode'; // If source is a thirdparty
2478
2479 $tmparray['__CHECK_READ__'] = $langs->trans('TagCheckMail');
2480 $tmparray['__UNSUBSCRIBE__'] = $langs->trans('TagUnsubscribe');
2481 $tmparray['__UNSUBSCRIBE_URL__'] = $langs->trans('TagUnsubscribe').' (URL)';
2482
2483 $onlinepaymentenabled = 0;
2484 if (isModEnabled('paypal')) {
2485 $onlinepaymentenabled++;
2486 }
2487 if (isModEnabled('stripe')) {
2488 $onlinepaymentenabled++;
2489 }
2490 if ($onlinepaymentenabled && getDolGlobalString('PAYMENT_SECURITY_TOKEN')) {
2491 $tmparray['__SECUREKEYPAYMENT__'] = getDolGlobalString('PAYMENT_SECURITY_TOKEN');
2492 if (isModEnabled('member')) {
2493 $tmparray['__SECUREKEYPAYMENT_MEMBER__'] = 'SecureKeyPAYMENTUniquePerMember';
2494 }
2495 if (isModEnabled('don')) {
2496 $tmparray['__SECUREKEYPAYMENT_DONATION__'] = 'SecureKeyPAYMENTUniquePerDonation';
2497 }
2498 if (isModEnabled('invoice')) {
2499 $tmparray['__SECUREKEYPAYMENT_INVOICE__'] = 'SecureKeyPAYMENTUniquePerInvoice';
2500 }
2501 if (isModEnabled('order')) {
2502 $tmparray['__SECUREKEYPAYMENT_ORDER__'] = 'SecureKeyPAYMENTUniquePerOrder';
2503 }
2504 if (isModEnabled('contract')) {
2505 $tmparray['__SECUREKEYPAYMENT_CONTRACTLINE__'] = 'SecureKeyPAYMENTUniquePerContractLine';
2506 }
2507
2508 //Online payment link
2509 if (isModEnabled('member')) {
2510 $tmparray['__ONLINEPAYMENTLINK_MEMBER__'] = 'OnlinePaymentLinkUniquePerMember';
2511 }
2512 if (isModEnabled('don')) {
2513 $tmparray['__ONLINEPAYMENTLINK_DONATION__'] = 'OnlinePaymentLinkUniquePerDonation';
2514 }
2515 if (isModEnabled('invoice')) {
2516 $tmparray['__ONLINEPAYMENTLINK_INVOICE__'] = 'OnlinePaymentLinkUniquePerInvoice';
2517 }
2518 if (isModEnabled('order')) {
2519 $tmparray['__ONLINEPAYMENTLINK_ORDER__'] = 'OnlinePaymentLinkUniquePerOrder';
2520 }
2521 if (isModEnabled('contract')) {
2522 $tmparray['__ONLINEPAYMENTLINK_CONTRACTLINE__'] = 'OnlinePaymentLinkUniquePerContractLine';
2523 }
2524 } else {
2525 /* No need to show into tooltip help, option is not enabled
2526 $vars['__SECUREKEYPAYMENT__']='';
2527 $vars['__SECUREKEYPAYMENT_MEMBER__']='';
2528 $vars['__SECUREKEYPAYMENT_INVOICE__']='';
2529 $vars['__SECUREKEYPAYMENT_ORDER__']='';
2530 $vars['__SECUREKEYPAYMENT_CONTRACTLINE__']='';
2531 */
2532 }
2533 if (getDolGlobalString('MEMBER_ENABLE_PUBLIC')) {
2534 $tmparray['__PUBLICLINK_NEWMEMBERFORM__'] = 'BlankSubscriptionForm';
2535 }
2536 }
2537
2538 foreach ($tmparray as $key => $val) {
2539 if (empty($val)) {
2540 $tmparray[$key] = $key;
2541 }
2542 }
2543
2544 return $tmparray;
2545 }
2546}
print $object position
Definition edit.php:206
Class to manage a WYSIWYG editor.
Class to generate HTML forms for single email Usage: $formai = new FormAI($db) $formai->proprietes=1 ...
Class to generate html code for admin pages.
Class to offer components to list and upload files.
Class to manage generation of HTML components Only common components must be here.
static selectarray($htmlname, $array, $id='', $show_empty=0, $key_in_label=0, $value_as_key=0, $moreparam='', $translate=0, $maxlen=0, $disabled=0, $sort='', $morecss='minwidth75', $addjscombo=1, $moreparamonempty='', $disablebademail=0, $nohtmlescape=0)
Return a HTML select string, built from an array of key+value.
Class to manage a HTML form to send a unitary email Usage: $formail = new FormMail($db) $formmail->pr...
get_attached_files()
Return list of attached files (stored in SECTION array)
getHtmlForWithErrorsTo()
get Html For WithErrorsTo
getHtmlForTopic($arraydefaultmessage, $helpforsubstitution)
Return Html section for the Topic of message.
clear_attached_files()
Clear list of attached files in send mail form (also stored in session)
getHtmlForCcNew()
get html For CC, using a single select2 combo that also accepts free-typed "Name <email>" tags (used ...
fetchAllEMailTemplate($type_template, $user, $outputlangs, $active=1)
Find if template exists and are available for current user, then set them into $this->lines_model.
getHtmlForCc()
get html For CC
getHtmlForTo()
get html For To
getHtmlForWithCccNew()
get html For WithCCC, using a single select2 combo that also accepts free-typed "Name <email>" tags (...
add_attached_files($path, $file='', $type='')
Add a file into the list of attached files (stored in SECTION array)
getHtmlForToNew()
get html For To, using a single select2 combo that also accepts free-typed "Name <email>" tags (used ...
getHtmlForWithCcc()
get html For WithCCC This information is show when MAIN_EMAIL_USECCC is set.
remove_attached_files($keytodelete)
Remove a file from the list of attached files (stored in SECTION array)
__construct($db)
Constructor.
getHtmlForDeliveryreceipt()
get Html For Asking for Delivery Receipt
getEMailTemplate($dbs, $type_template, $user, $outputlangs, $id=0, $active=1, $label='', $defaultfortype=-1)
Return templates of email with type = $type_template or type = 'all'.
show_form($addfileaction='addfile', $removefileaction='removefile')
Show the form to input an email this->withfile: 0=No attaches files, 1=Show attached files,...
get_form($addfileaction='addfile', $removefileaction='removefile')
Get the form to input an email this->withfile: 0=No attaches files, 1=Show attached files,...
Class to manage products or services.
Class to manage translations.
print $script_file $mode $langs defaultlang(is_numeric($duration_value) ? " delay=". $duration_value :"").(is_numeric($duration_value2) ? " after cd cd cd description as p label as s rowid as s nom as s email
Sender: Who sends the email ("Sender" has sent emails on behalf of "From").
if(!isModEnabled('ai')||!getDolGlobalString('AI_ASSISTANT_ENABLED')) global $conf
The main.inc.php has been included so the following variable are now defined:
dol_delete_dir_recursive($dir, $count=0, $nophperrors=0, $onlysub=0, &$countdeleted=0, $indexdatabase=1, $nolog=0, $level=0)
Remove a directory $dir and its subdirectories (or only files and subdirectories)
dol_mimetype($file, $default='application/octet-stream', $mode=0)
Return MIME type of a file from its name with extension.
dolGetFirstLineOfText($text, $nboflines=1, $charset='UTF-8')
Return first line of text.
dol_string_nohtmltag($stringtoclean, $removelinefeed=1, $pagecodeto='UTF-8', $strip_tags=0, $removedoublespaces=1)
Clean a string from all HTML tags and entities.
dol_nl2br($stringtoencode, $nl2brmode=0, $forxml=false)
Replace CRLF in string with a HTML BR tag.
getDolGlobalInt($key, $default=0)
Return a Dolibarr global constant int value.
GETPOST($paramname, $check='alphanohtml', $method=0, $filter=null, $options=null, $noreplace=0, $nodefault=0)
Return value of a param into GET or POST supervariable.
dol_htmlentities($string, $flags=ENT_QUOTES|ENT_SUBSTITUTE, $encoding='UTF-8', $double_encode=false)
Replace htmlentities functions.
make_substitutions($text, $substitutionarray, $outputlangs=null, $converttextinhtmlifnecessary=0)
Make substitution into a text string, replacing keys with vals from $substitutionarray (oldval=>newva...
dol_textishtml($msg, $option=0)
Return if a text is a html content.
GETPOSTINT($paramname, $method=0, $nodefault=0)
Return the value of a $_GET or $_POST supervariable, converted into integer.
getDolGlobalBool($key, $default=false)
Return a Dolibarr global constant boolean value.
GETPOSTISSET($paramname)
Return true if we are in a context of submitting the parameter $paramname from a POST of a form.
getDolGlobalString($key, $default='')
Return a Dolibarr global constant string value.
isModEnabled($module)
Is Dolibarr module enabled.
getEntity($element, $shared=1, $currentobject=null)
Get list of entity id to use.
multi select button
0 = Do not include form tag and submit button -1 = Do not include form tag but include submit button
editval_textarea active
picto_from_langcode($codelang, $moreatt='', $notitlealt=0)
Return img flag of country for a language code or country code.
img_mime($file, $titlealt='', $morecss='')
Show MIME img of a file.
info_admin($text, $infoonimgalt=0, $nodiv=0, $admin='1', $morecss='hideonsmartphone', $textfordropdown='', $picto='', $textonpictotooltip='', $cssfordropdown='info_admin')
Show information in HTML for admin users or standard users.
dol_escape_htmltag($stringtoescape, $keepb=0, $keepn=0, $noescapetags='', $escapeonlyhtmltags=0, $cleanalsojavascript=0)
Definition html.lib.php:181
getMaxFileSizeArray()
Return the max allowed for file upload.