1<?php
2/* Copyright (C) 2013-2015  Jean-François FERRY     <hello@librethic.io>
3 * Copyright (C) 2016       Christophe Battarel     <christophe@altairis.fr>
4 * Copyright (C) 2019       Frédéric France         <frederic.france@netlogic.fr>
5 * Copyright (C) 2021       Juanjo Menent			<jmenent@2byte.es>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <https://www.gnu.org/licenses/>.
19 */
20
21/**
22 *       \file       htdocs/core/class/html.formticket.class.php
23 *       \ingroup    ticket
24 *       \brief      Fichier de la classe permettant la generation du formulaire html d'envoi de mail unitaire
25 */
26require_once DOL_DOCUMENT_ROOT."/core/class/html.form.class.php";
27require_once DOL_DOCUMENT_ROOT."/core/class/html.formmail.class.php";
28require_once DOL_DOCUMENT_ROOT."/core/class/html.formprojet.class.php";
29
30if (!class_exists('FormCompany')) {
31	include DOL_DOCUMENT_ROOT.'/core/class/html.formcompany.class.php';
32}
33
34/**
35 * Classe permettant la generation du formulaire d'un nouveau ticket.
36 *
37 * @package Ticket
38 * \remarks Utilisation: $formticket = new FormTicket($db)
39 * \remarks $formticket->proprietes=1 ou chaine ou tableau de valeurs
40 * \remarks $formticket->show_form() affiche le formulaire
41 */
42class FormTicket
43{
44	/**
45	 * @var DoliDB Database handler.
46	 */
47	public $db;
48
49	public $track_id;
50
51	/**
52	 * @var int ID
53	 */
54	public $fk_user_create;
55
56	public $message;
57	public $topic_title;
58
59	public $action;
60
61	public $withtopic;
62	public $withemail;
63	/**
64	 *
65	 * @var int $withsubstit Show substitution array
66	 */
67	public $withsubstit;
68
69	public $withfile;
70	public $withfilereadonly;
71
72	public $ispublic; // To show information or not into public form
73
74	public $withtitletopic;
75	public $withcompany; // affiche liste déroulante company
76	public $withfromsocid;
77	public $withfromcontactid;
78	public $withnotifytiersatcreate;
79	public $withusercreate; // Show name of creating user in form
80	public $withcreatereadonly;
81
82	public $withref; // Show ref field
83
84	public $withcancel;
85
86	/**
87	 *
88	 * @var array $substit Substitutions
89	 */
90	public $substit = array();
91	public $param = array();
92
93	/**
94	 * @var string Error code (or message)
95	 */
96	public $error;
97
98
99	/**
100	 * Constructor
101	 *
102	 * @param DoliDB $db Database handler
103	 */
104	public function __construct($db)
105	{
106		global $conf;
107
108		$this->db = $db;
109
110		$this->action = 'add';
111
112		$this->withcompany = $conf->societe->enabled ? 1 : 0;
113		$this->withfromsocid = 0;
114		$this->withfromcontactid = 0;
115		//$this->withthreadid=0;
116		//$this->withtitletopic='';
117		$this->withnotifytiersatcreate = 0;
118		$this->withusercreate = 1;
119		$this->withcreatereadonly = 1;
120		$this->withemail = 0;
121		$this->withref = 0;
122		$this->withextrafields = 0; // Show extrafields or not
123		//$this->withtopicreadonly=0;
124	}
125
126	/**
127	 * Show the form to input ticket
128	 *
129	 * @param  	int	 		$withdolfichehead		With dol_get_fiche_head() and dol_get_fiche_end()
130	 * @param	string		$mode					Mode ('create' or 'edit')
131	 * @param	int			$public					1=If we show the form for the public interface
132	 * @return 	void
133	 */
134	public function showForm($withdolfichehead = 0, $mode = 'edit', $public = 0)
135	{
136		global $conf, $langs, $user, $hookmanager;
137
138		// Load translation files required by the page
139		$langs->loadLangs(array('other', 'mails', 'ticket'));
140
141		$form = new Form($this->db);
142		$formcompany = new FormCompany($this->db);
143		$ticketstatic = new Ticket($this->db);
144
145		$soc = new Societe($this->db);
146		if (!empty($this->withfromsocid) && $this->withfromsocid > 0) {
147			$soc->fetch($this->withfromsocid);
148		}
149
150		$ticketstat = new Ticket($this->db);
151
152		$extrafields = new ExtraFields($this->db);
153		$extrafields->fetch_name_optionals_label($ticketstat->table_element);
154
155		print "\n<!-- Begin form TICKET -->\n";
156
157		if ($withdolfichehead) {
158			print dol_get_fiche_head(null, 'card', '', 0, '');
159		}
160
161		print '<form method="POST" '.($withdolfichehead ? '' : 'style="margin-bottom: 30px;" ').'name="ticket" id="form_create_ticket" enctype="multipart/form-data" action="'.$this->param["returnurl"].'">';
162		print '<input type="hidden" name="token" value="'.newToken().'">';
163		print '<input type="hidden" name="action" value="'.$this->action.'">';
164		foreach ($this->param as $key => $value) {
165			print '<input type="hidden" name="'.$key.'" value="'.$value.'">';
166		}
167		print '<input type="hidden" name="fk_user_create" value="'.$this->fk_user_create.'">';
168
169		print '<table class="border centpercent">';
170
171		if ($this->withref) {
172			// Ref
173			$defaultref = $ticketstat->getDefaultRef();
174			print '<tr><td class="titlefieldcreate"><span class="fieldrequired">'.$langs->trans("Ref").'</span></td><td>';
175			print '<input type="text" name="ref" value="'.dol_escape_htmltag(GETPOST("ref", 'alpha') ? GETPOST("ref", 'alpha') : $defaultref).'">';
176			print '</td></tr>';
177		}
178
179		// TITLE
180		if ($this->withemail) {
181			print '<tr><td class="titlefield"><label for="email"><span class="fieldrequired">'.$langs->trans("Email").'</span></label></td><td>';
182			print '<input  class="text minwidth200" id="email" name="email" value="'.(GETPOST('email', 'alpha') ? GETPOST('email', 'alpha') : $subject).'" autofocus>';
183			print '</td></tr>';
184		}
185
186		// If ticket created from another object
187		if (isset($this->param['origin']) && $this->param['originid'] > 0) {
188			// Parse element/subelement (ex: project_task)
189			$element = $subelement = $this->param['origin'];
190			$regs = array();
191			if (preg_match('/^([^_]+)_([^_]+)/i', $this->param['origin'], $regs)) {
192				$element = $regs[1];
193				$subelement = $regs[2];
194			}
195
196			dol_include_once('/'.$element.'/class/'.$subelement.'.class.php');
197			$classname = ucfirst($subelement);
198			$objectsrc = new $classname($this->db);
199			$objectsrc->fetch(GETPOST('originid', 'int'));
200
201			if (empty($objectsrc->lines) && method_exists($objectsrc, 'fetch_lines')) {
202				$objectsrc->fetch_lines();
203			}
204
205			$objectsrc->fetch_thirdparty();
206			$newclassname = $classname;
207			print '<tr><td>'.$langs->trans($newclassname).'</td><td colspan="2"><input name="'.$subelement.'id" value="'.GETPOST('originid').'" type="hidden" />'.$objectsrc->getNomUrl(1).'</td></tr>';
208		}
209
210		// Type
211		print '<tr><td class="titlefield"><span class="fieldrequired"><label for="selecttype_code">'.$langs->trans("TicketTypeRequest").'</span></label></td><td>';
212		$this->selectTypesTickets((GETPOST('type_code', 'alpha') ? GETPOST('type_code', 'alpha') : $this->type_code), 'type_code', '', 2, 0, 0, 0, 'minwidth200');
213		print '</td></tr>';
214
215		// Group
216		print '<tr><td><span class="fieldrequired"><label for="selectcategory_code">'.$langs->trans("TicketCategory").'</span></label></td><td>';
217		$filter = '';
218		if ($public) {
219			$filter = 'public=1';
220		}
221		$this->selectGroupTickets((GETPOST('category_code') ? GETPOST('category_code') : $this->category_code), 'category_code', $filter, 2, 0, 0, 0, 'minwidth200');
222		print '</td></tr>';
223
224		// Severity
225		print '<tr><td><span class="fieldrequired"><label for="selectseverity_code">'.$langs->trans("TicketSeverity").'</span></label></td><td>';
226		$this->selectSeveritiesTickets((GETPOST('severity_code') ? GETPOST('severity_code') : $this->severity_code), 'severity_code', '', 2, 0);
227		print '</td></tr>';
228
229		// Subject
230		if ($this->withtitletopic) {
231			print '<tr><td><label for="subject"><span class="fieldrequired">'.$langs->trans("Subject").'</span></label></td><td>';
232
233			// Réponse à un ticket : affichage du titre du thread en readonly
234			if ($this->withtopicreadonly) {
235				print $langs->trans('SubjectAnswerToTicket').' '.$this->topic_title;
236				print '</td></tr>';
237			} else {
238				if ($this->withthreadid > 0) {
239					$subject = $langs->trans('SubjectAnswerToTicket').' '.$this->withthreadid.' : '.$this->topic_title.'';
240				}
241				print '<input class="text minwidth500" id="subject" name="subject" value="'.(GETPOST('subject', 'alpha') ? GETPOST('subject', 'alpha') : $subject).'" autofocus />';
242				print '</td></tr>';
243			}
244		}
245
246		// MESSAGE
247		$msg = GETPOSTISSET('message') ? GETPOST('message', 'restricthtml') : '';
248		print '<tr><td><label for="message"><span class="fieldrequired">'.$langs->trans("Message").'</span></label></td><td>';
249
250		// If public form, display more information
251		$toolbarname = 'dolibarr_notes';
252		if ($this->ispublic) {
253			$toolbarname = 'dolibarr_details';
254			print '<div class="warning">'.($conf->global->TICKET_PUBLIC_TEXT_HELP_MESSAGE ? $conf->global->TICKET_PUBLIC_TEXT_HELP_MESSAGE : $langs->trans('TicketPublicPleaseBeAccuratelyDescribe')).'</div>';
255		}
256		include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
257		$uselocalbrowser = true;
258		$doleditor = new DolEditor('message', $msg, '100%', 230, $toolbarname, 'In', true, $uselocalbrowser, $conf->global->FCKEDITOR_ENABLE_TICKET, ROWS_8, '90%');
259		$doleditor->Create();
260		print '</td></tr>';
261
262		if ($public && !empty($conf->global->MAIN_SECURITY_ENABLECAPTCHA)) {
263			require_once DOL_DOCUMENT_ROOT.'/core/lib/security2.lib.php';
264			print '<tr><td class="titlefield"><label for="email"><span class="fieldrequired">'.$langs->trans("SecurityCode").'</span></label></td><td>';
265			print '<span class="span-icon-security inline-block">';
266			print '<input id="securitycode" placeholder="'.$langs->trans("SecurityCode").'" class="flat input-icon-security width125" type="text" maxlength="5" name="code" tabindex="3" />';
267			print '</span>';
268			print '<span class="nowrap inline-block">';
269			print '<img class="inline-block valignmiddle" src="'.DOL_URL_ROOT.'/core/antispamimage.php" border="0" width="80" height="32" id="img_securitycode" />';
270			print '<a class="inline-block valignmiddle" href="'.$php_self.'" tabindex="4" data-role="button">'.img_picto($langs->trans("Refresh"), 'refresh', 'id="captcha_refresh_img"').'</a>';
271			print '</span>';
272			print '</td></tr>';
273		}
274
275		// Attached files
276		if (!empty($this->withfile)) {
277			// Define list of attached files
278			$listofpaths = array();
279			$listofnames = array();
280			$listofmimes = array();
281			if (!empty($_SESSION["listofpaths"])) {
282				$listofpaths = explode(';', $_SESSION["listofpaths"]);
283			}
284
285			if (!empty($_SESSION["listofnames"])) {
286				$listofnames = explode(';', $_SESSION["listofnames"]);
287			}
288
289			if (!empty($_SESSION["listofmimes"])) {
290				$listofmimes = explode(';', $_SESSION["listofmimes"]);
291			}
292
293			$out = '<tr>';
294			$out .= '<td>'.$langs->trans("MailFile").'</td>';
295			$out .= '<td>';
296			// TODO Trick to have param removedfile containing nb of image to delete. But this does not works without javascript
297			$out .= '<input type="hidden" class="removedfilehidden" name="removedfile" value="">'."\n";
298			$out .= '<script type="text/javascript" language="javascript">';
299			$out .= 'jQuery(document).ready(function () {';
300			$out .= '    jQuery(".removedfile").click(function() {';
301			$out .= '        jQuery(".removedfilehidden").val(jQuery(this).val());';
302			$out .= '    });';
303			$out .= '})';
304			$out .= '</script>'."\n";
305			if (count($listofpaths)) {
306				foreach ($listofpaths as $key => $val) {
307					$out .= '<div id="attachfile_'.$key.'">';
308					$out .= img_mime($listofnames[$key]).' '.$listofnames[$key];
309					if (!$this->withfilereadonly) {
310						$out .= ' <input type="image" style="border: 0px;" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/delete.png" value="'.($key + 1).'" class="removedfile" id="removedfile_'.$key.'" name="removedfile_'.$key.'" />';
311					}
312					$out .= '<br></div>';
313				}
314			} else {
315				$out .= $langs->trans("NoAttachedFiles").'<br>';
316			}
317			if ($this->withfile == 2) { // Can add other files
318				$out .= '<input type="file" class="flat" id="addedfile" name="addedfile" value="'.$langs->trans("Upload").'" />';
319				$out .= ' ';
320				$out .= '<input type="submit" class="button smallpaddingimp reposition" id="addfile" name="addfile" value="'.$langs->trans("MailingAddFile").'" />';
321			}
322			$out .= "</td></tr>\n";
323
324			print $out;
325		}
326
327		// User of creation
328		if ($this->withusercreate > 0 && $this->fk_user_create) {
329			print '<tr><td class="titlefield">'.$langs->trans("CreatedBy").'</td><td>';
330			$langs->load("users");
331			$fuser = new User($this->db);
332
333			if ($this->withcreatereadonly) {
334				if ($res = $fuser->fetch($this->fk_user_create)) {
335					print $fuser->getNomUrl(1);
336				}
337			}
338			print ' &nbsp; ';
339			print "</td></tr>\n";
340		}
341
342		// Customer or supplier
343		if ($this->withcompany) {
344			// altairis: force company and contact id for external user
345			if (empty($user->socid)) {
346				// Company
347				print '<tr><td class="titlefield">'.$langs->trans("ThirdParty").'</td><td>';
348				$events = array();
349				$events[] = array('method' => 'getContacts', 'url' => dol_buildpath('/core/ajax/contacts.php', 1), 'htmlname' => 'contactid', 'params' => array('add-customer-contact' => 'disabled'));
350				print img_picto('', 'company', 'class="paddingright"');
351				print $form->select_company($this->withfromsocid, 'socid', '', 1, 1, '', $events, 0, 'minwidth200');
352				print '</td></tr>';
353				if (!empty($conf->use_javascript_ajax) && !empty($conf->global->COMPANY_USE_SEARCH_TO_SELECT)) {
354					$htmlname = 'socid';
355					print '<script type="text/javascript">
356                    $(document).ready(function () {
357                        jQuery("#'.$htmlname.'").change(function () {
358                            var obj = '.json_encode($events).';
359                            $.each(obj, function(key,values) {
360                                if (values.method.length) {
361                                    runJsCodeForEvent'.$htmlname.'(values);
362                                }
363                            });
364                        });
365
366                        function runJsCodeForEvent'.$htmlname.'(obj) {
367                            console.log("Run runJsCodeForEvent'.$htmlname.'");
368                            var id = $("#'.$htmlname.'").val();
369                            var method = obj.method;
370                            var url = obj.url;
371                            var htmlname = obj.htmlname;
372                            var showempty = obj.showempty;
373                            $.getJSON(url,
374                                    {
375                                        action: method,
376                                        id: id,
377                                        htmlname: htmlname,
378                                        showempty: showempty
379                                    },
380                                    function(response) {
381                                        $.each(obj.params, function(key,action) {
382                                            if (key.length) {
383                                                var num = response.num;
384                                                if (num > 0) {
385                                                    $("#" + key).removeAttr(action);
386                                                } else {
387                                                    $("#" + key).attr(action, action);
388                                                }
389                                            }
390                                        });
391                                        $("select#" + htmlname).html(response.value);
392                                        if (response.num) {
393                                            var selecthtml_str = response.value;
394                                            var selecthtml_dom=$.parseHTML(selecthtml_str);
395											if (typeof(selecthtml_dom[0][0]) !== \'undefined\') {
396                                            	$("#inputautocomplete"+htmlname).val(selecthtml_dom[0][0].innerHTML);
397											}
398                                        } else {
399                                            $("#inputautocomplete"+htmlname).val("");
400                                        }
401                                        $("select#" + htmlname).change();	/* Trigger event change */
402                                    }
403                            );
404                        }
405                    });
406                    </script>';
407				}
408
409				// Contact and type
410				print '<tr><td>'.$langs->trans("Contact").'</td><td>';
411				// If no socid, set to -1 to avoid full contacts list
412				$selectedCompany = ($this->withfromsocid > 0) ? $this->withfromsocid : -1;
413				print img_picto('', 'contact', 'class="paddingright"');
414				print $form->selectcontacts($selectedCompany, $this->withfromcontactid, 'contactid', 3, '', '', 0, 'minwidth200');
415				print ' ';
416				$formcompany->selectTypeContact($ticketstatic, '', 'type', 'external', '', 0, 'maginleftonly');
417				print '</td></tr>';
418			} else {
419				print '<tr><td class="titlefield"><input type="hidden" name="socid" value="'.$user->socid.'"/></td>';
420				print '<td><input type="hidden" name="contactid" value="'.$user->contact_id.'"/></td>';
421				print '<td><input type="hidden" name="type" value="Z"/></td></tr>';
422			}
423
424			// Notify thirdparty at creation
425			if (empty($this->ispublic)) {
426				print '<tr><td><label for="notify_tiers_at_create">'.$langs->trans("TicketNotifyTiersAtCreation").'</label></td><td>';
427				print '<input type="checkbox" id="notify_tiers_at_create" name="notify_tiers_at_create"'.($this->withnotifytiersatcreate ? ' checked="checked"' : '').'>';
428				print '</td></tr>';
429			}
430		}
431
432		if (!empty($conf->projet->enabled) && !$this->ispublic) {
433			$formproject = new FormProjets($this->db);
434			print '<tr><td><label for="project"><span class="">'.$langs->trans("Project").'</span></label></td><td>';
435			print img_picto('', 'project').$formproject->select_projects(-1, GETPOST('projectid', 'int'), 'projectid', 0, 0, 1, 1, 0, 0, 0, '', 1, 0, 'maxwidth500');
436			print '</td></tr>';
437		}
438
439		// Other attributes
440		$parameters = array();
441		$reshook = $hookmanager->executeHooks('formObjectOptions', $parameters, $ticketstat, $this->action); // Note that $action and $object may have been modified by hook
442		if (empty($reshook)) {
443			print $ticketstat->showOptionals($extrafields, 'create');
444		}
445
446		print '</table>';
447
448		if ($withdolfichehead) {
449			print dol_get_fiche_end();
450		}
451
452		print '<br><div class="center">';
453		print '<input class="button" type="submit" name="add" value="'.$langs->trans(($this->withthreadid > 0 ? "SendResponse" : "CreateTicket")).'" />';
454		if ($this->withcancel) {
455			print " &nbsp; &nbsp; &nbsp;";
456			print '<input class="button button-cancel" type="submit" name="cancel" value="'.$langs->trans("Cancel").'">';
457		}
458		print '</div>';
459
460		print '<input type="hidden" name="page_y">'."\n";
461
462		print "</form>\n";
463		print "<!-- End form TICKET -->\n";
464	}
465
466	/**
467	 *      Return html list of tickets type
468	 *
469	 *      @param  string $selected    Id du type pre-selectionne
470	 *      @param  string $htmlname    Nom de la zone select
471	 *      @param  string $filtertype  To filter on field type in llx_c_ticket_type (array('code'=>xx,'label'=>zz))
472	 *      @param  int    $format      0=id+libelle, 1=code+code, 2=code+libelle, 3=id+code
473	 *      @param  int    $empty       1=peut etre vide, 0 sinon
474	 *      @param  int    $noadmininfo 0=Add admin info, 1=Disable admin info
475	 *      @param  int    $maxlength   Max length of label
476	 *      @param	string	$morecss	More CSS
477	 *      @return void
478	 */
479	public function selectTypesTickets($selected = '', $htmlname = 'tickettype', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss = '')
480	{
481		global $langs, $user;
482
483		$ticketstat = new Ticket($this->db);
484
485		dol_syslog(get_class($this)."::select_types_tickets ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
486
487		$filterarray = array();
488
489		if ($filtertype != '' && $filtertype != '-1') {
490			$filterarray = explode(',', $filtertype);
491		}
492
493		$ticketstat->loadCacheTypesTickets();
494
495		print '<select id="select'.$htmlname.'" class="flat minwidth100'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
496		if ($empty) {
497			print '<option value="">&nbsp;</option>';
498		}
499
500		if (is_array($ticketstat->cache_types_tickets) && count($ticketstat->cache_types_tickets)) {
501			foreach ($ticketstat->cache_types_tickets as $id => $arraytypes) {
502				// On passe si on a demande de filtrer sur des modes de paiments particuliers
503				if (count($filterarray) && !in_array($arraytypes['type'], $filterarray)) {
504					continue;
505				}
506
507				// We discard empty line if showempty is on because an empty line has already been output.
508				if ($empty && empty($arraytypes['code'])) {
509					continue;
510				}
511
512				if ($format == 0) {
513					print '<option value="'.$id.'"';
514				}
515
516				if ($format == 1) {
517					print '<option value="'.$arraytypes['code'].'"';
518				}
519
520				if ($format == 2) {
521					print '<option value="'.$arraytypes['code'].'"';
522				}
523
524				if ($format == 3) {
525					print '<option value="'.$id.'"';
526				}
527
528				// Si selected est text, on compare avec code, sinon avec id
529				if (preg_match('/[a-z]/i', $selected) && $selected == $arraytypes['code']) {
530					print ' selected="selected"';
531				} elseif ($selected == $id) {
532					print ' selected="selected"';
533				} elseif ($arraytypes['use_default'] == "1" && !$selected && !$empty) {
534					print ' selected="selected"';
535				}
536
537				print '>';
538				$value = '&nbsp;';
539				if ($format == 0) {
540					$value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
541				} elseif ($format == 1) {
542					$value = $arraytypes['code'];
543				} elseif ($format == 2) {
544					$value = ($maxlength ? dol_trunc($arraytypes['label'], $maxlength) : $arraytypes['label']);
545				} elseif ($format == 3) {
546					$value = $arraytypes['code'];
547				}
548
549				print $value;
550				print '</option>';
551			}
552		}
553		print '</select>';
554		if ($user->admin && !$noadmininfo) {
555			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
556		}
557
558		print ajax_combobox('select'.$htmlname);
559	}
560
561	/**
562	 *      Return html list of ticket anaytic codes
563	 *
564	 *      @param  string $selected    Id categorie pre-selectionnée
565	 *      @param  string $htmlname    Nom de la zone select
566	 *      @param  string $filtertype  To filter on some properties in llx_c_ticket_category ('public = 1'). This parameter must not come from input of users.
567	 *      @param  int    $format      0=id+libelle, 1=code+code, 2=code+libelle, 3=id+code
568	 *      @param  int    $empty       1=peut etre vide, 0 sinon
569	 *      @param  int    $noadmininfo 0=Add admin info, 1=Disable admin info
570	 *      @param  int    $maxlength   Max length of label
571	 *      @param	string	$morecss	More CSS
572	 * 		@param	int 	$use_multilevel	if != 0 create a multilevel select ( Do not use any of the other params)
573	 *      @return void
574	 */
575	public function selectGroupTickets($selected = '', $htmlname = 'ticketcategory', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss = '', $use_multilevel = 0)
576	{
577		global $langs, $user;
578
579		if ($use_multilevel == 0) {
580			$ticketstat = new Ticket($this->db);
581
582			dol_syslog(get_class($this)."::selectCategoryTickets ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
583
584			$ticketstat->loadCacheCategoriesTickets();
585
586			print '<select id="select'.$htmlname.'" class="flat minwidth100'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
587			if ($empty) {
588				print '<option value="">&nbsp;</option>';
589			}
590
591			if (is_array($ticketstat->cache_category_tickets) && count($ticketstat->cache_category_tickets)) {
592				foreach ($ticketstat->cache_category_tickets as $id => $arraycategories) {
593					// Exclude some record
594					if ($filtertype == 'public=1') {
595						if (empty($arraycategories['public'])) {
596							continue;
597						}
598					}
599
600					// We discard empty line if showempty is on because an empty line has already been output.
601					if ($empty && empty($arraycategories['code'])) {
602						continue;
603					}
604
605					if ($format == 0) {
606						print '<option value="'.$id.'"';
607					}
608
609					if ($format == 1) {
610						print '<option value="'.$arraycategories['code'].'"';
611					}
612
613					if ($format == 2) {
614						print '<option value="'.$arraycategories['code'].'"';
615					}
616
617					if ($format == 3) {
618						print '<option value="'.$id.'"';
619					}
620
621					// Si selected est text, on compare avec code, sinon avec id
622					if (preg_match('/[a-z]/i', $selected) && $selected == $arraycategories['code']) {
623						print ' selected="selected"';
624					} elseif ($selected == $id) {
625						print ' selected="selected"';
626					} elseif ($arraycategories['use_default'] == "1" && !$selected && !$empty) {
627						print ' selected="selected"';
628					}
629
630					print '>';
631
632					if ($format == 0) {
633						$value = ($maxlength ? dol_trunc($arraycategories['label'], $maxlength) : $arraycategories['label']);
634					}
635
636					if ($format == 1) {
637						$value = $arraycategories['code'];
638					}
639
640					if ($format == 2) {
641						$value = ($maxlength ? dol_trunc($arraycategories['label'], $maxlength) : $arraycategories['label']);
642					}
643
644					if ($format == 3) {
645						$value = $arraycategories['code'];
646					}
647
648					print $value ? $value : '&nbsp;';
649					print '</option>';
650				}
651			}
652			print '</select>';
653			if ($user->admin && !$noadmininfo) {
654				print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
655			}
656
657			print ajax_combobox('select'.$htmlname);
658		} else {
659			$groupticket=GETPOST('groupticket', 'aZ09');
660			$groupticketchild=GETPOST('groupticket_child', 'aZ09');
661			$arraycodenotparent[] = "";
662			$stringtoprint = '<span class="supportemailfield bold">'.$langs->trans("GroupOfTicket").'</span> ';
663			$stringtoprint .= '<select name="groupticket" id ="groupticket" class="maxwidth500 minwidth400">';
664			$stringtoprint .= '<option value="">&nbsp;</option>';
665
666			$sql = "SELECT ctc.rowid, ctc.code, ctc.label, ctc.fk_parent, ";
667			$sql .= $this->db->ifsql("ctc.rowid NOT IN (SELECT ctcfather.rowid FROM llx_c_ticket_category as ctcfather JOIN llx_c_ticket_category as ctcjoin ON ctcfather.rowid = ctcjoin.fk_parent)", "'NOTPARENT'", "'PARENT'")." as isparent";
668			$sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_category as ctc";
669			$sql .= " WHERE ctc.public = 1";
670			$sql .= " AND ctc.active = 1";
671			$sql .= " AND ctc.fk_parent = 0";
672			$sql .= $this->db->order('ctc.pos', 'ASC');
673			$resql = $this->db->query($sql);
674			if ($resql) {
675				$num_rows = $this->db->num_rows($resql);
676				$i = 0;
677				while ($i < $num_rows) {
678					$obj = $this->db->fetch_object($resql);
679					if ($obj) {
680						$grouprowid = $obj->rowid;
681						$groupvalue = $obj->code;
682						$grouplabel = $obj->label;
683						$isparent = $obj->isparent;
684						$iselected = $groupticket == $obj->code ?'selected':'';
685						$stringtoprint .= '<option '.$iselected.' class="groupticket'.dol_escape_htmltag($grouprowid).'" value="'.dol_escape_htmltag($groupvalue).'" data-html="'.dol_escape_htmltag($grouplabel).'">'.dol_escape_htmltag($grouplabel).'</option>';
686						if ($isparent == 'NOTPARENT') {
687							$arraycodenotparent[] = $groupvalue;
688						}
689					}
690					$i++;
691				}
692			} else {
693				dol_print_error($this->db);
694			}
695			if ($num_rows == 1) {
696				return '<input type="hidden" name="groupticket" id="groupticket" value="'.dol_escape_htmltag($groupvalue).'">';
697			}
698			$stringtoprint .= '</select>&nbsp';
699
700			$stringtoprint .= '<select name="groupticket_child" id ="groupticket_child" class="maxwidth500 minwidth400">';
701			$stringtoprint .= '<option value="">&nbsp;</option>';
702
703			$sql = "SELECT ctc.rowid, ctc.code, ctc.label, ctc.fk_parent, ctcjoin.code as codefather";
704			$sql .= " FROM ".MAIN_DB_PREFIX."c_ticket_category as ctc";
705			$sql .= " JOIN ".MAIN_DB_PREFIX."c_ticket_category as ctcjoin ON ctc.fk_parent = ctcjoin.rowid";
706			$sql .= " WHERE ctc.public = 1";
707			$sql .= " AND ctc.active = 1";
708			$sql .= " AND ctc.fk_parent <> 0";
709			$sql .= $this->db->order('ctc.pos', 'ASC');
710			$resql = $this->db->query($sql);
711			if ($resql) {
712				$num_rows = $this->db->num_rows($resql);
713				$i = 0;
714				while ($i < $num_rows) {
715					$obj = $this->db->fetch_object($resql);
716					if ($obj) {
717						$grouprowid = $obj->rowid;
718						$groupvalue = $obj->code;
719						$grouplabel = $obj->label;
720						$fatherid = $obj->fk_parent;
721						$groupcodefather = $obj->codefather;
722						$iselected = $groupticketchild == $obj->code ?'selected':'';
723						$stringtoprint .= '<option '.$iselected.' class="groupticket_'.dol_escape_htmltag($fatherid).'_child" value="'.dol_escape_htmltag($groupvalue).'" data-html="'.dol_escape_htmltag($grouplabel).'">'.dol_escape_htmltag($grouplabel).'</option>';
724						$tabscript[] = 'if($("#groupticket")[0].value == "'.dol_escape_js($groupcodefather).'"){
725							$(".groupticket_'.dol_escape_htmltag($fatherid).'_child").show()
726						}else{
727							$(".groupticket_'.dol_escape_htmltag($fatherid).'_child").hide()
728						}';
729					}
730					$i++;
731				}
732			} else {
733				dol_print_error($this->db);
734			}
735			$stringtoprint .='</select>';
736
737			$stringtoprint .='<script>';
738			$stringtoprint .='var arraynotparents = '.json_encode($arraycodenotparent).';';
739			$stringtoprint .='if (arraynotparents.includes($("#groupticket")[0].value)){$("#groupticket_child").hide()}
740			else{';
741			foreach ($tabscript as $script) {
742				$stringtoprint .= $script;
743			};
744			$stringtoprint .='}
745			$("#groupticket").change(function() {
746				$("#groupticket_child")[0].value = ""
747				if (!arraynotparents.includes(this.value)) {
748				$("#groupticket_child").show()
749				} else {
750				$("#groupticket_child").hide()
751				}
752			';
753			foreach ($tabscript as $script) {
754				$stringtoprint .= $script;
755			};
756			$stringtoprint .='})';
757			$stringtoprint .='</script>';
758			return $stringtoprint;
759		}
760	}
761
762	/**
763	 *      Return html list of ticket severitys
764	 *
765	 *      @param  string $selected    Id severity pre-selectionnée
766	 *      @param  string $htmlname    Nom de la zone select
767	 *      @param  string $filtertype  To filter on field type in llx_c_ticket_severity (array('code'=>xx,'label'=>zz))
768	 *      @param  int    $format      0=id+libelle, 1=code+code, 2=code+libelle, 3=id+code
769	 *      @param  int    $empty       1=peut etre vide, 0 sinon
770	 *      @param  int    $noadmininfo 0=Add admin info, 1=Disable admin info
771	 *      @param  int    $maxlength   Max length of label
772	 *      @param	string	$morecss	More CSS
773	 *      @return void
774	 */
775	public function selectSeveritiesTickets($selected = '', $htmlname = 'ticketseverity', $filtertype = '', $format = 0, $empty = 0, $noadmininfo = 0, $maxlength = 0, $morecss = '')
776	{
777		global $langs, $user;
778
779		$ticketstat = new Ticket($this->db);
780
781		dol_syslog(get_class($this)."::selectSeveritiesTickets ".$selected.", ".$htmlname.", ".$filtertype.", ".$format, LOG_DEBUG);
782
783		$filterarray = array();
784
785		if ($filtertype != '' && $filtertype != '-1') {
786			$filterarray = explode(',', $filtertype);
787		}
788
789		$ticketstat->loadCacheSeveritiesTickets();
790
791		print '<select id="select'.$htmlname.'" class="flat minwidth100'.($morecss ? ' '.$morecss : '').'" name="'.$htmlname.'">';
792		if ($empty) {
793			print '<option value="">&nbsp;</option>';
794		}
795
796		if (is_array($ticketstat->cache_severity_tickets) && count($ticketstat->cache_severity_tickets)) {
797			foreach ($ticketstat->cache_severity_tickets as $id => $arrayseverities) {
798				// On passe si on a demande de filtrer sur des modes de paiments particuliers
799				if (count($filterarray) && !in_array($arrayseverities['type'], $filterarray)) {
800					continue;
801				}
802
803				// We discard empty line if showempty is on because an empty line has already been output.
804				if ($empty && empty($arrayseverities['code'])) {
805					continue;
806				}
807
808				if ($format == 0) {
809					print '<option value="'.$id.'"';
810				}
811
812				if ($format == 1) {
813					print '<option value="'.$arrayseverities['code'].'"';
814				}
815
816				if ($format == 2) {
817					print '<option value="'.$arrayseverities['code'].'"';
818				}
819
820				if ($format == 3) {
821					print '<option value="'.$id.'"';
822				}
823
824				// Si selected est text, on compare avec code, sinon avec id
825				if (preg_match('/[a-z]/i', $selected) && $selected == $arrayseverities['code']) {
826					print ' selected="selected"';
827				} elseif ($selected == $id) {
828					print ' selected="selected"';
829				} elseif ($arrayseverities['use_default'] == "1" && !$selected && !$empty) {
830					print ' selected="selected"';
831				}
832
833				print '>';
834				if ($format == 0) {
835					$value = ($maxlength ? dol_trunc($arrayseverities['label'], $maxlength) : $arrayseverities['label']);
836				}
837
838				if ($format == 1) {
839					$value = $arrayseverities['code'];
840				}
841
842				if ($format == 2) {
843					$value = ($maxlength ? dol_trunc($arrayseverities['label'], $maxlength) : $arrayseverities['label']);
844				}
845
846				if ($format == 3) {
847					$value = $arrayseverities['code'];
848				}
849
850				print $value ? $value : '&nbsp;';
851				print '</option>';
852			}
853		}
854		print '</select>';
855		if ($user->admin && !$noadmininfo) {
856			print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
857		}
858
859		print ajax_combobox('select'.$htmlname);
860	}
861
862	// phpcs:disable PEAR.NamingConventions.ValidFunctionName.ScopeNotCamelCaps
863	/**
864	 * Clear list of attached files in send mail form (also stored in session)
865	 *
866	 * @return	void
867	 */
868	public function clear_attached_files()
869	{
870		// phpcs:enable
871		global $conf, $user;
872		require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';
873
874		// Set tmp user directory
875		$vardir = $conf->user->dir_output."/".$user->id;
876		$upload_dir = $vardir.'/temp/'; // TODO Add $keytoavoidconflict in upload_dir path
877		if (is_dir($upload_dir)) {
878			dol_delete_dir_recursive($upload_dir);
879		}
880
881		$keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
882		unset($_SESSION["listofpaths".$keytoavoidconflict]);
883		unset($_SESSION["listofnames".$keytoavoidconflict]);
884		unset($_SESSION["listofmimes".$keytoavoidconflict]);
885	}
886
887	/**
888	 * Show the form to add message on ticket
889	 *
890	 * @param  	string  $width      	Width of form
891	 * @return 	void
892	 */
893	public function showMessageForm($width = '40%')
894	{
895		global $conf, $langs, $user, $hookmanager, $form, $mysoc;
896
897		$formmail = new FormMail($this->db);
898		$addfileaction = 'addfile';
899
900		if (!is_object($form)) {
901			$form = new Form($this->db);
902		}
903
904		// Load translation files required by the page
905		$langs->loadLangs(array('other', 'mails'));
906
907		// Clear temp files. Must be done at beginning, before call of triggers
908		if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1')) {
909			$this->clear_attached_files();
910		}
911
912		// Define output language
913		$outputlangs = $langs;
914		$newlang = '';
915		if ($conf->global->MAIN_MULTILANGS && empty($newlang)) {
916			$newlang = $this->param['langsmodels'];
917		}
918		if (!empty($newlang)) {
919			$outputlangs = new Translate("", $conf);
920			$outputlangs->setDefaultLang($newlang);
921			$outputlangs->load('other');
922		}
923
924		// Get message template for $this->param["models"] into c_email_templates
925		$arraydefaultmessage = -1;
926		if ($this->param['models'] != 'none') {
927			$model_id = 0;
928			if (array_key_exists('models_id', $this->param)) {
929				$model_id = $this->param["models_id"];
930			}
931
932			$arraydefaultmessage = $formmail->getEMailTemplate($this->db, $this->param["models"], $user, $outputlangs, $model_id); // If $model_id is empty, preselect the first one
933		}
934
935		// Define list of attached files
936		$listofpaths = array();
937		$listofnames = array();
938		$listofmimes = array();
939		$keytoavoidconflict = empty($this->trackid) ? '' : '-'.$this->trackid; // this->trackid must be defined
940
941		if (GETPOST('mode', 'alpha') == 'init' || (GETPOST('modelmailselected', 'alpha') && GETPOST('modelmailselected', 'alpha') != '-1')) {
942			if (!empty($arraydefaultmessage->joinfiles) && is_array($this->param['fileinit'])) {
943				foreach ($this->param['fileinit'] as $file) {
944					$this->add_attached_files($file, basename($file), dol_mimetype($file));
945				}
946			}
947		}
948
949		if (!empty($_SESSION["listofpaths".$keytoavoidconflict])) {
950			$listofpaths = explode(';', $_SESSION["listofpaths".$keytoavoidconflict]);
951		}
952		if (!empty($_SESSION["listofnames".$keytoavoidconflict])) {
953			$listofnames = explode(';', $_SESSION["listofnames".$keytoavoidconflict]);
954		}
955		if (!empty($_SESSION["listofmimes".$keytoavoidconflict])) {
956			$listofmimes = explode(';', $_SESSION["listofmimes".$keytoavoidconflict]);
957		}
958
959		// Define output language
960		$outputlangs = $langs;
961		$newlang = '';
962		if ($conf->global->MAIN_MULTILANGS && empty($newlang)) {
963			$newlang = $this->param['langsmodels'];
964		}
965		if (!empty($newlang)) {
966			$outputlangs = new Translate("", $conf);
967			$outputlangs->setDefaultLang($newlang);
968			$outputlangs->load('other');
969		}
970
971		print "\n<!-- Begin message_form TICKET -->\n";
972
973		$send_email = GETPOST('send_email', 'int') ? GETPOST('send_email', 'int') : 0;
974
975		// Example 1 : Adding jquery code
976		print '<script type="text/javascript" language="javascript">
977		jQuery(document).ready(function() {
978			send_email=' . $send_email.';
979			if (send_email) {
980				jQuery(".email_line").show();
981			} else {
982				jQuery(".email_line").hide();
983			}
984
985			jQuery("#send_msg_email").click(function() {
986				if(jQuery(this).is(":checked")) {
987					jQuery(".email_line").show();
988				}
989				else {
990					jQuery(".email_line").hide();
991				}
992            });';
993		print '});
994		</script>';
995
996		print '<form method="post" name="ticket" enctype="multipart/form-data" action="'.$this->param["returnurl"].'">';
997		print '<input type="hidden" name="token" value="'.newToken().'">';
998		print '<input type="hidden" name="action" value="'.$this->action.'">';
999		print '<input type="hidden" name="actionbis" value="add_message">';
1000		foreach ($this->param as $key => $value) {
1001			print '<input type="hidden" name="'.$key.'" value="'.$value.'">';
1002		}
1003
1004		// Get message template
1005		$model_id = 0;
1006		if (array_key_exists('models_id', $this->param)) {
1007			$model_id = $this->param["models_id"];
1008			$arraydefaultmessage = $formmail->getEMailTemplate($this->db, $this->param["models"], $user, $outputlangs, $model_id);
1009		}
1010
1011		$result = $formmail->fetchAllEMailTemplate($this->param["models"], $user, $outputlangs);
1012		if ($result < 0) {
1013			setEventMessages($this->error, $this->errors, 'errors');
1014		}
1015		$modelmail_array = array();
1016		foreach ($formmail->lines_model as $line) {
1017			$modelmail_array[$line->id] = $line->label;
1018		}
1019
1020		print '<table class="border" width="'.$width.'">';
1021
1022		// External users can't send message email
1023		if ($user->rights->ticket->write && !$user->socid) {
1024			print '<tr><td></td><td>';
1025			$checkbox_selected = (GETPOST('send_email') == "1" ? ' checked' : '');
1026			print '<input type="checkbox" name="send_email" value="1" id="send_msg_email" '.$checkbox_selected.'/> ';
1027			print '<label for="send_msg_email">'.$langs->trans('SendMessageByEmail').'</label>';
1028			print '</td></tr>';
1029
1030			// Zone to select its email template
1031			if (count($modelmail_array) > 0) {
1032				print '<tr class="email_line"><td></td><td colspan="2"><div style="padding: 3px 0 3px 0">'."\n";
1033				print $langs->trans('SelectMailModel').': '.$formmail->selectarray('modelmailselected', $modelmail_array, $this->param['models_id'], 1);
1034				if ($user->admin) {
1035					print info_admin($langs->trans("YouCanChangeValuesForThisListFromDictionarySetup"), 1);
1036				}
1037				print ' &nbsp; ';
1038				print '<input class="button" type="submit" value="'.$langs->trans('Apply').'" name="modelselected" id="modelselected">';
1039				print '</div></td>';
1040			}
1041
1042			// Private message (not visible by customer/external user)
1043			if (!$user->socid) {
1044				print '<tr><td></td><td>';
1045				$checkbox_selected = (GETPOST('private_message', 'alpha') == "1" ? ' checked' : '');
1046				print '<input type="checkbox" name="private_message" value="1" id="private_message" '.$checkbox_selected.'/> ';
1047				print '<label for="private_message">'.$langs->trans('MarkMessageAsPrivate').'</label>';
1048				print ' '.$form->textwithpicto('', $langs->trans("TicketMessagePrivateHelp"), 1, 'help');
1049				print '</td></tr>';
1050			}
1051
1052			// Subject
1053			print '<tr class="email_line"><td>'.$langs->trans('Subject').'</td>';
1054			print '<td><input type="text" class="text minwidth500" name="subject" value="['.$conf->global->MAIN_INFO_SOCIETE_NOM.' - '.$langs->trans("Ticket").' '.$this->ref.'] '.$langs->trans('TicketNewMessage').'" />';
1055			print '</td></tr>';
1056
1057			// Destinataires
1058			print '<tr class="email_line"><td>'.$langs->trans('MailRecipients').'</td><td>';
1059			$ticketstat = new Ticket($this->db);
1060			$res = $ticketstat->fetch('', '', $this->track_id);
1061			if ($res) {
1062				// Retrieve email of all contacts (internal and external)
1063				$contacts = $ticketstat->getInfosTicketInternalContact();
1064				$contacts = array_merge($contacts, $ticketstat->getInfosTicketExternalContact());
1065
1066				// Build array to display recipient list
1067				if (is_array($contacts) && count($contacts) > 0) {
1068					foreach ($contacts as $key => $info_sendto) {
1069						if ($info_sendto['email'] != '') {
1070							$sendto[] = dol_escape_htmltag(trim($info_sendto['firstname']." ".$info_sendto['lastname'])." <".$info_sendto['email'].">").' <small class="opacitymedium">('.dol_escape_htmltag($info_sendto['libelle']).")</small>";
1071						}
1072					}
1073				}
1074
1075				if ($ticketstat->origin_email && !in_array($this->dao->origin_email, $sendto)) {
1076					$sendto[] = dol_escape_htmltag($ticketstat->origin_email).' <small class="opacitymedium">('.$langs->trans("TicketEmailOriginIssuer").")</small>";
1077				}
1078
1079				if ($ticketstat->fk_soc > 0) {
1080					$ticketstat->socid = $ticketstat->fk_soc;
1081					$ticketstat->fetch_thirdparty();
1082
1083					if (is_array($ticketstat->thirdparty->email) && !in_array($ticketstat->thirdparty->email, $sendto)) {
1084						$sendto[] = $ticketstat->thirdparty->email.' <small class="opacitymedium">('.$langs->trans('Customer').')</small>';
1085					}
1086				}
1087
1088				if ($conf->global->TICKET_NOTIFICATION_ALSO_MAIN_ADDRESS) {
1089					$sendto[] = $conf->global->TICKET_NOTIFICATION_EMAIL_TO.' <small class="opacitymedium">(generic email)</small>';
1090				}
1091
1092				// Print recipient list
1093				if (is_array($sendto) && count($sendto) > 0) {
1094					print implode(', ', $sendto);
1095				} else {
1096					print '<div class="warning">'.$langs->trans('WarningNoEMailsAdded').' '.$langs->trans('TicketGoIntoContactTab').'</div>';
1097				}
1098			}
1099			print '</td></tr>';
1100		}
1101
1102		$uselocalbrowser = false;
1103
1104		// Intro
1105		// External users can't send message email
1106		if ($user->rights->ticket->write && !$user->socid) {
1107			$mail_intro = GETPOST('mail_intro') ? GETPOST('mail_intro') : $conf->global->TICKET_MESSAGE_MAIL_INTRO;
1108			print '<tr class="email_line"><td><label for="mail_intro">';
1109			print $form->textwithpicto($langs->trans("TicketMessageMailIntro"), $langs->trans("TicketMessageMailIntroHelp"), 1, 'help');
1110			print '</label>';
1111
1112			print '</td><td>';
1113			include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1114
1115			$doleditor = new DolEditor('mail_intro', $mail_intro, '100%', 90, 'dolibarr_details', '', false, $uselocalbrowser, $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_2, 70);
1116
1117			$doleditor->Create();
1118			print '</td></tr>';
1119		}
1120
1121		// MESSAGE
1122
1123		$defaultmessage = "";
1124		if (is_object($arraydefaultmessage) && $arraydefaultmessage->content) {
1125			$defaultmessage = $arraydefaultmessage->content;
1126		}
1127		$defaultmessage = str_replace('\n', "\n", $defaultmessage);
1128
1129		// Deal with format differences between message and signature (text / HTML)
1130		if (dol_textishtml($defaultmessage) && !dol_textishtml($this->substit['__USER_SIGNATURE__'])) {
1131			$this->substit['__USER_SIGNATURE__'] = dol_nl2br($this->substit['__USER_SIGNATURE__']);
1132		} elseif (!dol_textishtml($defaultmessage) && dol_textishtml($this->substit['__USER_SIGNATURE__'])) {
1133			$defaultmessage = dol_nl2br($defaultmessage);
1134		}
1135		if (GETPOSTISSET("message") && !$_POST['modelselected']) {
1136			$defaultmessage = GETPOST('message', 'restricthtml');
1137		} else {
1138			$defaultmessage = make_substitutions($defaultmessage, $this->substit);
1139			// Clean first \n and br (to avoid empty line when CONTACTCIVNAME is empty)
1140			$defaultmessage = preg_replace("/^(<br>)+/", "", $defaultmessage);
1141			$defaultmessage = preg_replace("/^\n+/", "", $defaultmessage);
1142		}
1143
1144		print '<tr><td class="tdtop"><label for="message"><span class="fieldrequired">'.$langs->trans("Message").'</span>';
1145		if ($user->rights->ticket->write && !$user->socid) {
1146			print $form->textwithpicto('', $langs->trans("TicketMessageHelp"), 1, 'help');
1147		}
1148		print '</label></td><td>';
1149		//$toolbarname = 'dolibarr_details';
1150		$toolbarname = 'dolibarr_notes';
1151		include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1152		$doleditor = new DolEditor('message', $defaultmessage, '100%', 200, $toolbarname, '', false, $uselocalbrowser, $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_5, 70);
1153		$doleditor->Create();
1154		print '</td></tr>';
1155
1156		// Signature
1157		// External users can't send message email
1158		if ($user->rights->ticket->write && !$user->socid) {
1159			$mail_signature = GETPOST('mail_signature') ? GETPOST('mail_signature') : $conf->global->TICKET_MESSAGE_MAIL_SIGNATURE;
1160			print '<tr class="email_line"><td><label for="mail_intro">'.$langs->trans("TicketMessageMailSignature").'</label>';
1161			print $form->textwithpicto('', $langs->trans("TicketMessageMailSignatureHelp"), 1, 'help');
1162			print '</td><td>';
1163			include_once DOL_DOCUMENT_ROOT.'/core/class/doleditor.class.php';
1164			$doleditor = new DolEditor('mail_signature', $mail_signature, '100%', 150, 'dolibarr_details', '', false, $uselocalbrowser, $conf->global->FCKEDITOR_ENABLE_SOCIETE, ROWS_2, 70);
1165			$doleditor->Create();
1166			print '</td></tr>';
1167		}
1168
1169		// Attached files
1170		if (!empty($this->withfile)) {
1171			$out = '<tr>';
1172			$out .= '<td width="180">'.$langs->trans("MailFile").'</td>';
1173			$out .= '<td>';
1174			// TODO Trick to have param removedfile containing nb of image to delete. But this does not works without javascript
1175			$out .= '<input type="hidden" class="removedfilehidden" name="removedfile" value="">'."\n";
1176			$out .= '<script type="text/javascript" language="javascript">';
1177			$out .= 'jQuery(document).ready(function () {';
1178			$out .= '    jQuery(".removedfile").click(function() {';
1179			$out .= '        jQuery(".removedfilehidden").val(jQuery(this).val());';
1180			$out .= '    });';
1181			$out .= '})';
1182			$out .= '</script>'."\n";
1183			if (count($listofpaths)) {
1184				foreach ($listofpaths as $key => $val) {
1185					$out .= '<div id="attachfile_'.$key.'">';
1186					$out .= img_mime($listofnames[$key]).' '.$listofnames[$key];
1187					if (!$this->withfilereadonly) {
1188						$out .= ' <input type="image" style="border: 0px;" src="'.DOL_URL_ROOT.'/theme/'.$conf->theme.'/img/delete.png" value="'.($key + 1).'" class="removedfile reposition" id="removedfile_'.$key.'" name="removedfile_'.$key.'" />';
1189					}
1190					$out .= '<br></div>';
1191				}
1192			} else {
1193				$out .= $langs->trans("NoAttachedFiles").'<br>';
1194			}
1195			if ($this->withfile == 2) { // Can add other files
1196				$out .= '<input type="file" class="flat" id="addedfile" name="addedfile" value="'.$langs->trans("Upload").'" />';
1197				$out .= ' ';
1198				$out .= '<input type="submit" class="button smallpaddingimp reposition" id="'.$addfileaction.'" name="'.$addfileaction.'" value="'.$langs->trans("MailingAddFile").'" />';
1199			}
1200			$out .= "</td></tr>\n";
1201
1202			print $out;
1203		}
1204
1205		print '</table>';
1206
1207		print '<center><br>';
1208		print '<input class="button" type="submit" name="btn_add_message" value="'.$langs->trans("AddMessage").'" />';
1209		if ($this->withcancel) {
1210			print " &nbsp; &nbsp; ";
1211			print '<input class="button button-cancel" type="submit" name="cancel" value="'.$langs->trans("Cancel").'">';
1212		}
1213		print "</center>\n";
1214
1215		print '<input type="hidden" name="page_y">'."\n";
1216
1217		print "</form>\n";
1218		print "<!-- End form TICKET -->\n";
1219	}
1220}
1221